Handle RIPAS changes and selected GICv3 sysreg exits. RIPAS work runs before the next REC entry, and sysreg transfers use ESR_EL2.ISS.Rt.
Signed-off-by: Koichiro Den <[email protected]> --- xen/arch/arm/cca/rec.c | 209 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 206 insertions(+), 3 deletions(-) diff --git a/xen/arch/arm/cca/rec.c b/xen/arch/arm/cca/rec.c index efff7fa48745..7959fb767e11 100644 --- a/xen/arch/arm/cca/rec.c +++ b/xen/arch/arm/cca/rec.c @@ -399,6 +399,66 @@ static void arm_cca_set_mmio_result(unsigned long *entry_flags, entry_gprs[0] = value; } +static register_t +arm_cca_rec_exit_gpr(const struct arm_cca_rmi_rec_exit *exit, + unsigned int reg) +{ + return reg < ARM_CCA_RMI_REC_NR_GPRS ? exit->gprs[reg] : 0; +} + +static void arm_cca_rec_enter_set_gpr(register_t *entry_gprs, + unsigned int reg, + register_t value) +{ + if ( reg < ARM_CCA_RMI_REC_NR_GPRS ) + entry_gprs[reg] = value; +} + +static bool arm_cca_vgic_emulate_sgi1r(struct vcpu *v, register_t sgir) +{ + return vgic_v3_to_sgi(v, sgir); +} + +static bool arm_cca_vgic_emulate_dir(struct vcpu *v, register_t dir) +{ + unsigned int virq = dir & GICC_IAR_INTID_MASK; + struct gic_lr lr; + unsigned int i; + + /* + * RMM may exit to Xen for Realm ICC_DIR_EL1 accesses. Handle the + * deactivation locally when Xen can find a matching software LR. + * DEN0137 2.0-bet1 - A6.1 Realm interrupts. + */ + if ( virq >= vgic_num_irqs(v->domain) ) + return true; + + for ( i = 0; i < gic_get_nr_lrs(); i++ ) + { + gic_hw_ops->read_lr(i, &lr); + + if ( lr.virq != virq || !lr.active ) + continue; + + if ( lr.hw_status ) + { + gprintk(XENLOG_ERR, + "ARM CCA: ICC_DIR_EL1 on hw-backed vIRQ %u is unsupported\n", + virq); + return false; + } + + lr.active = false; + gic_hw_ops->write_lr(i, &lr); + isb(); + vgic_sync_from_lrs(v); + + return true; + } + + return true; +} + static void arm_cca_request_sea(unsigned long *entry_flags, register_t *entry_gprs) { @@ -560,6 +620,87 @@ static void arm_cca_handle_psci_exit(struct vcpu *v, } } +/* + * DEN0137 2.0-bet1 - D1.5.3 RIPAS change flow. + * RIPAS_EMPTY and RIPAS_RAM changes are applied with RMI_RTT_SET_RIPAS. + * RIPAS_DEV requires VDEV mapping validation support, while RIPAS_DESTROYED + * is not a Realm-requested target state. + */ +static void +arm_cca_handle_ripas_change(struct vcpu *v, + const struct arm_cca_rmi_rec_exit *exit, + unsigned long *entry_flags) +{ + struct arm_smccc_res res; + paddr_t base = exit->ripas_base; + paddr_t top = exit->ripas_top; + int rc; + + if ( v->domain->arch.cca.rd == INVALID_PADDR ) + { + gprintk(XENLOG_ERR, + "ARM CCA: missing RD for RIPAS completion\n"); + arm_cca_domain_crash(v); + } + + switch ( exit->ripas_value ) + { + case ARM_CCA_RMI_RIPAS_EMPTY: + case ARM_CCA_RMI_RIPAS_RAM: + break; + + default: + gprintk(XENLOG_ERR, + "ARM CCA: unsupported RIPAS change value=%u " + "range=%#lx-%#lx\n", + (unsigned int)exit->ripas_value, + (unsigned long)base, (unsigned long)top); + arm_cca_domain_crash(v); + } + + /* + * Note that RMI_RTT_SET_RIPAS uses the pending request recorded in the + * REC, so the Host does not pass the target RIPAS value explicitly here. + * + * Xen accepts the request while progress is possible. If a RIPAS_RAM + * request reaches a point the RMM cannot change, report Host rejection on + * the next REC_ENTER so the Realm sees the normal RSI response. + */ + while ( base < top ) + { + rc = arm_cca_rmi_rtt_set_ripas(v->domain->arch.cca.rd, + v->arch.cca.rec, + base, top, &res); + if ( rc != 0 ) + { + if ( exit->ripas_value == ARM_CCA_RMI_RIPAS_RAM && + arm_cca_rmi_status_is(arm_cca_rmi_result(&res), + ARM_CCA_RMI_ERROR_RTT) ) + { + /* The RIPAS response flag value 1 means Host reject. */ + *entry_flags |= ARM_CCA_RMI_REC_ENTER_FLAG_RIPAS_RESPONSE; + return; + } + + gprintk(XENLOG_ERR, + "ARM CCA: RMI_RTT_SET_RIPAS failed status=%#x data=%#lx\n", + arm_cca_rmi_status_code(arm_cca_rmi_result(&res)), + (unsigned long)arm_cca_rmi_result_data( + arm_cca_rmi_result(&res))); + arm_cca_domain_crash(v); + } + + if ( res.a1 <= base || res.a1 > top ) + { + gprintk(XENLOG_ERR, + "ARM CCA: invalid RIPAS progress %#lx -> %#lx (top %#lx)\n", + (unsigned long)base, res.a1, (unsigned long)top); + arm_cca_domain_crash(v); + } + + base = res.a1; + } +} /* * DEN0137 2.0-bet1 - D1.3.3 REC exit due to Data Abort fault flow. @@ -573,6 +714,55 @@ static void arm_cca_handle_sync_exit(struct vcpu *v, switch ( hsr.ec ) { + case HSR_EC_SYSREG: + { + unsigned int rt = hsr.sysreg.reg; + register_t val = arm_cca_rec_exit_gpr(exit, rt); + + /* + * Realm guests use the GICv3 sysreg CPU interface: + * - Linux expects ICC_SRE_EL1.SRE to read as enabled. + * - SGI generation and explicit deactivate stay in Xen's vGIC path. + * + * DEN0137 2.0-bet1 A6.1 makes ICC_* traps System register exits, + * but A4.3.4.4 only guarantees ESR. Use the Rt encoded in ESR_EL2.ISS + * for the transfer GPR. Current TF-RMM clears Rt for these exits, + * making this equivalent to its gprs[0] convention. + */ + switch ( hsr.bits & HSR_SYSREG_REGS_MASK ) + { + case HSR_SYSREG_ICC_SRE_EL1: + arm_cca_rec_enter_set_gpr(entry_gprs, rt, + ARM_CCA_ICC_SRE_EL1_VALUE); + break; + case HSR_SYSREG_ICC_DIR_EL1: + if ( hsr.sysreg.read || + !arm_cca_vgic_emulate_dir(v, val) ) + { + gprintk(XENLOG_ERR, + "ARM CCA: unsupported ICC_DIR_EL1 access read=%u val=%#lx\n", + hsr.sysreg.read, val); + arm_cca_domain_crash(v); + } + break; + case HSR_SYSREG_ICC_SGI1R_EL1: + if ( hsr.sysreg.read || + !arm_cca_vgic_emulate_sgi1r(v, val) ) + { + gprintk(XENLOG_ERR, + "ARM CCA: unsupported ICC_SGI1R_EL1 access read=%u val=%#lx\n", + hsr.sysreg.read, val); + arm_cca_domain_crash(v); + } + break; + default: + gprintk(XENLOG_ERR, + "ARM CCA: unsupported SYSREG ec=%#x sysreg=%#lx\n", + hsr.ec, (unsigned long)(hsr.bits & HSR_SYSREG_REGS_MASK)); + arm_cca_domain_crash(v); + } + break; + } case HSR_EC_WFI_WFE: /* * DEN0137 2.0-bet1 - A4.3.4.1 REC exit due to WFI or WFE. @@ -619,6 +809,7 @@ void noreturn arm_cca_vcpu_run(struct vcpu *v) struct arm_smccc_res res; unsigned long entry_flags; register_t entry_gprs[ARM_CCA_RMI_REC_NR_GPRS]; + bool pending_ripas = false; int rc; ASSERT(v == current); @@ -663,6 +854,19 @@ void noreturn arm_cca_vcpu_run(struct vcpu *v) arm_cca_check_for_vcpu_work(v); arm_cca_service_host_events(); + /* + * RMI_RTT_SET_RIPAS can cover a large range. The REC exit buffer + * remains valid until the next REC_ENTER, so complete the request + * here with IRQs enabled rather than in the exit dispatch path. + */ + if ( pending_ripas ) + { + local_irq_enable(); + arm_cca_handle_ripas_change(v, &run->exit, &entry_flags); + local_irq_disable(); + pending_ripas = false; + } + arm_cca_prepare_rec_enter(run, entry_flags, entry_gprs); entry_flags = ARM_CCA_RMI_REC_ENTER_FLAG_TRAP_WFI | ARM_CCA_RMI_REC_ENTER_FLAG_TRAP_WFE; @@ -700,9 +904,8 @@ void noreturn arm_cca_vcpu_run(struct vcpu *v) break; case ARM_CCA_RMI_EXIT_RIPAS_CHANGE: - gprintk(XENLOG_ERR, - "ARM CCA: RIPAS change exits are unsupported\n"); - arm_cca_domain_crash(v); + pending_ripas = true; + break; case ARM_CCA_RMI_EXIT_SYNC: arm_cca_handle_sync_exit(v, &run->exit, &entry_flags, -- 2.51.0
