diff mbox series

[v3] target/loongarch/kvm: Add software breakpoint support

Message ID 20240607035016.2975799-1-maobibo@loongson.cn (mailing list archive)
State New, archived
Headers show
Series [v3] target/loongarch/kvm: Add software breakpoint support | expand

Commit Message

Bibo Mao June 7, 2024, 3:50 a.m. UTC
With KVM virtualization, debug exception is injected to guest kernel
rather than host for normal break intruction. Here hypercall
instruction with special code is used for sw breakpoint usage,
and detailed instruction comes from kvm kernel with user API
KVM_REG_LOONGARCH_DEBUG_INST.

Now only software breakpoint is supported, and it is allowed to
insert/remove software breakpoint. We can debug guest kernel with gdb
method after kernel is loaded, hardware breakpoint will be added in later.

Signed-off-by: Bibo Mao <maobibo@loongson.cn>
---
v2 ... v3:
  1. Refresh patch based on the latest version, succeed in compile and
run since kvm uapi header files is updated.
v1 ... v2:
  1. Enable TARGET_KVM_HAVE_GUEST_DEBUG on loongarch64 platform
---
 configs/targets/loongarch64-softmmu.mak |  1 +
 target/loongarch/kvm/kvm.c              | 76 +++++++++++++++++++++++++
 2 files changed, 77 insertions(+)


base-commit: dec9742cbc59415a8b83e382e7ae36395394e4bd

Comments

gaosong June 14, 2024, 9:35 a.m. UTC | #1
在 2024/6/7 上午11:50, Bibo Mao 写道:
> With KVM virtualization, debug exception is injected to guest kernel
> rather than host for normal break intruction. Here hypercall
> instruction with special code is used for sw breakpoint usage,
> and detailed instruction comes from kvm kernel with user API
> KVM_REG_LOONGARCH_DEBUG_INST.
>
> Now only software breakpoint is supported, and it is allowed to
> insert/remove software breakpoint. We can debug guest kernel with gdb
> method after kernel is loaded, hardware breakpoint will be added in later.
>
> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
> ---
Reviewed-by: Song Gao <gaosong@loongson.cn>
Tested-by: Song Gao <gaosong@loongson.cn>

Thanks.
Song Gao
> v2 ... v3:
>    1. Refresh patch based on the latest version, succeed in compile and
> run since kvm uapi header files is updated.
> v1 ... v2:
>    1. Enable TARGET_KVM_HAVE_GUEST_DEBUG on loongarch64 platform
> ---
>   configs/targets/loongarch64-softmmu.mak |  1 +
>   target/loongarch/kvm/kvm.c              | 76 +++++++++++++++++++++++++
>   2 files changed, 77 insertions(+)
>
> diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak
> index 84beb19b90..65b65e0c34 100644
> --- a/configs/targets/loongarch64-softmmu.mak
> +++ b/configs/targets/loongarch64-softmmu.mak
> @@ -1,5 +1,6 @@
>   TARGET_ARCH=loongarch64
>   TARGET_BASE_ARCH=loongarch
> +TARGET_KVM_HAVE_GUEST_DEBUG=y
>   TARGET_SUPPORTS_MTTCG=y
>   TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml
>   # all boards require libfdt
> diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c
> index 8e6e27c8bf..e1be6a6959 100644
> --- a/target/loongarch/kvm/kvm.c
> +++ b/target/loongarch/kvm/kvm.c
> @@ -28,6 +28,7 @@
>   #include "trace.h"
>   
>   static bool cap_has_mp_state;
> +static unsigned int brk_insn;
>   const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
>       KVM_CAP_LAST_INFO
>   };
> @@ -664,7 +665,14 @@ static void kvm_loongarch_vm_stage_change(void *opaque, bool running,
>   
>   int kvm_arch_init_vcpu(CPUState *cs)
>   {
> +    uint64_t val;
> +
>       qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs);
> +
> +    if (!kvm_get_one_reg(cs, KVM_REG_LOONGARCH_DEBUG_INST, &val)) {
> +        brk_insn = val;
> +    }
> +
>       return 0;
>   }
>   
> @@ -739,6 +747,67 @@ bool kvm_arch_stop_on_emulation_error(CPUState *cs)
>       return true;
>   }
>   
> +void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg)
> +{
> +    if (kvm_sw_breakpoints_active(cpu)) {
> +        dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
> +    }
> +}
> +
> +int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
> +{
> +    if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) ||
> +        cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk_insn, 4, 1)) {
> +        error_report("%s failed", __func__);
> +        return -EINVAL;
> +    }
> +    return 0;
> +}
> +
> +int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
> +{
> +    static uint32_t brk;
> +
> +    if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk, 4, 0) ||
> +        brk != brk_insn ||
> +        cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 1)) {
> +        error_report("%s failed", __func__);
> +        return -EINVAL;
> +    }
> +    return 0;
> +}
> +
> +int kvm_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type)
> +{
> +    return -ENOSYS;
> +}
> +
> +int kvm_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type)
> +{
> +    return -ENOSYS;
> +}
> +
> +void kvm_arch_remove_all_hw_breakpoints(void)
> +{
> +}
> +
> +static bool kvm_loongarch_handle_debug(CPUState *cs, struct kvm_run *run)
> +{
> +    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
> +    CPULoongArchState *env = &cpu->env;
> +
> +    kvm_cpu_synchronize_state(cs);
> +    if (cs->singlestep_enabled) {
> +        return true;
> +    }
> +
> +    if (kvm_find_sw_breakpoint(cs, env->pc)) {
> +        return true;
> +    }
> +
> +    return false;
> +}
> +
>   int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
>   {
>       int ret = 0;
> @@ -757,6 +826,13 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
>                            run->iocsr_io.len,
>                            run->iocsr_io.is_write);
>           break;
> +
> +    case KVM_EXIT_DEBUG:
> +        if (kvm_loongarch_handle_debug(cs, run)) {
> +            ret = EXCP_DEBUG;
> +        }
> +        break;
> +
>       default:
>           ret = -1;
>           warn_report("KVM: unknown exit reason %d", run->exit_reason);
>
> base-commit: dec9742cbc59415a8b83e382e7ae36395394e4bd
diff mbox series

Patch

diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak
index 84beb19b90..65b65e0c34 100644
--- a/configs/targets/loongarch64-softmmu.mak
+++ b/configs/targets/loongarch64-softmmu.mak
@@ -1,5 +1,6 @@ 
 TARGET_ARCH=loongarch64
 TARGET_BASE_ARCH=loongarch
+TARGET_KVM_HAVE_GUEST_DEBUG=y
 TARGET_SUPPORTS_MTTCG=y
 TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml
 # all boards require libfdt
diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c
index 8e6e27c8bf..e1be6a6959 100644
--- a/target/loongarch/kvm/kvm.c
+++ b/target/loongarch/kvm/kvm.c
@@ -28,6 +28,7 @@ 
 #include "trace.h"
 
 static bool cap_has_mp_state;
+static unsigned int brk_insn;
 const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
     KVM_CAP_LAST_INFO
 };
@@ -664,7 +665,14 @@  static void kvm_loongarch_vm_stage_change(void *opaque, bool running,
 
 int kvm_arch_init_vcpu(CPUState *cs)
 {
+    uint64_t val;
+
     qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs);
+
+    if (!kvm_get_one_reg(cs, KVM_REG_LOONGARCH_DEBUG_INST, &val)) {
+        brk_insn = val;
+    }
+
     return 0;
 }
 
@@ -739,6 +747,67 @@  bool kvm_arch_stop_on_emulation_error(CPUState *cs)
     return true;
 }
 
+void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg)
+{
+    if (kvm_sw_breakpoints_active(cpu)) {
+        dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
+    }
+}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
+{
+    if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) ||
+        cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk_insn, 4, 1)) {
+        error_report("%s failed", __func__);
+        return -EINVAL;
+    }
+    return 0;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
+{
+    static uint32_t brk;
+
+    if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk, 4, 0) ||
+        brk != brk_insn ||
+        cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 1)) {
+        error_report("%s failed", __func__);
+        return -EINVAL;
+    }
+    return 0;
+}
+
+int kvm_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type)
+{
+    return -ENOSYS;
+}
+
+int kvm_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type)
+{
+    return -ENOSYS;
+}
+
+void kvm_arch_remove_all_hw_breakpoints(void)
+{
+}
+
+static bool kvm_loongarch_handle_debug(CPUState *cs, struct kvm_run *run)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+    CPULoongArchState *env = &cpu->env;
+
+    kvm_cpu_synchronize_state(cs);
+    if (cs->singlestep_enabled) {
+        return true;
+    }
+
+    if (kvm_find_sw_breakpoint(cs, env->pc)) {
+        return true;
+    }
+
+    return false;
+}
+
 int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
 {
     int ret = 0;
@@ -757,6 +826,13 @@  int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
                          run->iocsr_io.len,
                          run->iocsr_io.is_write);
         break;
+
+    case KVM_EXIT_DEBUG:
+        if (kvm_loongarch_handle_debug(cs, run)) {
+            ret = EXCP_DEBUG;
+        }
+        break;
+
     default:
         ret = -1;
         warn_report("KVM: unknown exit reason %d", run->exit_reason);