@@ -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;
@@ -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
@@ -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
*/
@@ -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;
}
/*
@@ -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)
@@ -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:
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@loongson.cn/ Signed-off-by: Bibo Mao <maobibo@loongson.cn> --- 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(-) base-commit: 7e90b5c295ec1e47c8ad865429f046970c549a66