[PATCH v2 4/6] KVM: arm64: Emulate the OS Lock

Reiji Watanabe reijiw at google.com
Thu Nov 4 20:56:18 PDT 2021


Hi Oliver,

On Tue, Nov 2, 2021 at 2:47 AM Oliver Upton <oupton at google.com> wrote:
>
> The OS lock blocks all debug exceptions at every EL. To date, KVM has
> not implemented the OS lock for its guests, despite the fact that it is
> mandatory per the architecture. Simple context switching between the
> guest and host is not appropriate, as its effects are not constrained to
> the guest context.
>
> Emulate the OS Lock by clearing MDE and SS in MDSCR_EL1, thereby
> blocking all but software breakpoint instructions. To handle breakpoint
> instructions, trap debug exceptions to EL2 and skip the instruction.
>
> Signed-off-by: Oliver Upton <oupton at google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h |  4 ++++
>  arch/arm64/kvm/debug.c            | 20 +++++++++++++++-----
>  arch/arm64/kvm/handle_exit.c      |  8 ++++++++
>  arch/arm64/kvm/sys_regs.c         |  6 +++---
>  4 files changed, 30 insertions(+), 8 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index c98f65c4a1f7..f13b8b79b06d 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -724,6 +724,10 @@ void kvm_arm_vcpu_init_debug(struct kvm_vcpu *vcpu);
>  void kvm_arm_setup_debug(struct kvm_vcpu *vcpu);
>  void kvm_arm_clear_debug(struct kvm_vcpu *vcpu);
>  void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu);
> +
> +#define kvm_vcpu_os_lock_enabled(vcpu)         \
> +       (__vcpu_sys_reg(vcpu, OSLSR_EL1) & SYS_OSLSR_OSLK)

I would think the name of this macro might sound like it generates
a code that is evaluated as bool :)


> +
>  int kvm_arm_vcpu_arch_set_attr(struct kvm_vcpu *vcpu,
>                                struct kvm_device_attr *attr);
>  int kvm_arm_vcpu_arch_get_attr(struct kvm_vcpu *vcpu,
> diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
> index db9361338b2a..5690a9c99c89 100644
> --- a/arch/arm64/kvm/debug.c
> +++ b/arch/arm64/kvm/debug.c
> @@ -95,8 +95,11 @@ static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
>                                 MDCR_EL2_TDRA |
>                                 MDCR_EL2_TDOSA);
>
> -       /* Is the VM being debugged by userspace? */
> -       if (vcpu->guest_debug)
> +       /*
> +        * Check if the VM is being debugged by userspace or the guest has
> +        * enabled the OS lock.
> +        */
> +       if (vcpu->guest_debug || kvm_vcpu_os_lock_enabled(vcpu))

IMHO, it might be nicer to create a macro or function that abstracts the
condition that needs save_guest_debug_regs/restore_guest_debug_regs.
(rather than putting those conditions in each part of codes where they
are needed)

Thanks,
Reiji




>                 /* Route all software debug exceptions to EL2 */
>                 vcpu->arch.mdcr_el2 |= MDCR_EL2_TDE;
>
> @@ -160,8 +163,11 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
>
>         kvm_arm_setup_mdcr_el2(vcpu);
>
> -       /* Is Guest debugging in effect? */
> -       if (vcpu->guest_debug) {
> +       /*
> +        * Check if the guest is being debugged or if the guest has enabled the
> +        * OS lock.
> +        */
> +       if (vcpu->guest_debug || kvm_vcpu_os_lock_enabled(vcpu)) {
>                 /* Save guest debug state */
>                 save_guest_debug_regs(vcpu);
>
> @@ -223,6 +229,10 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
>                         trace_kvm_arm_set_regset("WAPTS", get_num_wrps(),
>                                                 &vcpu->arch.debug_ptr->dbg_wcr[0],
>                                                 &vcpu->arch.debug_ptr->dbg_wvr[0]);
> +               } else if (kvm_vcpu_os_lock_enabled(vcpu)) {
> +                       mdscr = vcpu_read_sys_reg(vcpu, MDSCR_EL1);
> +                       mdscr &= ~DBG_MDSCR_MDE;
> +                       vcpu_write_sys_reg(vcpu, mdscr, MDSCR_EL1);
>                 }
>         }
>
> @@ -244,7 +254,7 @@ void kvm_arm_clear_debug(struct kvm_vcpu *vcpu)
>  {
>         trace_kvm_arm_clear_debug(vcpu->guest_debug);
>
> -       if (vcpu->guest_debug) {
> +       if (vcpu->guest_debug || kvm_vcpu_os_lock_enabled(vcpu)) {
>                 restore_guest_debug_regs(vcpu);
>
>                 /*
> diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
> index 275a27368a04..a7136888434d 100644
> --- a/arch/arm64/kvm/handle_exit.c
> +++ b/arch/arm64/kvm/handle_exit.c
> @@ -119,6 +119,14 @@ static int kvm_handle_guest_debug(struct kvm_vcpu *vcpu)
>  {
>         struct kvm_run *run = vcpu->run;
>         u32 esr = kvm_vcpu_get_esr(vcpu);
> +       u8 esr_ec = ESR_ELx_EC(esr);
> +
> +       if (!vcpu->guest_debug) {
> +               WARN_ONCE(esr_ec != ESR_ELx_EC_BRK64 || esr_ec != ESR_ELx_EC_BKPT32,
> +                         "Unexpected debug exception\n");
> +               kvm_incr_pc(vcpu);
> +               return 1;
> +       }
>
>         run->exit_reason = KVM_EXIT_DEBUG;
>         run->debug.arch.hsr = esr;
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index acd8aa2e5a44..d336e4c66870 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -1446,9 +1446,9 @@ static unsigned int mte_visibility(const struct kvm_vcpu *vcpu,
>   * Debug handling: We do trap most, if not all debug related system
>   * registers. The implementation is good enough to ensure that a guest
>   * can use these with minimal performance degradation. The drawback is
> - * that we don't implement any of the external debug, none of the
> - * OSlock protocol. This should be revisited if we ever encounter a
> - * more demanding guest...
> + * that we don't implement any of the external debug architecture.
> + * This should be revisited if we ever encounter a more demanding
> + * guest...
>   */
>  static const struct sys_reg_desc sys_reg_descs[] = {
>         { SYS_DESC(SYS_DC_ISW), access_dcsw },
> --
> 2.33.1.1089.g2158813163f-goog
>



More information about the linux-arm-kernel mailing list