]> www.infradead.org Git - nvme.git/commitdiff
LoongArch: KVM: Add software breakpoint support
authorBibo Mao <maobibo@loongson.cn>
Mon, 6 May 2024 14:00:47 +0000 (22:00 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Mon, 6 May 2024 14:00:47 +0000 (22:00 +0800)
When VM runs in kvm mode, system will not exit to host mode when
executing a general software breakpoint instruction such as INSN_BREAK,
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 a hypercall instruction with a special code is used for software
breakpoint usage. VM exits 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.

Idea comes from ppc kvm, one api KVM_REG_LOONGARCH_DEBUG_INST is added
to get the hypercall code. VMM needs get sw breakpoint instruction with
this api and set the corresponding sw break point for guest kernel.

Signed-off-by: Bibo Mao <maobibo@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/include/asm/inst.h
arch/loongarch/include/asm/kvm_host.h
arch/loongarch/include/asm/kvm_para.h
arch/loongarch/include/uapi/asm/kvm.h
arch/loongarch/kvm/exit.c
arch/loongarch/kvm/vcpu.c
arch/loongarch/kvm/vm.c

index ad120f924905045d269c7aba0c1221a736945c97..c3993fd88abaab16a52c18e90c280ca33b28ec5a 100644 (file)
@@ -12,6 +12,7 @@
 
 #define INSN_NOP               0x03400000
 #define INSN_BREAK             0x002a0000
+#define INSN_HVCL              0x002b8000
 
 #define ADDR_IMMMASK_LU52ID    0xFFF0000000000000
 #define ADDR_IMMMASK_LU32ID    0x000FFFFF00000000
index 2aae1517c8bfeb16844cb179f20bf5bb514a136b..fc2edaefd6885dffada2759e6fb8f3e6ebc3849f 100644 (file)
 
 #define KVM_HALT_POLL_NS_DEFAULT       500000
 
+#define KVM_GUESTDBG_SW_BP_MASK                \
+       (KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP)
+#define KVM_GUESTDBG_VALID_MASK                \
+       (KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP | KVM_GUESTDBG_SINGLESTEP)
+
 struct kvm_vm_stat {
        struct kvm_vm_stat_generic generic;
        u64 pages;
index 012b85fcb2cb36094c9b7afb84e5c82414cbe197..4ba2312e5f8c8e46b92fc59a33684eae664abb6b 100644 (file)
 #define HYPERCALL_ENCODE(vendor, code) ((vendor << HYPERVISOR_VENDOR_SHIFT) + code)
 
 #define KVM_HCALL_CODE_SERVICE         0
+#define KVM_HCALL_CODE_SWDBG           1
 
 #define KVM_HCALL_SERVICE              HYPERCALL_ENCODE(HYPERVISOR_KVM, KVM_HCALL_CODE_SERVICE)
 #define  KVM_HCALL_FUNC_IPI            1
 
+#define KVM_HCALL_SWDBG                        HYPERCALL_ENCODE(HYPERVISOR_KVM, KVM_HCALL_CODE_SWDBG)
+
 /*
  * LoongArch hypercall return code
  */
index 109785922cf94e455b3945f18ec7b1f0c82b233d..f9abef382317d19bddec69396d088bd3f1cf309f 100644 (file)
@@ -17,6 +17,8 @@
 #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
  */
@@ -72,6 +74,8 @@ struct kvm_fpu {
 
 #define KVM_REG_LOONGARCH_COUNTER      (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 1)
 #define KVM_REG_LOONGARCH_VCPU_RESET   (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 2)
+/* Debugging: Special instruction for software breakpoint */
+#define KVM_REG_LOONGARCH_DEBUG_INST   (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 3)
 
 #define LOONGARCH_REG_SHIFT            3
 #define LOONGARCH_REG_64(TYPE, REG)    (TYPE | KVM_REG_SIZE_U64 | (REG << LOONGARCH_REG_SHIFT))
index 5ace3e8b4e805bd41b95f99d264e1b811c52791b..43cf34f31c107fd4bd361b296600c483a3c57108 100644 (file)
@@ -760,25 +760,37 @@ static void kvm_handle_service(struct kvm_vcpu *vcpu)
 
 static int kvm_handle_hypercall(struct kvm_vcpu *vcpu)
 {
+       int ret;
        larch_inst inst;
        unsigned int code;
 
        inst.word = vcpu->arch.badi;
        code = inst.reg0i15_format.immediate;
-       update_pc(&vcpu->arch);
+       ret = RESUME_GUEST;
 
        switch (code) {
        case KVM_HCALL_SERVICE:
                vcpu->stat.hypercall_exits++;
                kvm_handle_service(vcpu);
                break;
+       case KVM_HCALL_SWDBG:
+               /* KVM_HCALL_SWDBG only in effective when SW_BP is enabled */
+               if (vcpu->guest_debug & KVM_GUESTDBG_SW_BP_MASK) {
+                       vcpu->run->exit_reason = KVM_EXIT_DEBUG;
+                       ret = RESUME_HOST;
+                       break;
+               }
+               fallthrough;
        default:
                /* Treat it as noop intruction, only set return value */
                kvm_write_reg(vcpu, LOONGARCH_GPR_A0, KVM_HCALL_INVALID_CODE);
                break;
        }
 
-       return RESUME_GUEST;
+       if (ret == RESUME_GUEST)
+               update_pc(&vcpu->arch);
+
+       return ret;
 }
 
 /*
index 97a7aa508e969ec1b3c311344590c9b42f5b9b26..9e8030d4512902656a17597a186566118bdc6891 100644 (file)
@@ -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 inline int kvm_set_cpuid(struct kvm_vcpu *vcpu, u64 val)
@@ -499,6 +507,9 @@ static int kvm_get_one_reg(struct kvm_vcpu *vcpu,
                case KVM_REG_LOONGARCH_COUNTER:
                        *v = drdtime() + vcpu->kvm->arch.time_offset;
                        break;
+               case KVM_REG_LOONGARCH_DEBUG_INST:
+                       *v = INSN_HVCL | KVM_HCALL_SWDBG;
+                       break;
                default:
                        ret = -EINVAL;
                        break;
index 191d82309a1ed8c58c03abb12b84ee2d5f82c95f..6b2e4f66ad26193aa043f01ce93941a4b4bae711 100644 (file)
@@ -76,6 +76,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: