On Tue, Sep 01, 2026 at 10:47:06PM +0100, Mark Brown wrote:
> As per I_CFFNS and the pseudocode for the SPSR_ELx and ELR_ELx registers
> a GCS exception with ExType 1 is generated for attempts to write to
> those registers when both GCSCR_ELx.EXLOCKEN and PSTATE.EXLOCK are set.
> Ensure that this is enforced for guests if access to these registers
> from the guest is handled by the hypervisor.
> 
> Signed-off-by: Mark Brown <[email protected]>
> ---
>  arch/arm64/include/asm/kvm_emulate.h |  8 +++++++
>  arch/arm64/kvm/sys_regs.c            | 42 
> ++++++++++++++++++++++++++++++++----
>  2 files changed, 46 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_emulate.h 
> b/arch/arm64/include/asm/kvm_emulate.h
> index 0cd91b3d6c82..e47ab38327de 100644
> --- a/arch/arm64/include/asm/kvm_emulate.h
> +++ b/arch/arm64/include/asm/kvm_emulate.h
> @@ -81,6 +81,14 @@ int kvm_inject_nested_irq(struct kvm_vcpu *vcpu);
>  int kvm_inject_nested_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr);
>  int kvm_inject_nested_serror(struct kvm_vcpu *vcpu, u64 esr);
>  
> +static inline void kvm_inject_exlock(struct kvm_vcpu *vcpu)
> +{
> +     u64 esr = FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_GCS) | ESR_ELx_IL |
> +               FIELD_PREP(ESR_ELx_ExType_MASK, ESR_ELx_ExType_EXLOCK);

Seems the likely encoding, based on reading the ESR_EL2 doc, not sure if 
there is a better place to read that from.

> +
> +     kvm_inject_sync(vcpu, esr);
> +}
> +
>  static inline void kvm_inject_nested_sve_trap(struct kvm_vcpu *vcpu)
>  {
>       u64 esr = FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SVE) |
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index c5ce18b3f7d8..1f80c26a9839 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -2866,14 +2866,42 @@ static bool access_sp_el1(struct kvm_vcpu *vcpu,
>       return true;
>  }
>  
> +static inline bool sysregs_exlocked(struct kvm_vcpu *vcpu)
> +{
> +     u64 gcscr;
> +
> +     if (!kvm_has_gcs(vcpu->kvm))
> +             return false;
> +
> +     if (!(vcpu->arch.ctxt.regs.pstate & PSR_EXLOCK_BIT))
> +             return false;
> +
> +     /*
> +      * Note that the EXLOCKEN for the running EL is checked
> +      * regardless of the register written to.
> +      */
> +     if (is_hyp_ctxt(vcpu))
> +             gcscr = vcpu_read_sys_reg(vcpu, GCSCR_EL2);
> +     else
> +             gcscr = vcpu_read_sys_reg(vcpu, GCSCR_EL1);
> +
> +     return gcscr & GCSCR_ELx_EXLOCKEN;
> +}
> +

Right, because on both ELR_ELx and SPSR_EL{1,2} we have to check if
GCS is enabled && EXLOCKEN=1 && EXLOCK=1, 
so any of those conditions being false mean no exlock injection is needed. 


>  static bool access_elr(struct kvm_vcpu *vcpu,
>                      struct sys_reg_params *p,
>                      const struct sys_reg_desc *r)
>  {
> -     if (p->is_write)
> +     if (p->is_write) {
> +             if (sysregs_exlocked(vcpu)) {
> +                     kvm_inject_exlock(vcpu);
> +                     return false;
> +             }
> +
>               vcpu_write_sys_reg(vcpu, p->regval, ELR_EL1);
> -     else
> +     } else {
>               p->regval = vcpu_read_sys_reg(vcpu, ELR_EL1);
> +     }
>  
>       return true;
>  }
> @@ -2882,10 +2910,16 @@ static bool access_spsr(struct kvm_vcpu *vcpu,
>                       struct sys_reg_params *p,
>                       const struct sys_reg_desc *r)
>  {
> -     if (p->is_write)
> +     if (p->is_write) {
> +             if (sysregs_exlocked(vcpu)) {
> +                     kvm_inject_exlock(vcpu);
> +                     return false;
> +             }
> +
>               __vcpu_assign_sys_reg(vcpu, SPSR_EL1, p->regval);
> -     else
> +     } else {
>               p->regval = __vcpu_sys_reg(vcpu, SPSR_EL1);
> +     }
>  
>       return true;
>  }

FWIW:
Reviewed-by: Leonardo Bras <[email protected]>

Thanks!
Leo

Reply via email to