[PATCH v2] KVM: arm64: Initialize VCPU mdcr_el2 before loading it
Alexandru Elisei
alexandru.elisei at arm.com
Tue Mar 30 18:49:54 BST 2021
Hi Marc,
On 3/30/21 6:13 PM, Alexandru Elisei wrote:
> [..]
>>> +}
>>> +
>>> /**
>>> * kvm_arm_reset_debug_ptr - reset the debug ptr to point to the vcpu state
>>> */
>>> @@ -83,12 +137,7 @@ void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu)
>>> * @vcpu: the vcpu pointer
>>> *
>>> * This is called before each entry into the hypervisor to setup any
>>> - * debug related registers. Currently this just ensures we will trap
>>> - * access to:
>>> - * - Performance monitors (MDCR_EL2_TPM/MDCR_EL2_TPMCR)
>>> - * - Debug ROM Address (MDCR_EL2_TDRA)
>>> - * - OS related registers (MDCR_EL2_TDOSA)
>>> - * - Statistical profiler (MDCR_EL2_TPMS/MDCR_EL2_E2PB)
>>> + * debug related registers.
>>> *
>>> * Additionally, KVM only traps guest accesses to the debug registers if
>>> * the guest is not actively using them (see the KVM_ARM64_DEBUG_DIRTY
>>> @@ -100,27 +149,14 @@ void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu)
>>>
>>> void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
>>> {
>>> - bool trap_debug = !(vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY);
>>> unsigned long mdscr, orig_mdcr_el2 = vcpu->arch.mdcr_el2;
>>>
>>> trace_kvm_arm_setup_debug(vcpu, vcpu->guest_debug);
>>>
>>> - /*
>>> - * This also clears MDCR_EL2_E2PB_MASK to disable guest access
>>> - * to the profiling buffer.
>>> - */
>>> - vcpu->arch.mdcr_el2 = __this_cpu_read(mdcr_el2) & MDCR_EL2_HPMN_MASK;
>>> - vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM |
>>> - MDCR_EL2_TPMS |
>>> - MDCR_EL2_TPMCR |
>>> - MDCR_EL2_TDRA |
>>> - MDCR_EL2_TDOSA);
>>> + kvm_arm_setup_mdcr_el2(vcpu, __this_cpu_read(mdcr_el2));
>>>
>>> /* Is Guest debugging in effect? */
>>> if (vcpu->guest_debug) {
>>> - /* Route all software debug exceptions to EL2 */
>>> - vcpu->arch.mdcr_el2 |= MDCR_EL2_TDE;
>>> -
>>> /* Save guest debug state */
>>> save_guest_debug_regs(vcpu);
>>>
>>> @@ -174,7 +210,6 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
>>>
>>> vcpu->arch.debug_ptr = &vcpu->arch.external_debug_state;
>>> vcpu->arch.flags |= KVM_ARM64_DEBUG_DIRTY;
>>> - trap_debug = true;
>> There is something that slightly worries me here: there is now a
>> disconnect between flagging debug as dirty and setting the
>> trapping. And actually, you now check for KVM_ARM64_DEBUG_DIRTY and
>> set the trap bits *before* setting the dirty bit itself.
>>
>> Here, I believe you end up with guest/host confusion of breakpoints,
>> which isn't great. Or did I miss something?
> I'm sorry, but I don't understand what you mean. This is my understanding of what
> is happening.
>
> Without this patch, trap_debug is set to true and the KVM_ARM64_DEBUG_DIRTY flag
> is set if vcpu->guest_debug & KVM_GUESTDBG_USE_HW. Further down, trap debug is
> only used when computing mdcr_el2.
>
> With this patch, trap_debug is set to true if vcpu->guest_debug &
> KVM_GUESTDBG_USE_HW and it's also used for computing mdcr_el2, but this happens in
> kvm_arm_setup_mdcr_el2(), which is called at the start of kvm_arm_setup_debug().
> The KVM_ARM_DEBUG_DIRTY flags is still set in kvm_arm_setup_debug() if
> vcpu->guest_debug & KVM_GUESTDBG_USE_HW, like before.
>
> The guest never runs with the value computed in kvm_vcpu_first_run_init() unless
> it's identical with the value recomputed in kvm_arm_setup_debug().
>
> The only difference I see is that mdcr_el2 is computed at the start of
> kvm_arm_setup_debug(). I get the feeling I'm also missing something.
I think I understand what you mean, you are worried that we won't set the bit in
mdcr_el2 to trap debug in the same place where we set the debug dirty flag. If
that's the case, then I can move kvm_arm_setup_mdcr_el2 right after the BUG_ON()
and remove the KVM_GUESTDBG_USE_HW check because the KVM_ARM_DEBUG_DIRTY would be
already set.
Question though, if mdcr_el2 is tied to the debug dirty flag, we ignore the flag
here (code without this patch):
BUG_ON(!vcpu->guest_debug &&
vcpu->arch.debug_ptr != &vcpu->arch.vcpu_debug_state);
/* Trap debug register access */
if (trap_debug)
vcpu->arch.mdcr_el2 |= MDCR_EL2_TDA;
/* If KDE or MDE are set, perform a full save/restore cycle. */
if (vcpu_read_sys_reg(vcpu, MDSCR_EL1) & (DBG_MDSCR_KDE | DBG_MDSCR_MDE))
vcpu->arch.flags |= KVM_ARM64_DEBUG_DIRTY;
I suppose there's something I don't understand yet about how this is supposed to work.
Thanks,
Alex
>
> Thanks,
>
> Alex
>
>>>
>>> trace_kvm_arm_set_regset("BKPTS", get_num_brps(),
>>> &vcpu->arch.debug_ptr->dbg_bcr[0],
>>> @@ -189,10 +224,6 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
>>> BUG_ON(!vcpu->guest_debug &&
>>> vcpu->arch.debug_ptr != &vcpu->arch.vcpu_debug_state);
>>>
>>> - /* Trap debug register access */
>>> - if (trap_debug)
>>> - vcpu->arch.mdcr_el2 |= MDCR_EL2_TDA;
>>> -
>>> /* If KDE or MDE are set, perform a full save/restore cycle. */
>>> if (vcpu_read_sys_reg(vcpu, MDSCR_EL1) & (DBG_MDSCR_KDE | DBG_MDSCR_MDE))
>>> vcpu->arch.flags |= KVM_ARM64_DEBUG_DIRTY;
>>> @@ -201,7 +232,6 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
>>> if (has_vhe() && orig_mdcr_el2 != vcpu->arch.mdcr_el2)
>>> write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
>>>
>>> - trace_kvm_arm_set_dreg32("MDCR_EL2", vcpu->arch.mdcr_el2);
>>> trace_kvm_arm_set_dreg32("MDSCR_EL1", vcpu_read_sys_reg(vcpu, MDSCR_EL1));
>>> }
>> Thanks,
>>
>> M.
>>
> _______________________________________________
> kvmarm mailing list
> kvmarm at lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
More information about the linux-arm-kernel
mailing list