[PATCH] LoongArch: KVM: Add software breakpoint support

From: Bibo Mao
Date: Mon Feb 05 2024 - 02:50:20 EST


When VM runs in kvm mode, system will not exit to host mode if
executing general software breakpoint instruction, one trap exception
happens in guest mode rather than host mode. In order to debug guest
kernel on host side, one mechanism should be used to let vm exit to
host mode.

Here one special hypercall code is used for software breakpoint usage,
vm exists to host mode and kvm hypervisor identifies the special hypercall
code and set exit_reason with KVM_EXIT_DEBUG, and then let qemu handle it.

This patch uses hypercall code and needs hypercall instruction emulation
handling, and it is dependent on this patchset:
https://lore.kernel.org/all/20240201031950.3225626-1-maobibo@xxxxxxxxxxx/

Signed-off-by: Bibo Mao <maobibo@xxxxxxxxxxx>
---
arch/loongarch/include/asm/kvm_para.h | 2 ++
arch/loongarch/include/uapi/asm/kvm.h | 3 +++
arch/loongarch/kvm/exit.c | 12 ++++++++++--
arch/loongarch/kvm/vcpu.c | 16 ++++++++++++++--
arch/loongarch/kvm/vm.c | 1 +
5 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/arch/loongarch/include/asm/kvm_para.h b/arch/loongarch/include/asm/kvm_para.h
index a25a84e372b9..c44412feabb3 100644
--- a/arch/loongarch/include/asm/kvm_para.h
+++ b/arch/loongarch/include/asm/kvm_para.h
@@ -10,8 +10,10 @@
#define HYPERCALL_CODE(vendor, code) ((vendor << HYPERVISOR_VENDOR_SHIFT) + code)

#define KVM_HC_CODE_SERVICE 0
+#define KVM_HC_CODE_SWDBG 1
#define KVM_HC_SERVICE HYPERCALL_CODE(HYPERVISOR_KVM, KVM_HC_CODE_SERVICE)
#define KVM_HC_FUNC_IPI 1
+#define KVM_HC_SWDBG HYPERCALL_CODE(HYPERVISOR_KVM, KVM_HC_CODE_SWDBG)

/*
* LoongArch hypcall return code
diff --git a/arch/loongarch/include/uapi/asm/kvm.h b/arch/loongarch/include/uapi/asm/kvm.h
index 923d0bd38294..ad6d79ff6742 100644
--- a/arch/loongarch/include/uapi/asm/kvm.h
+++ b/arch/loongarch/include/uapi/asm/kvm.h
@@ -15,10 +15,13 @@
*/

#define __KVM_HAVE_READONLY_MEM
+#define __KVM_HAVE_GUEST_DEBUG

#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
#define KVM_DIRTY_LOG_PAGE_OFFSET 64

+#define KVM_GUESTDBG_USE_SW_BP 0x00010000
+
/*
* for KVM_GET_REGS and KVM_SET_REGS
*/
diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c
index 189b70bad825..01e3f30f041b 100644
--- a/arch/loongarch/kvm/exit.c
+++ b/arch/loongarch/kvm/exit.c
@@ -758,23 +758,31 @@ static int kvm_handle_hypcall(struct kvm_vcpu *vcpu)
{
larch_inst inst;
unsigned int code;
+ int ret;

inst.word = vcpu->arch.badi;
code = inst.reg0i15_format.immediate;
- update_pc(&vcpu->arch);
+ ret = RESUME_GUEST;

switch (code) {
case KVM_HC_SERVICE:
vcpu->stat.hvcl_exits++;
kvm_handle_pv_hcall(vcpu);
break;
+ case KVM_HC_SWDBG:
+ vcpu->run->exit_reason = KVM_EXIT_DEBUG;
+ ret = RESUME_HOST;
+ break;
default:
/* Treat it as noop intruction, only set return value */
vcpu->arch.gprs[LOONGARCH_GPR_A0] = KVM_HC_INVALID_CODE;
break;
}

- return RESUME_GUEST;
+ if (ret == RESUME_GUEST)
+ update_pc(&vcpu->arch);
+
+ return ret;
}

/*
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index 80e05ba9b48d..aa0d2eaddafe 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -245,10 +245,22 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
return ret;
}

+#define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \
+ KVM_GUESTDBG_USE_SW_BP | KVM_GUESTDBG_SINGLESTEP)
int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
- struct kvm_guest_debug *dbg)
+ struct kvm_guest_debug *dbg)
{
- return -EINVAL;
+ if (dbg->control & ~KVM_GUESTDBG_VALID_MASK)
+ return -EINVAL;
+
+ if (dbg->control & KVM_GUESTDBG_ENABLE) {
+ vcpu->guest_debug = dbg->control;
+ /* No hardware breakpoint */
+ } else {
+ vcpu->guest_debug = 0;
+ }
+
+ return 0;
}

static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
diff --git a/arch/loongarch/kvm/vm.c b/arch/loongarch/kvm/vm.c
index 6fd5916ebef3..44fb18118442 100644
--- a/arch/loongarch/kvm/vm.c
+++ b/arch/loongarch/kvm/vm.c
@@ -77,6 +77,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_IMMEDIATE_EXIT:
case KVM_CAP_IOEVENTFD:
case KVM_CAP_MP_STATE:
+ case KVM_CAP_SET_GUEST_DEBUG:
r = 1;
break;
case KVM_CAP_NR_VCPUS:
--
2.39.3