[PATCH v2] LoongArch: KVM: Add software breakpoint support

From: Bibo Mao
Date: Sat Feb 17 2024 - 04:42:54 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 sets exit_reason with KVM_EXIT_DEBUG, and then let qemu handle it.

Since it 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>
---
Changes in v2:
1. Add checking for hypercall code KVM_HC_SWDBG, it is effective only if
KVM_GUESTDBG_USE_SW_BP and KVM_GUESTDBG_ENABLE is set.

---
arch/loongarch/include/asm/kvm_host.h | 4 ++++
arch/loongarch/include/asm/kvm_para.h | 2 ++
arch/loongarch/include/uapi/asm/kvm.h | 3 +++
arch/loongarch/kvm/exit.c | 17 +++++++++++++++--
arch/loongarch/kvm/vcpu.c | 10 +++++++++-
arch/loongarch/kvm/vm.c | 1 +
6 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h
index 1bf927e2bfac..41a81e7269ee 100644
--- a/arch/loongarch/include/asm/kvm_host.h
+++ b/arch/loongarch/include/asm/kvm_host.h
@@ -31,6 +31,10 @@

#define KVM_HALT_POLL_NS_DEFAULT 500000

+#define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \
+ KVM_GUESTDBG_USE_SW_BP | KVM_GUESTDBG_SINGLESTEP)
+#define KVM_GUESTDBG_SW_BP_MASK (KVM_GUESTDBG_ENABLE | \
+ KVM_GUESTDBG_USE_SW_BP)
struct kvm_vm_stat {
struct kvm_vm_stat_generic generic;
u64 pages;
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..79f26b5f52a6 100644
--- a/arch/loongarch/kvm/exit.c
+++ b/arch/loongarch/kvm/exit.c
@@ -758,23 +758,36 @@ 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:
+ /* KVM_HC_SWDBG only in effective when SW_BP is enabled */
+ if ((vcpu->guest_debug & KVM_GUESTDBG_SW_BP_MASK) ==
+ KVM_GUESTDBG_SW_BP_MASK) {
+ vcpu->run->exit_reason = KVM_EXIT_DEBUG;
+ ret = RESUME_HOST;
+ } else
+ vcpu->arch.gprs[LOONGARCH_GPR_A0] = KVM_HC_INVALID_CODE;
+ 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..b3c84441d1a3 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -248,7 +248,15 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
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;
+ 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:

base-commit: 7e90b5c295ec1e47c8ad865429f046970c549a66
--
2.39.3