Message ID | 20211029003202.158161-3-oupton@google.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | KVM: arm64: Fixes for the exposed debug architecture | expand |
Hi Oliver, On Fri, 29 Oct 2021 01:32:01 +0100, Oliver Upton <oupton@google.com> wrote: > > KVM diverges from the architecture in the way it handles the OSLAR_EL1 > register. While the architecture requires that the register be WO and > that the OSLK bit is 1 out of reset, KVM implements the register as > RAZ/WI. > > Align KVM with the architecture by permitting writes to OSLAR_EL1. Since > the register is WO, stash the OS Lock status bit in OSLSR_EL1 and > context switch the status between host/guest. Additionally, change the > reset value of the OSLK bit to 1. > > Suggested-by: Marc Zyngier <maz@kernel.org> > Signed-off-by: Oliver Upton <oupton@google.com> > --- > arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h | 5 +++++ > arch/arm64/kvm/sys_regs.c | 22 +++++++++++++++++++--- > 2 files changed, 24 insertions(+), 3 deletions(-) > > diff --git a/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h b/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h > index de7e14c862e6..a65dab34f85b 100644 > --- a/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h > +++ b/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h > @@ -65,6 +65,8 @@ static inline void __sysreg_save_el1_state(struct kvm_cpu_context *ctxt) > ctxt_sys_reg(ctxt, SP_EL1) = read_sysreg(sp_el1); > ctxt_sys_reg(ctxt, ELR_EL1) = read_sysreg_el1(SYS_ELR); > ctxt_sys_reg(ctxt, SPSR_EL1) = read_sysreg_el1(SYS_SPSR); > + > + ctxt_sys_reg(ctxt, OSLSR_EL1) = read_sysreg(oslsr_el1); Why do we need to save/restore this outside of the debug context? It seems to me that this is only needed if debug has been enabled by the guest (KVM_ARM64_DEBUG_DIRTY being set), as we will have trapped the OSLAR_EL1 access otherwise. I don't think we need to deal with this register outside of this context, as debug exceptions cannot happen otherwise (BRK excepted, of course). > } > > static inline void __sysreg_save_el2_return_state(struct kvm_cpu_context *ctxt) > @@ -149,6 +151,9 @@ static inline void __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt) > write_sysreg(ctxt_sys_reg(ctxt, SP_EL1), sp_el1); > write_sysreg_el1(ctxt_sys_reg(ctxt, ELR_EL1), SYS_ELR); > write_sysreg_el1(ctxt_sys_reg(ctxt, SPSR_EL1), SYS_SPSR); > + > + /* restore OSLSR_EL1 by writing the OSLK bit to OSLAR_EL1 */ > + write_sysreg((ctxt_sys_reg(ctxt, OSLSR_EL1) >> 1) & 1, oslar_el1); Please introduce some eye-pleasing bit definitions ("Here, there, and everywhere", to quote someone famous). > } > > static inline void __sysreg_restore_el2_return_state(struct kvm_cpu_context *ctxt) > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c > index 0eb03e7508fe..0840ae081290 100644 > --- a/arch/arm64/kvm/sys_regs.c > +++ b/arch/arm64/kvm/sys_regs.c > @@ -298,6 +298,22 @@ static bool trap_oslsr_el1(struct kvm_vcpu *vcpu, > return true; > } > > +static bool trap_oslar_el1(struct kvm_vcpu *vcpu, > + struct sys_reg_params *p, > + const struct sys_reg_desc *r) > +{ > + u64 oslsr; > + > + if (!p->is_write) > + return read_zero(vcpu, p); This really should be an UNDEF (and it really should UNDEF in HW, but we are being, maybe pointlessly, cautious). > + > + /* preserve all but the OSLK bit */ > + oslsr = vcpu_read_sys_reg(vcpu, OSLSR_EL1) & ~0x2ull; > + vcpu_write_sys_reg(vcpu, OSLSR_EL1, oslsr | ((p->regval & 1) << 1)); > + return true; > +} > + > + Extra newline. > static bool trap_dbgauthstatus_el1(struct kvm_vcpu *vcpu, > struct sys_reg_params *p, > const struct sys_reg_desc *r) > @@ -1439,8 +1455,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { > DBG_BCR_BVR_WCR_WVR_EL1(15), > > { SYS_DESC(SYS_MDRAR_EL1), trap_raz_wi }, > - { SYS_DESC(SYS_OSLAR_EL1), trap_raz_wi }, > - { SYS_DESC(SYS_OSLSR_EL1), trap_oslsr_el1, reset_val, OSLSR_EL1, 0x00000008 }, > + { SYS_DESC(SYS_OSLAR_EL1), trap_oslar_el1 }, > + { SYS_DESC(SYS_OSLSR_EL1), trap_oslsr_el1, reset_val, OSLSR_EL1, 0x0000000A }, > { SYS_DESC(SYS_OSDLR_EL1), trap_raz_wi }, > { SYS_DESC(SYS_DBGPRCR_EL1), trap_raz_wi }, > { SYS_DESC(SYS_DBGCLAIMSET_EL1), trap_raz_wi }, > @@ -1912,7 +1928,7 @@ static const struct sys_reg_desc cp14_regs[] = { > > DBGBXVR(0), > /* DBGOSLAR */ > - { Op1( 0), CRn( 1), CRm( 0), Op2( 4), trap_raz_wi }, > + { Op1( 0), CRn( 1), CRm( 0), Op2( 4), trap_oslar_el1 }, > DBGBXVR(1), > /* DBGOSLSR */ > { Op1( 0), CRn( 1), CRm( 1), Op2( 4), trap_oslsr_el1, NULL, OSLSR_EL1 }, Thanks, M.
diff --git a/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h b/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h index de7e14c862e6..a65dab34f85b 100644 --- a/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h +++ b/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h @@ -65,6 +65,8 @@ static inline void __sysreg_save_el1_state(struct kvm_cpu_context *ctxt) ctxt_sys_reg(ctxt, SP_EL1) = read_sysreg(sp_el1); ctxt_sys_reg(ctxt, ELR_EL1) = read_sysreg_el1(SYS_ELR); ctxt_sys_reg(ctxt, SPSR_EL1) = read_sysreg_el1(SYS_SPSR); + + ctxt_sys_reg(ctxt, OSLSR_EL1) = read_sysreg(oslsr_el1); } static inline void __sysreg_save_el2_return_state(struct kvm_cpu_context *ctxt) @@ -149,6 +151,9 @@ static inline void __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt) write_sysreg(ctxt_sys_reg(ctxt, SP_EL1), sp_el1); write_sysreg_el1(ctxt_sys_reg(ctxt, ELR_EL1), SYS_ELR); write_sysreg_el1(ctxt_sys_reg(ctxt, SPSR_EL1), SYS_SPSR); + + /* restore OSLSR_EL1 by writing the OSLK bit to OSLAR_EL1 */ + write_sysreg((ctxt_sys_reg(ctxt, OSLSR_EL1) >> 1) & 1, oslar_el1); } static inline void __sysreg_restore_el2_return_state(struct kvm_cpu_context *ctxt) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 0eb03e7508fe..0840ae081290 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -298,6 +298,22 @@ static bool trap_oslsr_el1(struct kvm_vcpu *vcpu, return true; } +static bool trap_oslar_el1(struct kvm_vcpu *vcpu, + struct sys_reg_params *p, + const struct sys_reg_desc *r) +{ + u64 oslsr; + + if (!p->is_write) + return read_zero(vcpu, p); + + /* preserve all but the OSLK bit */ + oslsr = vcpu_read_sys_reg(vcpu, OSLSR_EL1) & ~0x2ull; + vcpu_write_sys_reg(vcpu, OSLSR_EL1, oslsr | ((p->regval & 1) << 1)); + return true; +} + + static bool trap_dbgauthstatus_el1(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) @@ -1439,8 +1455,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { DBG_BCR_BVR_WCR_WVR_EL1(15), { SYS_DESC(SYS_MDRAR_EL1), trap_raz_wi }, - { SYS_DESC(SYS_OSLAR_EL1), trap_raz_wi }, - { SYS_DESC(SYS_OSLSR_EL1), trap_oslsr_el1, reset_val, OSLSR_EL1, 0x00000008 }, + { SYS_DESC(SYS_OSLAR_EL1), trap_oslar_el1 }, + { SYS_DESC(SYS_OSLSR_EL1), trap_oslsr_el1, reset_val, OSLSR_EL1, 0x0000000A }, { SYS_DESC(SYS_OSDLR_EL1), trap_raz_wi }, { SYS_DESC(SYS_DBGPRCR_EL1), trap_raz_wi }, { SYS_DESC(SYS_DBGCLAIMSET_EL1), trap_raz_wi }, @@ -1912,7 +1928,7 @@ static const struct sys_reg_desc cp14_regs[] = { DBGBXVR(0), /* DBGOSLAR */ - { Op1( 0), CRn( 1), CRm( 0), Op2( 4), trap_raz_wi }, + { Op1( 0), CRn( 1), CRm( 0), Op2( 4), trap_oslar_el1 }, DBGBXVR(1), /* DBGOSLSR */ { Op1( 0), CRn( 1), CRm( 1), Op2( 4), trap_oslsr_el1, NULL, OSLSR_EL1 },
KVM diverges from the architecture in the way it handles the OSLAR_EL1 register. While the architecture requires that the register be WO and that the OSLK bit is 1 out of reset, KVM implements the register as RAZ/WI. Align KVM with the architecture by permitting writes to OSLAR_EL1. Since the register is WO, stash the OS Lock status bit in OSLSR_EL1 and context switch the status between host/guest. Additionally, change the reset value of the OSLK bit to 1. Suggested-by: Marc Zyngier <maz@kernel.org> Signed-off-by: Oliver Upton <oupton@google.com> --- arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h | 5 +++++ arch/arm64/kvm/sys_regs.c | 22 +++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-)