KVM: arm64: Monitor Debug support for non-protected guests
Add monitor debug support for non-protected guests in protected
mode.
Save and restore the monitor debug state when running a
non-protected guest, and propagate the monitor debug
configuration of non-protected vcpus from the host.
This patch assumes that the hyp vcpu debug iflags are kept in
sync with the host.
Signed-off-by: Fuad Tabba <tabba@google.com>
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 98300c4..4b3a7028 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -615,6 +615,57 @@ static void __flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
__copy_vcpu_state(hyp_vcpu->host_vcpu, &hyp_vcpu->vcpu);
}
+static void flush_debug_state(struct pkvm_hyp_vcpu *hyp_vcpu)
+{
+ struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
+ struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
+ u64 mdcr_el2 = READ_ONCE(host_vcpu->arch.mdcr_el2);
+
+ /*
+ * Propagate the monitor debug configuration of the vcpu from host.
+ * Preserve HPMN, which is set-up by some knowledgeable bootcode.
+ * Ensure that MDCR_EL2_E2PB_MASK and MDCR_EL2_E2TB_MASK are clear,
+ * as guests should not be able to access profiling and trace buffers.
+ * Ensure that RES0 bits are clear.
+ */
+ mdcr_el2 &= ~(MDCR_EL2_RES0 |
+ MDCR_EL2_HPMN_MASK |
+ (MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT) |
+ (MDCR_EL2_E2TB_MASK << MDCR_EL2_E2TB_SHIFT));
+ vcpu->arch.mdcr_el2 = read_sysreg(mdcr_el2) & MDCR_EL2_HPMN_MASK;
+ vcpu->arch.mdcr_el2 |= mdcr_el2;
+
+ vcpu->arch.pmu = host_vcpu->arch.pmu;
+ vcpu->guest_debug = READ_ONCE(host_vcpu->guest_debug);
+
+ if (!kvm_vcpu_needs_debug_regs(vcpu))
+ return;
+
+ __vcpu_save_guest_debug_regs(vcpu);
+
+ /* Switch debug_ptr to the external_debug_state if done by the host. */
+ if (kern_hyp_va(READ_ONCE(host_vcpu->arch.debug_ptr)) ==
+ &host_vcpu->arch.external_debug_state)
+ vcpu->arch.debug_ptr = &host_vcpu->arch.external_debug_state;
+
+ /* Propagate any special handling for single step from host. */
+ vcpu_write_sys_reg(vcpu, vcpu_read_sys_reg(host_vcpu, MDSCR_EL1),
+ MDSCR_EL1);
+ *vcpu_cpsr(vcpu) = *vcpu_cpsr(host_vcpu);
+}
+
+static void sync_debug_state(struct pkvm_hyp_vcpu *hyp_vcpu)
+{
+ struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
+ struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
+
+ if (!kvm_vcpu_needs_debug_regs(vcpu))
+ return;
+
+ __vcpu_restore_guest_debug_regs(vcpu);
+ vcpu->arch.debug_ptr = &host_vcpu->arch.vcpu_debug_state;
+}
+
static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
@@ -636,12 +687,11 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
__flush_hyp_vcpu(hyp_vcpu);
hyp_vcpu->vcpu.arch.iflags = READ_ONCE(host_vcpu->arch.iflags);
+ flush_debug_state(hyp_vcpu);
+
hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE);
hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) &
(HCR_TWI | HCR_TWE);
-
- hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2;
- hyp_vcpu->vcpu.arch.debug_ptr = kern_hyp_va(host_vcpu->arch.debug_ptr);
}
hyp_vcpu->vcpu.arch.vsesr_el2 = host_vcpu->arch.vsesr_el2;
@@ -680,6 +730,9 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu, u32 exit_reason)
fpsimd_sve_sync(&hyp_vcpu->vcpu);
+ if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu))
+ sync_debug_state(hyp_vcpu);
+
/*
* Don't sync the vcpu GPR/sysreg state after a run. Instead,
* leave it in the hyp vCPU until someone actually requires it.
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 378458b..5e49d4b 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -636,6 +636,7 @@ static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
hyp_vcpu->vcpu.arch.hw_mmu = &hyp_vm->kvm.arch.mmu;
hyp_vcpu->vcpu.arch.cflags = READ_ONCE(host_vcpu->arch.cflags);
hyp_vcpu->vcpu.arch.mp_state.mp_state = mp_state;
+ hyp_vcpu->vcpu.arch.debug_ptr = &host_vcpu->arch.vcpu_debug_state;
ret = pkvm_vcpu_init_sve(hyp_vcpu, host_vcpu);
if (ret)