* [PATCH 1/11] QEMU/KVM: Fix deadlocks in monitor and debugger
2008-05-26 22:05 [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Jan Kiszka
@ 2008-05-26 22:09 ` Jan Kiszka
2008-05-27 9:36 ` Avi Kivity
2008-05-26 22:09 ` [PATCH 2/11] QEMU/KVM: Cleanup and improve kvm_load/save_registers usage Jan Kiszka
` (10 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Jan Kiszka @ 2008-05-26 22:09 UTC (permalink / raw)
To: kvm-devel; +Cc: Avi Kivity, Hollis Blanchard, Jerone Young, Joerg Roedel
Some monitor commands as well as the vm_stop() issued by the gdbstub on
external interruption so far deadlock on vcpu locks in the kernel. Patch
below resolves the issue by temporarily or permanently stopping all vcpu
threads before issuing the related KVM IOCTLs. It enables, e.g., to
break into guest code spinning in the vcpu and to use things like "info
cpus" in the monitor.
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
qemu/qemu-kvm.c | 41 +++++++++++++++++++++++++++--------------
qemu/vl.c | 2 +-
2 files changed, 28 insertions(+), 15 deletions(-)
Index: b/qemu/qemu-kvm.c
===================================================================
--- a/qemu/qemu-kvm.c
+++ b/qemu/qemu-kvm.c
@@ -137,18 +137,6 @@ static int pre_kvm_run(void *opaque, int
return 0;
}
-void kvm_load_registers(CPUState *env)
-{
- if (kvm_enabled())
- kvm_arch_load_regs(env);
-}
-
-void kvm_save_registers(CPUState *env)
-{
- if (kvm_enabled())
- kvm_arch_save_regs(env);
-}
-
int kvm_cpu_exec(CPUState *env)
{
int r;
@@ -252,6 +240,26 @@ static void kvm_vm_state_change_handler(
pause_all_threads();
}
+void kvm_load_registers(CPUState *env)
+{
+ assert(!vm_running);
+
+ if (kvm_enabled())
+ kvm_arch_load_regs(env);
+}
+
+void kvm_save_registers(CPUState *env)
+{
+ if (!kvm_enabled())
+ return;
+
+ if (vm_running)
+ pause_all_threads();
+ kvm_arch_save_regs(env);
+ if (vm_running)
+ resume_all_threads();
+}
+
static void update_regs_for_sipi(CPUState *env)
{
kvm_arch_update_regs_for_sipi(env);
@@ -740,7 +748,7 @@ int kvm_qemu_init_env(CPUState *cenv)
int kvm_update_debugger(CPUState *env)
{
struct kvm_debug_guest dbg;
- int i;
+ int i, r;
memset(dbg.breakpoints, 0, sizeof(dbg.breakpoints));
@@ -753,7 +761,12 @@ int kvm_update_debugger(CPUState *env)
}
dbg.singlestep = env->singlestep_enabled;
}
- return kvm_guest_debug(kvm_context, env->cpu_index, &dbg);
+ if (vm_running)
+ pause_all_threads();
+ r = kvm_guest_debug(kvm_context, env->cpu_index, &dbg);
+ if (vm_running)
+ resume_all_threads();
+ return r;
}
Index: b/qemu/vl.c
===================================================================
--- a/qemu/vl.c
+++ b/qemu/vl.c
@@ -7312,12 +7312,12 @@ void vm_stop(int reason)
{
if (vm_running) {
cpu_disable_ticks();
- vm_running = 0;
if (reason != 0) {
if (vm_stop_cb) {
vm_stop_cb(vm_stop_opaque, reason);
}
}
+ vm_running = 0;
vm_state_notify(0);
}
}
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 1/11] QEMU/KVM: Fix deadlocks in monitor and debugger
2008-05-26 22:09 ` [PATCH 1/11] QEMU/KVM: Fix deadlocks in monitor and debugger Jan Kiszka
@ 2008-05-27 9:36 ` Avi Kivity
2008-05-27 13:00 ` Jan Kiszka
0 siblings, 1 reply; 21+ messages in thread
From: Avi Kivity @ 2008-05-27 9:36 UTC (permalink / raw)
To: Jan Kiszka; +Cc: kvm-devel, Hollis Blanchard, Jerone Young, Joerg Roedel
Jan Kiszka wrote:
> Some monitor commands as well as the vm_stop() issued by the gdbstub on
> external interruption so far deadlock on vcpu locks in the kernel. Patch
> below resolves the issue by temporarily or permanently stopping all vcpu
> threads before issuing the related KVM IOCTLs. It enables, e.g., to
> break into guest code spinning in the vcpu and to use things like "info
> cpus" in the monitor.
>
I implemented the alternative on_vcpu() approach for this (similar to
smp_call_function_single in the kernel) which solves the livelock
without resorting to stopping the VM.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/11] QEMU/KVM: Fix deadlocks in monitor and debugger
2008-05-27 9:36 ` Avi Kivity
@ 2008-05-27 13:00 ` Jan Kiszka
2008-05-27 13:09 ` Avi Kivity
0 siblings, 1 reply; 21+ messages in thread
From: Jan Kiszka @ 2008-05-27 13:00 UTC (permalink / raw)
To: Avi Kivity; +Cc: kvm-devel, Hollis Blanchard, Jerone Young, Joerg Roedel
Avi Kivity wrote:
> Jan Kiszka wrote:
>> Some monitor commands as well as the vm_stop() issued by the gdbstub on
>> external interruption so far deadlock on vcpu locks in the kernel. Patch
>> below resolves the issue by temporarily or permanently stopping all vcpu
>> threads before issuing the related KVM IOCTLs. It enables, e.g., to
>> break into guest code spinning in the vcpu and to use things like "info
>> cpus" in the monitor.
>>
>
> I implemented the alternative on_vcpu() approach for this (similar to
> smp_call_function_single in the kernel) which solves the livelock
> without resorting to stopping the VM.
I assume this pattern should then be applied to kvm_guest_debug (and
later on kvm_set_guest_debug) as well? You missed to fix that bug.
Here is a quick patch to complete the work, will post rebased versions
of my remaining patches later today:
-----------
Use on_vcpu to call into kvm_guest_debug. Fix on_vpuc usage in
kvm_load_registers.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
qemu/qemu-kvm.c | 32 +++++++++++++++++++++++---------
1 file changed, 23 insertions(+), 9 deletions(-)
Index: b/qemu/qemu-kvm.c
===================================================================
--- a/qemu/qemu-kvm.c
+++ b/qemu/qemu-kvm.c
@@ -187,7 +187,7 @@ static void kvm_do_load_registers(void *
void kvm_load_registers(CPUState *env)
{
if (kvm_enabled())
- on_vcpu(env->cpu_index, kvm_do_load_registers, env);
+ on_vcpu(env, kvm_do_load_registers, env);
}
static void kvm_do_save_registers(void *_env)
@@ -816,23 +816,37 @@ int kvm_qemu_init_env(CPUState *cenv)
return kvm_arch_qemu_init_env(cenv);
}
+struct kvm_guest_debug_data {
+ struct kvm_debug_guest dbg;
+ int err;
+};
+
+void kvm_invoke_guest_debug(void *data)
+{
+ struct kvm_guest_debug_data *dbg_data = data;
+
+ dbg_data->err = kvm_guest_debug(kvm_context, cpu_single_env->cpu_index,
+ &dbg_data->dbg);
+}
+
int kvm_update_debugger(CPUState *env)
{
- struct kvm_debug_guest dbg;
+ struct kvm_guest_debug_data data;
int i;
- memset(dbg.breakpoints, 0, sizeof(dbg.breakpoints));
+ memset(data.dbg.breakpoints, 0, sizeof(data.dbg.breakpoints));
- dbg.enabled = 0;
+ data.dbg.enabled = 0;
if (env->nb_breakpoints || env->singlestep_enabled) {
- dbg.enabled = 1;
+ data.dbg.enabled = 1;
for (i = 0; i < 4 && i < env->nb_breakpoints; ++i) {
- dbg.breakpoints[i].enabled = 1;
- dbg.breakpoints[i].address = env->breakpoints[i];
+ data.dbg.breakpoints[i].enabled = 1;
+ data.dbg.breakpoints[i].address = env->breakpoints[i];
}
- dbg.singlestep = env->singlestep_enabled;
+ data.dbg.singlestep = env->singlestep_enabled;
}
- return kvm_guest_debug(kvm_context, env->cpu_index, &dbg);
+ on_vcpu(env, kvm_invoke_guest_debug, &data);
+ return data.err;
}
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 1/11] QEMU/KVM: Fix deadlocks in monitor and debugger
2008-05-27 13:00 ` Jan Kiszka
@ 2008-05-27 13:09 ` Avi Kivity
0 siblings, 0 replies; 21+ messages in thread
From: Avi Kivity @ 2008-05-27 13:09 UTC (permalink / raw)
To: Jan Kiszka; +Cc: kvm-devel, Hollis Blanchard, Jerone Young, Joerg Roedel
Jan Kiszka wrote:
> Avi Kivity wrote:
>
>> Jan Kiszka wrote:
>>
>>> Some monitor commands as well as the vm_stop() issued by the gdbstub on
>>> external interruption so far deadlock on vcpu locks in the kernel. Patch
>>> below resolves the issue by temporarily or permanently stopping all vcpu
>>> threads before issuing the related KVM IOCTLs. It enables, e.g., to
>>> break into guest code spinning in the vcpu and to use things like "info
>>> cpus" in the monitor.
>>>
>>>
>> I implemented the alternative on_vcpu() approach for this (similar to
>> smp_call_function_single in the kernel) which solves the livelock
>> without resorting to stopping the VM.
>>
>
> I assume this pattern should then be applied to kvm_guest_debug (and
> later on kvm_set_guest_debug) as well? You missed to fix that bug.
>
Yeah, I just patched the register stuff so I could test on_vcpu().
> Here is a quick patch to complete the work, will post rebased versions
> of my remaining patches later today:
> -----------
>
> Use on_vcpu to call into kvm_guest_debug.
Applied that, thanks.
> Fix on_vpuc usage in
> kvm_load_registers.
>
How embarrasing. I had that queued already, though.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 2/11] QEMU/KVM: Cleanup and improve kvm_load/save_registers usage
2008-05-26 22:05 [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Jan Kiszka
2008-05-26 22:09 ` [PATCH 1/11] QEMU/KVM: Fix deadlocks in monitor and debugger Jan Kiszka
@ 2008-05-26 22:09 ` Jan Kiszka
2008-06-09 19:16 ` Anthony Liguori
2008-05-26 22:09 ` [PATCH 3/11] QEMU/KVM: Proper vm_stop on debug events Jan Kiszka
` (9 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Jan Kiszka @ 2008-05-26 22:09 UTC (permalink / raw)
To: kvm-devel; +Cc: Avi Kivity, Hollis Blanchard, Jerone Young, Joerg Roedel
Remove redundant checkes for kvm_enabled() on register updates between
userspace and kvm kernel driver. Ensure register update across all CPUs
on "info cpus" monitor command.
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
qemu/gdbstub.c | 12 ++++--------
qemu/monitor.c | 4 ++--
2 files changed, 6 insertions(+), 10 deletions(-)
Index: b/qemu/monitor.c
===================================================================
--- a/qemu/monitor.c
+++ b/qemu/monitor.c
@@ -286,8 +286,7 @@ static CPUState *mon_get_cpu(void)
mon_set_cpu(0);
}
- if (kvm_enabled())
- kvm_save_registers(mon_cpu);
+ kvm_save_registers(mon_cpu);
return mon_cpu;
}
@@ -315,6 +314,7 @@ static void do_info_cpus(void)
mon_get_cpu();
for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ kvm_save_registers(env);
term_printf("%c CPU #%d:",
(env == mon_cpu) ? '*' : ' ',
env->cpu_index);
Index: b/qemu/gdbstub.c
===================================================================
--- a/qemu/gdbstub.c
+++ b/qemu/gdbstub.c
@@ -904,8 +904,7 @@ static int gdb_handle_packet(GDBState *s
addr = strtoull(p, (char **)&p, 16);
#if defined(TARGET_I386)
env->eip = addr;
- if (kvm_enabled())
- kvm_load_registers(env);
+ kvm_load_registers(env);
#elif defined (TARGET_PPC)
env->nip = addr;
#elif defined (TARGET_SPARC)
@@ -928,8 +927,7 @@ static int gdb_handle_packet(GDBState *s
addr = strtoull(p, (char **)&p, 16);
#if defined(TARGET_I386)
env->eip = addr;
- if (kvm_enabled())
- kvm_load_registers(env);
+ kvm_load_registers(env);
#elif defined (TARGET_PPC)
env->nip = addr;
#elif defined (TARGET_SPARC)
@@ -973,8 +971,7 @@ static int gdb_handle_packet(GDBState *s
}
break;
case 'g':
- if (kvm_enabled())
- kvm_save_registers(env);
+ kvm_save_registers(env);
reg_size = cpu_gdb_read_registers(env, mem_buf);
memtohex(buf, mem_buf, reg_size);
put_packet(s, buf);
@@ -984,8 +981,7 @@ static int gdb_handle_packet(GDBState *s
len = strlen(p) / 2;
hextomem((uint8_t *)registers, p, len);
cpu_gdb_write_registers(env, mem_buf, len);
- if (kvm_enabled())
- kvm_load_registers(env);
+ kvm_load_registers(env);
put_packet(s, "OK");
break;
case 'm':
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 2/11] QEMU/KVM: Cleanup and improve kvm_load/save_registers usage
2008-05-26 22:09 ` [PATCH 2/11] QEMU/KVM: Cleanup and improve kvm_load/save_registers usage Jan Kiszka
@ 2008-06-09 19:16 ` Anthony Liguori
2008-06-12 12:38 ` Avi Kivity
0 siblings, 1 reply; 21+ messages in thread
From: Anthony Liguori @ 2008-06-09 19:16 UTC (permalink / raw)
To: Jan Kiszka
Cc: kvm-devel, Avi Kivity, Hollis Blanchard, Jerone Young,
Joerg Roedel
Jan Kiszka wrote:
> Remove redundant checkes for kvm_enabled() on register updates between
> userspace and kvm kernel driver. Ensure register update across all CPUs
> on "info cpus" monitor command.
>
This breaks the build when KVM is disabled. The explicit guard is
needed to avoid having #ifdefs. Please revert.
Regards,
Anthony Liguori
> Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
> ---
> qemu/gdbstub.c | 12 ++++--------
> qemu/monitor.c | 4 ++--
> 2 files changed, 6 insertions(+), 10 deletions(-)
>
> Index: b/qemu/monitor.c
> ===================================================================
> --- a/qemu/monitor.c
> +++ b/qemu/monitor.c
> @@ -286,8 +286,7 @@ static CPUState *mon_get_cpu(void)
> mon_set_cpu(0);
> }
>
> - if (kvm_enabled())
> - kvm_save_registers(mon_cpu);
> + kvm_save_registers(mon_cpu);
>
> return mon_cpu;
> }
> @@ -315,6 +314,7 @@ static void do_info_cpus(void)
> mon_get_cpu();
>
> for(env = first_cpu; env != NULL; env = env->next_cpu) {
> + kvm_save_registers(env);
> term_printf("%c CPU #%d:",
> (env == mon_cpu) ? '*' : ' ',
> env->cpu_index);
> Index: b/qemu/gdbstub.c
> ===================================================================
> --- a/qemu/gdbstub.c
> +++ b/qemu/gdbstub.c
> @@ -904,8 +904,7 @@ static int gdb_handle_packet(GDBState *s
> addr = strtoull(p, (char **)&p, 16);
> #if defined(TARGET_I386)
> env->eip = addr;
> - if (kvm_enabled())
> - kvm_load_registers(env);
> + kvm_load_registers(env);
> #elif defined (TARGET_PPC)
> env->nip = addr;
> #elif defined (TARGET_SPARC)
> @@ -928,8 +927,7 @@ static int gdb_handle_packet(GDBState *s
> addr = strtoull(p, (char **)&p, 16);
> #if defined(TARGET_I386)
> env->eip = addr;
> - if (kvm_enabled())
> - kvm_load_registers(env);
> + kvm_load_registers(env);
> #elif defined (TARGET_PPC)
> env->nip = addr;
> #elif defined (TARGET_SPARC)
> @@ -973,8 +971,7 @@ static int gdb_handle_packet(GDBState *s
> }
> break;
> case 'g':
> - if (kvm_enabled())
> - kvm_save_registers(env);
> + kvm_save_registers(env);
> reg_size = cpu_gdb_read_registers(env, mem_buf);
> memtohex(buf, mem_buf, reg_size);
> put_packet(s, buf);
> @@ -984,8 +981,7 @@ static int gdb_handle_packet(GDBState *s
> len = strlen(p) / 2;
> hextomem((uint8_t *)registers, p, len);
> cpu_gdb_write_registers(env, mem_buf, len);
> - if (kvm_enabled())
> - kvm_load_registers(env);
> + kvm_load_registers(env);
> put_packet(s, "OK");
> break;
> case 'm':
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 2/11] QEMU/KVM: Cleanup and improve kvm_load/save_registers usage
2008-06-09 19:16 ` Anthony Liguori
@ 2008-06-12 12:38 ` Avi Kivity
0 siblings, 0 replies; 21+ messages in thread
From: Avi Kivity @ 2008-06-12 12:38 UTC (permalink / raw)
To: Anthony Liguori
Cc: Jan Kiszka, kvm-devel, Hollis Blanchard, Jerone Young,
Joerg Roedel
Anthony Liguori wrote:
> Jan Kiszka wrote:
>> Remove redundant checkes for kvm_enabled() on register updates between
>> userspace and kvm kernel driver. Ensure register update across all CPUs
>> on "info cpus" monitor command.
>>
>
> This breaks the build when KVM is disabled. The explicit guard is
> needed to avoid having #ifdefs. Please revert.
>
You mean a link time error? Well in that case we're relying on gcc
optimizing away the call. It will break with -O0.
The correct fix is to define stubs for the case kvm is configured out
(or just use ifdefs). But perhaps Glauber's accelerator framework will
take care of all this in a generic fashion.
--
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 3/11] QEMU/KVM: Proper vm_stop on debug events
2008-05-26 22:05 [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Jan Kiszka
2008-05-26 22:09 ` [PATCH 1/11] QEMU/KVM: Fix deadlocks in monitor and debugger Jan Kiszka
2008-05-26 22:09 ` [PATCH 2/11] QEMU/KVM: Cleanup and improve kvm_load/save_registers usage Jan Kiszka
@ 2008-05-26 22:09 ` Jan Kiszka
2008-05-26 22:09 ` [PATCH 4/11] QEMU: Enhance cpu_break/watchpoint API and gdbstub integration Jan Kiszka
` (8 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Jan Kiszka @ 2008-05-26 22:09 UTC (permalink / raw)
To: kvm-devel; +Cc: Avi Kivity, Hollis Blanchard, Jerone Young, Joerg Roedel
[ Changes to last revision: Reordered kvm_debug_stop_requested and
vm_stop to avoid race. ]
When a vcpu exits after hitting a debug exception, we have to invoke
vm_stop(EXCP_DEBUG). But this has to take place over the io-thread.
This patch introduces kvm_debug_stop_requested to signal this event, and
it takes care that the interrupted vcpu itself goes immediately into
stop state.
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
qemu/qemu-kvm.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
Index: b/qemu/qemu-kvm.c
===================================================================
--- a/qemu/qemu-kvm.c
+++ b/qemu/qemu-kvm.c
@@ -58,6 +58,8 @@ pthread_t io_thread;
static int io_thread_fd = -1;
static int io_thread_sigfd = -1;
+static int kvm_debug_stop_requested;
+
static inline unsigned long kvm_get_thread_id(void)
{
return syscall(SYS_gettid);
@@ -517,6 +519,10 @@ int kvm_main_loop(void)
qemu_system_powerdown();
else if (qemu_reset_requested())
qemu_kvm_system_reset();
+ else if (kvm_debug_stop_requested) {
+ vm_stop(EXCP_DEBUG);
+ kvm_debug_stop_requested = 0;
+ }
}
pause_all_threads();
@@ -529,7 +535,8 @@ static int kvm_debug(void *opaque, int v
{
CPUState *env = cpu_single_env;
- env->exception_index = EXCP_DEBUG;
+ kvm_debug_stop_requested = 1;
+ vcpu_info[vcpu].stopped = 1;
return 1;
}
^ permalink raw reply [flat|nested] 21+ messages in thread* [PATCH 4/11] QEMU: Enhance cpu_break/watchpoint API and gdbstub integration
2008-05-26 22:05 [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Jan Kiszka
` (2 preceding siblings ...)
2008-05-26 22:09 ` [PATCH 3/11] QEMU/KVM: Proper vm_stop on debug events Jan Kiszka
@ 2008-05-26 22:09 ` Jan Kiszka
2008-05-26 22:10 ` [PATCH 5/11] QEMU: Improve SMP debugging support Jan Kiszka
` (7 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Jan Kiszka @ 2008-05-26 22:09 UTC (permalink / raw)
To: kvm-devel; +Cc: Avi Kivity, Hollis Blanchard, Jerone Young, Joerg Roedel
This patch prepares the QEMU cpu_watchpoint/breakpoint API to allow us
hooking in with KVM and doing guest debugging differently (maybe
QEMUAccel should provide appropriate callbacks for this, too). But it
also prepares to extend QEMU's debugging features one day, specifically
/wrt different watchpoint types. So far these extensions are only used
by KVM.
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
qemu/cpu-all.h | 14 ++++++++---
qemu/cpu-defs.h | 2 +
qemu/exec.c | 30 ++++++++++++++++---------
qemu/gdbstub.c | 66 ++++++++++++++++++++++++++++++++++++--------------------
4 files changed, 75 insertions(+), 37 deletions(-)
Index: b/qemu/cpu-all.h
===================================================================
--- a/qemu/cpu-all.h
+++ b/qemu/cpu-all.h
@@ -758,10 +758,16 @@ extern int code_copy_enabled;
void cpu_interrupt(CPUState *s, int mask);
void cpu_reset_interrupt(CPUState *env, int mask);
-int cpu_watchpoint_insert(CPUState *env, target_ulong addr);
-int cpu_watchpoint_remove(CPUState *env, target_ulong addr);
-int cpu_breakpoint_insert(CPUState *env, target_ulong pc);
-int cpu_breakpoint_remove(CPUState *env, target_ulong pc);
+#define GDB_BREAKPOINT_SW 0
+#define GDB_BREAKPOINT_HW 1
+#define GDB_WATCHPOINT_WRITE 2
+#define GDB_WATCHPOINT_READ 3
+#define GDB_WATCHPOINT_ACCESS 4
+
+int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len, int type);
+int cpu_watchpoint_remove(CPUState *env, target_ulong addr, target_ulong len, int type);
+int cpu_breakpoint_insert(CPUState *env, target_ulong pc, target_ulong len, int type);
+int cpu_breakpoint_remove(CPUState *env, target_ulong pc, target_ulong len, int type);
void cpu_single_step(CPUState *env, int enabled);
void cpu_reset(CPUState *s);
Index: b/qemu/exec.c
===================================================================
--- a/qemu/exec.c
+++ b/qemu/exec.c
@@ -1104,16 +1104,20 @@ static void breakpoint_invalidate(CPUSta
#endif
/* Add a watchpoint. */
-int cpu_watchpoint_insert(CPUState *env, target_ulong addr)
+int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len,
+ int type)
{
int i;
+ if (type != GDB_WATCHPOINT_WRITE)
+ return -ENOSYS;
+
for (i = 0; i < env->nb_watchpoints; i++) {
if (addr == env->watchpoint[i].vaddr)
return 0;
}
if (env->nb_watchpoints >= MAX_WATCHPOINTS)
- return -1;
+ return -ENOBUFS;
i = env->nb_watchpoints++;
env->watchpoint[i].vaddr = addr;
@@ -1126,10 +1130,14 @@ int cpu_watchpoint_insert(CPUState *env
}
/* Remove a watchpoint. */
-int cpu_watchpoint_remove(CPUState *env, target_ulong addr)
+int cpu_watchpoint_remove(CPUState *env, target_ulong addr, target_ulong len,
+ int type)
{
int i;
+ if (type != GDB_WATCHPOINT_WRITE)
+ return -ENOSYS;
+
for (i = 0; i < env->nb_watchpoints; i++) {
if (addr == env->watchpoint[i].vaddr) {
env->nb_watchpoints--;
@@ -1138,12 +1146,13 @@ int cpu_watchpoint_remove(CPUState *env,
return 0;
}
}
- return -1;
+ return -ENOENT;
}
/* add a breakpoint. EXCP_DEBUG is returned by the CPU loop if a
breakpoint is reached */
-int cpu_breakpoint_insert(CPUState *env, target_ulong pc)
+int cpu_breakpoint_insert(CPUState *env, target_ulong pc, target_ulong len,
+ int type)
{
#if defined(TARGET_HAS_ICE)
int i;
@@ -1154,7 +1163,7 @@ int cpu_breakpoint_insert(CPUState *env,
}
if (env->nb_breakpoints >= MAX_BREAKPOINTS)
- return -1;
+ return -ENOBUFS;
env->breakpoints[env->nb_breakpoints++] = pc;
if (kvm_enabled())
@@ -1163,12 +1172,13 @@ int cpu_breakpoint_insert(CPUState *env,
breakpoint_invalidate(env, pc);
return 0;
#else
- return -1;
+ return -ENOSYS;
#endif
}
/* remove a breakpoint */
-int cpu_breakpoint_remove(CPUState *env, target_ulong pc)
+int cpu_breakpoint_remove(CPUState *env, target_ulong pc, target_ulong len,
+ int type)
{
#if defined(TARGET_HAS_ICE)
int i;
@@ -1176,7 +1186,7 @@ int cpu_breakpoint_remove(CPUState *env,
if (env->breakpoints[i] == pc)
goto found;
}
- return -1;
+ return -ENOENT;
found:
env->nb_breakpoints--;
if (i < env->nb_breakpoints)
@@ -1188,7 +1198,7 @@ int cpu_breakpoint_remove(CPUState *env,
breakpoint_invalidate(env, pc);
return 0;
#else
- return -1;
+ return -ENOSYS;
#endif
}
Index: b/qemu/gdbstub.c
===================================================================
--- a/qemu/gdbstub.c
+++ b/qemu/gdbstub.c
@@ -882,7 +882,7 @@ static void cpu_gdb_write_registers(CPUS
static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
{
const char *p;
- int ch, reg_size, type;
+ int ch, reg_size, type, res;
char buf[4096];
uint8_t mem_buf[4096];
uint32_t *registers;
@@ -1017,21 +1017,20 @@ static int gdb_handle_packet(GDBState *s
if (*p == ',')
p++;
len = strtoull(p, (char **)&p, 16);
- if (type == 0 || type == 1) {
- if (cpu_breakpoint_insert(env, addr) < 0)
- goto breakpoint_error;
- put_packet(s, "OK");
+ switch (type) {
+ case GDB_BREAKPOINT_SW ... GDB_BREAKPOINT_HW:
+ res = cpu_breakpoint_insert(env, addr, len, type);
+ break;
#ifndef CONFIG_USER_ONLY
- } else if (type == 2) {
- if (cpu_watchpoint_insert(env, addr) < 0)
- goto breakpoint_error;
- put_packet(s, "OK");
+ case GDB_WATCHPOINT_WRITE ... GDB_WATCHPOINT_ACCESS:
+ res = cpu_watchpoint_insert(env, addr, len, type);
+ break;
#endif
- } else {
- breakpoint_error:
- put_packet(s, "E22");
+ default:
+ res = -ENOSYS;
+ break;
}
- break;
+ goto answer_bp_packet;
case 'z':
type = strtoul(p, (char **)&p, 16);
if (*p == ',')
@@ -1040,17 +1039,26 @@ static int gdb_handle_packet(GDBState *s
if (*p == ',')
p++;
len = strtoull(p, (char **)&p, 16);
- if (type == 0 || type == 1) {
- cpu_breakpoint_remove(env, addr);
- put_packet(s, "OK");
+ switch (type) {
+ case GDB_BREAKPOINT_SW ... GDB_BREAKPOINT_HW:
+ res = cpu_breakpoint_remove(env, addr, len, type);
+ break;
#ifndef CONFIG_USER_ONLY
- } else if (type == 2) {
- cpu_watchpoint_remove(env, addr);
- put_packet(s, "OK");
+ case GDB_WATCHPOINT_WRITE ... GDB_WATCHPOINT_ACCESS:
+ res = cpu_watchpoint_remove(env, addr, len, type);
+ break;
#endif
- } else {
- goto breakpoint_error;
+ default:
+ res = -ENOSYS;
+ break;
}
+ answer_bp_packet:
+ if (res >= 0)
+ put_packet(s, "OK");
+ else if (res == -ENOSYS)
+ put_packet(s, "");
+ else
+ put_packet(s, "E22");
break;
#ifdef CONFIG_LINUX_USER
case 'q':
@@ -1085,6 +1093,7 @@ static void gdb_vm_stopped(void *opaque,
{
GDBState *s = opaque;
char buf[256];
+ char *type;
int ret;
if (s->state == RS_SYSCALL)
@@ -1095,8 +1104,19 @@ static void gdb_vm_stopped(void *opaque,
if (reason == EXCP_DEBUG) {
if (s->env->watchpoint_hit) {
- snprintf(buf, sizeof(buf), "T%02xwatch:" TARGET_FMT_lx ";",
- SIGTRAP,
+ switch (s->env->watchpoint[s->env->watchpoint_hit - 1].type) {
+ case GDB_WATCHPOINT_READ:
+ type = "r";
+ break;
+ case GDB_WATCHPOINT_ACCESS:
+ type = "a";
+ break;
+ default:
+ type = "";
+ break;
+ }
+ snprintf(buf, sizeof(buf), "T%02x%swatch:" TARGET_FMT_lx ";",
+ SIGTRAP, type,
s->env->watchpoint[s->env->watchpoint_hit - 1].vaddr);
put_packet(s, buf);
s->env->watchpoint_hit = 0;
Index: b/qemu/cpu-defs.h
===================================================================
--- a/qemu/cpu-defs.h
+++ b/qemu/cpu-defs.h
@@ -155,6 +155,8 @@ typedef struct CPUTLBEntry {
struct { \
target_ulong vaddr; \
target_phys_addr_t addend; \
+ target_ulong len; \
+ int type; \
} watchpoint[MAX_WATCHPOINTS]; \
int nb_watchpoints; \
int watchpoint_hit; \
^ permalink raw reply [flat|nested] 21+ messages in thread* [PATCH 5/11] QEMU: Improve SMP debugging support
2008-05-26 22:05 [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Jan Kiszka
` (3 preceding siblings ...)
2008-05-26 22:09 ` [PATCH 4/11] QEMU: Enhance cpu_break/watchpoint API and gdbstub integration Jan Kiszka
@ 2008-05-26 22:10 ` Jan Kiszka
2008-05-26 22:10 ` [PATCH 6/11] QEMU/KVM: Introduce single vcpu pause/resume Jan Kiszka
` (6 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Jan Kiszka @ 2008-05-26 22:10 UTC (permalink / raw)
To: kvm-devel; +Cc: Avi Kivity, Hollis Blanchard, Jerone Young, Joerg Roedel
This patch enhances QEMU's built-in debugger for SMP guest debugging.
It allows to set the debugger focus explicitly via the monitor command
"cpu", and it automatically switches the focus on breakpoint hit to
the reporting CPU.
Furthermore, the patch propagates breakpoint and watchpoint insertions
or removals to all CPUs, not just the current one as it was the case so
far.
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
qemu/cpu-defs.h | 3 +++
qemu/exec.c | 30 +++++++++++++++++++++---------
qemu/gdbstub.c | 25 ++++++++++++-------------
qemu/monitor.c | 19 ++++++++++++-------
qemu/monitor.h | 7 +++++++
qemu/vl.c | 2 ++
6 files changed, 57 insertions(+), 29 deletions(-)
Index: b/qemu/gdbstub.c
===================================================================
--- a/qemu/gdbstub.c
+++ b/qemu/gdbstub.c
@@ -35,6 +35,7 @@
#include "gdbstub.h"
#include "qemu-kvm.h"
#endif
+#include "monitor.h"
#include "qemu_socket.h"
#ifdef _WIN32
@@ -59,7 +60,6 @@ enum RSState {
RS_SYSCALL,
};
typedef struct GDBState {
- CPUState *env; /* current CPU */
enum RSState state; /* parsing state */
char line_buf[4096];
int line_buf_index;
@@ -962,7 +962,7 @@ static int gdb_handle_packet(GDBState *s
p++;
type = *p;
if (gdb_current_syscall_cb)
- gdb_current_syscall_cb(s->env, ret, err);
+ gdb_current_syscall_cb(mon_get_cpu(), ret, err);
if (type == 'C') {
put_packet(s, "T02");
} else {
@@ -1092,6 +1092,7 @@ extern void tb_flush(CPUState *env);
static void gdb_vm_stopped(void *opaque, int reason)
{
GDBState *s = opaque;
+ CPUState *env = mon_get_cpu();
char buf[256];
char *type;
int ret;
@@ -1100,11 +1101,11 @@ static void gdb_vm_stopped(void *opaque,
return;
/* disable single step if it was enable */
- cpu_single_step(s->env, 0);
+ cpu_single_step(env, 0);
if (reason == EXCP_DEBUG) {
- if (s->env->watchpoint_hit) {
- switch (s->env->watchpoint[s->env->watchpoint_hit - 1].type) {
+ if (env->watchpoint_hit) {
+ switch (env->watchpoint[env->watchpoint_hit - 1].type) {
case GDB_WATCHPOINT_READ:
type = "r";
break;
@@ -1117,12 +1118,12 @@ static void gdb_vm_stopped(void *opaque,
}
snprintf(buf, sizeof(buf), "T%02x%swatch:" TARGET_FMT_lx ";",
SIGTRAP, type,
- s->env->watchpoint[s->env->watchpoint_hit - 1].vaddr);
+ env->watchpoint[env->watchpoint_hit - 1].vaddr);
put_packet(s, buf);
- s->env->watchpoint_hit = 0;
+ env->watchpoint_hit = 0;
return;
}
- tb_flush(s->env);
+ tb_flush(env);
ret = SIGTRAP;
} else if (reason == EXCP_INTERRUPT) {
ret = SIGINT;
@@ -1192,15 +1193,15 @@ void gdb_do_syscall(gdb_syscall_complete
va_end(va);
put_packet(s, buf);
#ifdef CONFIG_USER_ONLY
- gdb_handlesig(s->env, 0);
+ gdb_handlesig(mon_get_cpu(), 0);
#else
- cpu_interrupt(s->env, CPU_INTERRUPT_EXIT);
+ cpu_interrupt(mon_get_cpu(), CPU_INTERRUPT_EXIT);
#endif
}
static void gdb_read_byte(GDBState *s, int ch)
{
- CPUState *env = s->env;
+ CPUState *env = mon_get_cpu();
int i, csum;
uint8_t reply;
@@ -1360,7 +1361,6 @@ static void gdb_accept(void *opaque)
s = &gdbserver_state;
memset (s, 0, sizeof (GDBState));
- s->env = first_cpu; /* XXX: allow to change CPU */
s->fd = fd;
gdb_syscall_state = s;
@@ -1463,7 +1463,6 @@ int gdbserver_start(const char *port)
if (!s) {
return -1;
}
- s->env = first_cpu; /* XXX: allow to change CPU */
s->chr = chr;
qemu_chr_add_handlers(chr, gdb_chr_can_receive, gdb_chr_receive,
gdb_chr_event, s);
Index: b/qemu/monitor.c
===================================================================
--- a/qemu/monitor.c
+++ b/qemu/monitor.c
@@ -266,8 +266,7 @@ static void do_info_blockstats(void)
bdrv_info_stats();
}
-/* get the current CPU defined by the user */
-static int mon_set_cpu(int cpu_index)
+static int mon_set_cpu_index(int cpu_index)
{
CPUState *env;
@@ -280,10 +279,16 @@ static int mon_set_cpu(int cpu_index)
return -1;
}
-static CPUState *mon_get_cpu(void)
+void mon_set_cpu(CPUState *env)
+{
+ mon_cpu = env;
+}
+
+/* get the current CPU defined by the user or by a breakpoint hit */
+CPUState *mon_get_cpu(void)
{
if (!mon_cpu) {
- mon_set_cpu(0);
+ mon_set_cpu(first_cpu);
}
kvm_save_registers(mon_cpu);
@@ -310,8 +315,8 @@ static void do_info_cpus(void)
{
CPUState *env;
- /* just to set the default cpu if not already done */
- mon_get_cpu();
+ if (!mon_cpu)
+ mon_set_cpu(first_cpu);
for(env = first_cpu; env != NULL; env = env->next_cpu) {
kvm_save_registers(env);
@@ -342,7 +347,7 @@ static void do_info_cpus(void)
static void do_cpu_set(int index)
{
- if (mon_set_cpu(index) < 0)
+ if (mon_set_cpu_index(index) < 0)
term_printf("Invalid CPU index\n");
}
Index: b/qemu/monitor.h
===================================================================
--- /dev/null
+++ b/qemu/monitor.h
@@ -0,0 +1,7 @@
+#ifndef QEMU_MONITOR_H
+#define QEMU_MONITOR_H
+
+void mon_set_cpu(CPUState *env);
+CPUState *mon_get_cpu(void);
+
+#endif /* QEMU_MONITOR_H */
Index: b/qemu/vl.c
===================================================================
--- a/qemu/vl.c
+++ b/qemu/vl.c
@@ -33,6 +33,7 @@
#include "console.h"
#include "sysemu.h"
#include "gdbstub.h"
+#include "monitor.h"
#include "qemu-timer.h"
#include "qemu-char.h"
#include "block.h"
@@ -7644,6 +7645,7 @@ static int main_loop(void)
ret = EXCP_INTERRUPT;
}
if (unlikely(ret == EXCP_DEBUG)) {
+ mon_set_cpu(cur_cpu);
vm_stop(EXCP_DEBUG);
}
/* If all cpus are halted then wait until the next IRQ */
Index: b/qemu/cpu-defs.h
===================================================================
--- a/qemu/cpu-defs.h
+++ b/qemu/cpu-defs.h
@@ -170,3 +170,6 @@ typedef struct CPUTLBEntry {
const char *cpu_model_str;
#endif
+
+#define foreach_cpu(env) \
+ for(env = first_cpu; env != NULL; env = env->next_cpu)
Index: b/qemu/exec.c
===================================================================
--- a/qemu/exec.c
+++ b/qemu/exec.c
@@ -1107,6 +1107,7 @@ static void breakpoint_invalidate(CPUSta
int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len,
int type)
{
+ CPUState *env_iter;
int i;
if (type != GDB_WATCHPOINT_WRITE)
@@ -1119,8 +1120,10 @@ int cpu_watchpoint_insert(CPUState *env,
if (env->nb_watchpoints >= MAX_WATCHPOINTS)
return -ENOBUFS;
- i = env->nb_watchpoints++;
- env->watchpoint[i].vaddr = addr;
+ foreach_cpu(env_iter) {
+ i = env_iter->nb_watchpoints++;
+ env_iter->watchpoint[i].vaddr = addr;
+ }
tlb_flush_page(env, addr);
/* FIXME: This flush is needed because of the hack to make memory ops
terminate the TB. It can be removed once the proper IO trap and
@@ -1133,6 +1136,7 @@ int cpu_watchpoint_insert(CPUState *env,
int cpu_watchpoint_remove(CPUState *env, target_ulong addr, target_ulong len,
int type)
{
+ CPUState *env_iter;
int i;
if (type != GDB_WATCHPOINT_WRITE)
@@ -1140,8 +1144,11 @@ int cpu_watchpoint_remove(CPUState *env,
for (i = 0; i < env->nb_watchpoints; i++) {
if (addr == env->watchpoint[i].vaddr) {
- env->nb_watchpoints--;
- env->watchpoint[i] = env->watchpoint[env->nb_watchpoints];
+ foreach_cpu(env_iter) {
+ env_iter->nb_watchpoints--;
+ env_iter->watchpoint[i] =
+ env_iter->watchpoint[env_iter->nb_watchpoints];
+ }
tlb_flush_page(env, addr);
return 0;
}
@@ -1155,6 +1162,7 @@ int cpu_breakpoint_insert(CPUState *env,
int type)
{
#if defined(TARGET_HAS_ICE)
+ CPUState *env_iter;
int i;
for(i = 0; i < env->nb_breakpoints; i++) {
@@ -1164,7 +1172,8 @@ int cpu_breakpoint_insert(CPUState *env,
if (env->nb_breakpoints >= MAX_BREAKPOINTS)
return -ENOBUFS;
- env->breakpoints[env->nb_breakpoints++] = pc;
+ foreach_cpu(env_iter)
+ env_iter->breakpoints[env_iter->nb_breakpoints++] = pc;
if (kvm_enabled())
kvm_update_debugger(env);
@@ -1181,6 +1190,7 @@ int cpu_breakpoint_remove(CPUState *env,
int type)
{
#if defined(TARGET_HAS_ICE)
+ CPUState *env_iter;
int i;
for(i = 0; i < env->nb_breakpoints; i++) {
if (env->breakpoints[i] == pc)
@@ -1188,13 +1198,15 @@ int cpu_breakpoint_remove(CPUState *env,
}
return -ENOENT;
found:
- env->nb_breakpoints--;
- if (i < env->nb_breakpoints)
- env->breakpoints[i] = env->breakpoints[env->nb_breakpoints];
+ foreach_cpu(env_iter) {
+ env_iter->nb_breakpoints--;
+ env_iter->breakpoints[i] =
+ env_iter->breakpoints[env_iter->nb_breakpoints];
+ }
if (kvm_enabled())
kvm_update_debugger(env);
-
+
breakpoint_invalidate(env, pc);
return 0;
#else
^ permalink raw reply [flat|nested] 21+ messages in thread* [PATCH 6/11] QEMU/KVM: Introduce single vcpu pause/resume
2008-05-26 22:05 [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Jan Kiszka
` (4 preceding siblings ...)
2008-05-26 22:10 ` [PATCH 5/11] QEMU: Improve SMP debugging support Jan Kiszka
@ 2008-05-26 22:10 ` Jan Kiszka
2008-05-26 22:10 ` [PATCH 7/11] QEMU/KVM: New guest debugging interface Jan Kiszka
` (5 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Jan Kiszka @ 2008-05-26 22:10 UTC (permalink / raw)
To: kvm-devel; +Cc: Avi Kivity, Hollis Blanchard, Jerone Young, Joerg Roedel
Small helpers that allow to pause and resume only a single vcpu thread.
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
qemu/qemu-kvm.c | 29 ++++++++++++++++++++++-------
1 file changed, 22 insertions(+), 7 deletions(-)
Index: b/qemu/qemu-kvm.c
===================================================================
--- a/qemu/qemu-kvm.c
+++ b/qemu/qemu-kvm.c
@@ -207,6 +207,17 @@ static int all_threads_paused(void)
return 1;
}
+static void pause_vcpu_thread(int index)
+{
+ assert(!cpu_single_env || cpu_single_env->cpu_index != index);
+
+ vcpu_info[index].stop = 1;
+ pthread_kill(vcpu_info[index].thread, SIG_IPI);
+
+ while (!vcpu_info[index].stopped)
+ qemu_cond_wait(&qemu_pause_cond);
+}
+
static void pause_all_threads(void)
{
int i;
@@ -221,17 +232,21 @@ static void pause_all_threads(void)
qemu_cond_wait(&qemu_pause_cond);
}
+static void resume_vcpu_thread(int index)
+{
+ assert(!cpu_single_env || cpu_single_env->cpu_index != index);
+
+ vcpu_info[index].stop = 0;
+ vcpu_info[index].stopped = 0;
+ pthread_kill(vcpu_info[index].thread, SIG_IPI);
+}
+
static void resume_all_threads(void)
{
int i;
- assert(!cpu_single_env);
-
- for (i = 0; i < smp_cpus; ++i) {
- vcpu_info[i].stop = 0;
- vcpu_info[i].stopped = 0;
- pthread_kill(vcpu_info[i].thread, SIG_IPI);
- }
+ for (i = 0; i < smp_cpus; ++i)
+ resume_vcpu_thread(i);
}
static void kvm_vm_state_change_handler(void *context, int running)
^ permalink raw reply [flat|nested] 21+ messages in thread* [PATCH 7/11] QEMU/KVM: New guest debugging interface
2008-05-26 22:05 [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Jan Kiszka
` (5 preceding siblings ...)
2008-05-26 22:10 ` [PATCH 6/11] QEMU/KVM: Introduce single vcpu pause/resume Jan Kiszka
@ 2008-05-26 22:10 ` Jan Kiszka
2008-05-27 18:31 ` Jan Kiszka
2008-05-26 22:10 ` [PATCH 8/11] QEMU/KVM: Support for SMP guest debugging Jan Kiszka
` (4 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Jan Kiszka @ 2008-05-26 22:10 UTC (permalink / raw)
To: kvm-devel; +Cc: Avi Kivity, Hollis Blanchard, Jerone Young, Joerg Roedel
This patch switches both libkvm as well as the qemu pieces over to the
new guest debug interface. It comes with full support for software-based
breakpoints (via guest code modification), hardware-assisted breakpoints
and watchpoints (x86-only so far).
Breakpoint management is done inside qemu-kvm, transparent to gdbstub,
and also avoiding that the gdb frontend takes over. This allows for
running debuggers inside the guest while guest debugging it active,
because the host can cleanly tell apart host- and guest-originated
breakpoint events.
Yet improvable are x86 corner cases when using single-step ("forgotten"
debug flags on the guest's stack). And, of courese, the yet empty
non-x86 helper functions have to be populated.
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
libkvm/libkvm.c | 14 +++-
libkvm/libkvm.h | 9 ++
qemu/exec.c | 31 +++++---
qemu/qemu-kvm-ia64.c | 33 +++++++++
qemu/qemu-kvm-powerpc.c | 33 +++++++++
qemu/qemu-kvm-x86.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++
qemu/qemu-kvm.c | 157 ++++++++++++++++++++++++++++++++++++++-------
qemu/qemu-kvm.h | 33 ++++++++-
user/main.c | 7 +-
9 files changed, 442 insertions(+), 42 deletions(-)
Index: b/libkvm/libkvm.c
===================================================================
--- a/libkvm/libkvm.c
+++ b/libkvm/libkvm.c
@@ -740,7 +740,13 @@ static int handle_io(kvm_context_t kvm,
int handle_debug(kvm_context_t kvm, int vcpu)
{
- return kvm->callbacks->debug(kvm->opaque, vcpu);
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ struct kvm_run *run = kvm->run[vcpu];
+
+ return kvm->callbacks->debug(kvm->opaque, vcpu, &run->debug.arch);
+#else
+ return 0;
+#endif
}
int kvm_get_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs)
@@ -945,10 +951,12 @@ int kvm_inject_irq(kvm_context_t kvm, in
return ioctl(kvm->vcpu_fd[vcpu], KVM_INTERRUPT, &intr);
}
-int kvm_guest_debug(kvm_context_t kvm, int vcpu, struct kvm_debug_guest *dbg)
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_set_guest_debug(kvm_context_t kvm, int vcpu, struct kvm_guest_debug *dbg)
{
- return ioctl(kvm->vcpu_fd[vcpu], KVM_DEBUG_GUEST, dbg);
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_GUEST_DEBUG, dbg);
}
+#endif
int kvm_set_signal_mask(kvm_context_t kvm, int vcpu, const sigset_t *sigset)
{
Index: b/qemu/exec.c
===================================================================
--- a/qemu/exec.c
+++ b/qemu/exec.c
@@ -1110,6 +1110,9 @@ int cpu_watchpoint_insert(CPUState *env,
CPUState *env_iter;
int i;
+ if (kvm_enabled())
+ return kvm_insert_breakpoint(env, addr, len, type);
+
if (type != GDB_WATCHPOINT_WRITE)
return -ENOSYS;
@@ -1139,6 +1142,9 @@ int cpu_watchpoint_remove(CPUState *env,
CPUState *env_iter;
int i;
+ if (kvm_enabled())
+ return kvm_remove_breakpoint(env, addr, len, type);
+
if (type != GDB_WATCHPOINT_WRITE)
return -ENOSYS;
@@ -1165,6 +1171,9 @@ int cpu_breakpoint_insert(CPUState *env,
CPUState *env_iter;
int i;
+ if (kvm_enabled())
+ return kvm_insert_breakpoint(env, pc, len, type);
+
for(i = 0; i < env->nb_breakpoints; i++) {
if (env->breakpoints[i] == pc)
return 0;
@@ -1175,9 +1184,6 @@ int cpu_breakpoint_insert(CPUState *env,
foreach_cpu(env_iter)
env_iter->breakpoints[env_iter->nb_breakpoints++] = pc;
- if (kvm_enabled())
- kvm_update_debugger(env);
-
breakpoint_invalidate(env, pc);
return 0;
#else
@@ -1192,6 +1198,10 @@ int cpu_breakpoint_remove(CPUState *env,
#if defined(TARGET_HAS_ICE)
CPUState *env_iter;
int i;
+
+ if (kvm_enabled())
+ return kvm_remove_breakpoint(env, pc, len, type);
+
for(i = 0; i < env->nb_breakpoints; i++) {
if (env->breakpoints[i] == pc)
goto found;
@@ -1204,9 +1214,6 @@ int cpu_breakpoint_remove(CPUState *env,
env_iter->breakpoints[env_iter->nb_breakpoints];
}
- if (kvm_enabled())
- kvm_update_debugger(env);
-
breakpoint_invalidate(env, pc);
return 0;
#else
@@ -1221,12 +1228,14 @@ void cpu_single_step(CPUState *env, int
#if defined(TARGET_HAS_ICE)
if (env->singlestep_enabled != enabled) {
env->singlestep_enabled = enabled;
- /* must flush all the translated code to avoid inconsistancies */
- /* XXX: only flush what is necessary */
- tb_flush(env);
+ if (kvm_enabled())
+ kvm_update_guest_debug(env, 0);
+ else {
+ /* must flush all the translated code to avoid inconsistancies */
+ /* XXX: only flush what is necessary */
+ tb_flush(env);
+ }
}
- if (kvm_enabled())
- kvm_update_debugger(env);
#endif
}
Index: b/qemu/qemu-kvm.c
===================================================================
--- a/qemu/qemu-kvm.c
+++ b/qemu/qemu-kvm.c
@@ -546,14 +546,18 @@ int kvm_main_loop(void)
return 0;
}
-static int kvm_debug(void *opaque, int vcpu)
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_debug(void *opaque, int vcpu, struct kvm_debug_exit_arch *arch_info)
{
- CPUState *env = cpu_single_env;
+ int handle = kvm_arch_debug(arch_info);
- kvm_debug_stop_requested = 1;
- vcpu_info[vcpu].stopped = 1;
- return 1;
+ if (handle) {
+ kvm_debug_stop_requested = 1;
+ vcpu_info[vcpu].stopped = 1;
+ }
+ return handle;
}
+#endif
static int kvm_inb(void *opaque, uint16_t addr, uint8_t *data)
{
@@ -655,7 +659,9 @@ static int kvm_shutdown(void *opaque, in
}
static struct kvm_callbacks qemu_kvm_ops = {
+#ifdef KVM_CAP_SET_GUEST_DEBUG
.debug = kvm_debug,
+#endif
.inb = kvm_inb,
.inw = kvm_inw,
.inl = kvm_inl,
@@ -767,30 +773,137 @@ int kvm_qemu_init_env(CPUState *cenv)
return kvm_arch_qemu_init_env(cenv);
}
-int kvm_update_debugger(CPUState *env)
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+struct kvm_sw_breakpoint *first_sw_breakpoint;
+
+struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(target_ulong pc)
+{
+ struct kvm_sw_breakpoint *bp = first_sw_breakpoint;
+
+ while (bp) {
+ if (bp->pc == pc)
+ break;
+ bp = bp->next;
+ }
+ return bp;
+}
+
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap)
{
- struct kvm_debug_guest dbg;
- int i, r;
+ struct kvm_guest_debug dbg;
+ int err;
+
+ dbg.control = 0;
+ if (env->singlestep_enabled)
+ dbg.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP;
+
+ kvm_arch_update_guest_debug(env, &dbg);
+ dbg.control |= reinject_trap;
+
+ assert(!cpu_single_env || cpu_single_env == env);
- memset(dbg.breakpoints, 0, sizeof(dbg.breakpoints));
+ if (!cpu_single_env && vm_running) {
+ pause_vcpu_thread(env->cpu_index);
+ err = kvm_set_guest_debug(kvm_context, env->cpu_index, &dbg);
+ resume_vcpu_thread(env->cpu_index);
+ } else
+ err = kvm_set_guest_debug(kvm_context, env->cpu_index, &dbg);
+ return err;
+}
+
+int kvm_insert_breakpoint(CPUState *env, target_ulong addr,
+ target_ulong len, int type)
+{
+ struct kvm_sw_breakpoint *bp;
+ int err;
- dbg.enabled = 0;
- if (env->nb_breakpoints || env->singlestep_enabled) {
- dbg.enabled = 1;
- for (i = 0; i < 4 && i < env->nb_breakpoints; ++i) {
- dbg.breakpoints[i].enabled = 1;
- dbg.breakpoints[i].address = env->breakpoints[i];
+ if (type == GDB_BREAKPOINT_SW) {
+ bp = kvm_find_sw_breakpoint(addr);
+ if (bp) {
+ bp->usecount++;
+ return 0;
}
- dbg.singlestep = env->singlestep_enabled;
+
+ bp = qemu_malloc(sizeof(struct kvm_sw_breakpoint));
+ if (!bp)
+ return -ENOMEM;
+
+ bp->pc = addr;
+ bp->usecount = 1;
+ bp->prev = NULL;
+ bp->next = first_sw_breakpoint;
+ err = kvm_arch_insert_sw_breakpoint(env, bp);
+ if (err) {
+ free(bp);
+ return err;
+ }
+
+ first_sw_breakpoint = bp;
+ if (bp->next)
+ bp->next->prev = bp;
+ } else {
+ err = kvm_arch_insert_hw_breakpoint(addr, len, type);
+ if (err)
+ return err;
+ }
+ return kvm_update_guest_debug(env, 0);
+}
+
+int kvm_remove_breakpoint(CPUState *env, target_ulong addr,
+ target_ulong len, int type)
+{
+ struct kvm_sw_breakpoint *bp;
+ int err;
+
+ if (type == GDB_BREAKPOINT_SW) {
+ bp = kvm_find_sw_breakpoint(addr);
+ if (!bp)
+ return -ENOENT;
+
+ if (bp->usecount > 1) {
+ bp->usecount--;
+ return 0;
+ }
+
+ err = kvm_arch_remove_sw_breakpoint(env, bp);
+ if (err)
+ return err;
+
+ if (bp->prev)
+ bp->prev->next = bp->next;
+ else
+ first_sw_breakpoint = bp->next;
+ if (bp->next)
+ bp->next->prev = bp->prev;
+
+ qemu_free(bp);
+ } else {
+ err = kvm_arch_remove_hw_breakpoint(addr, len, type);
+ if (err)
+ return err;
}
- if (vm_running)
- pause_all_threads();
- r = kvm_guest_debug(kvm_context, env->cpu_index, &dbg);
- if (vm_running)
- resume_all_threads();
- return r;
+ return kvm_update_guest_debug(env, 0);
}
+#else /* !KVM_CAP_SET_GUEST_DEBUG */
+
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap)
+{
+ return -EINVAL;
+}
+
+int kvm_insert_breakpoint(CPUState *env, target_ulong addr,
+ target_ulong len, int type)
+{
+ return -EINVAL;
+}
+
+int kvm_remove_breakpoint(CPUState *env, target_ulong addr,
+ target_ulong len, int type)
+{
+ return -EINVAL;
+}
+#endif /* !KVM_CAP_SET_GUEST_DEBUG */
/*
* dirty pages logging
Index: b/libkvm/libkvm.h
===================================================================
--- a/libkvm/libkvm.h
+++ b/libkvm/libkvm.h
@@ -51,7 +51,10 @@ struct kvm_callbacks {
/// generic memory writes to unmapped memory (For MMIO devices)
int (*mmio_write)(void *opaque, uint64_t addr, uint8_t *data,
int len);
- int (*debug)(void *opaque, int vcpu);
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ int (*debug)(void *opaque, int vcpu,
+ struct kvm_debug_exit_arch *arch_info);
+#endif
/*!
* \brief Called when the VCPU issues an 'hlt' instruction.
*
@@ -337,7 +340,9 @@ static inline int kvm_reset_mpstate(kvm_
*/
int kvm_inject_irq(kvm_context_t kvm, int vcpu, unsigned irq);
-int kvm_guest_debug(kvm_context_t, int vcpu, struct kvm_debug_guest *dbg);
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_set_guest_debug(kvm_context_t, int vcpu, struct kvm_guest_debug *dbg);
+#endif
#if defined(__i386__) || defined(__x86_64__)
/*!
Index: b/qemu/qemu-kvm.h
===================================================================
--- a/qemu/qemu-kvm.h
+++ b/qemu/qemu-kvm.h
@@ -12,8 +12,6 @@
#include <signal.h>
-#include <signal.h>
-
int kvm_main_loop(void);
int kvm_qemu_init(void);
int kvm_qemu_create_context(void);
@@ -25,7 +23,11 @@ void kvm_save_registers(CPUState *env);
void kvm_load_mpstate(CPUState *env);
void kvm_save_mpstate(CPUState *env);
int kvm_cpu_exec(CPUState *env);
-int kvm_update_debugger(CPUState *env);
+int kvm_insert_breakpoint(CPUState *env, target_ulong addr,
+ target_ulong len, int type);
+int kvm_remove_breakpoint(CPUState *env, target_ulong addr,
+ target_ulong len, int type);
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap);
int kvm_qemu_init_env(CPUState *env);
int kvm_qemu_check_extension(int ext);
void kvm_apic_init(CPUState *env);
@@ -61,6 +63,31 @@ int kvm_arch_try_push_interrupts(void *o
void kvm_arch_update_regs_for_sipi(CPUState *env);
void kvm_arch_cpu_reset(CPUState *env);
+struct kvm_guest_debug;
+struct kvm_debug_exit_arch;
+
+struct kvm_sw_breakpoint {
+ target_ulong pc;
+ target_ulong saved_insn;
+ int usecount;
+ struct kvm_sw_breakpoint *prev;
+ struct kvm_sw_breakpoint *next;
+};
+
+extern struct kvm_sw_breakpoint *first_sw_breakpoint;
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info);
+struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(target_ulong pc);
+int kvm_arch_insert_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp);
+int kvm_arch_remove_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp);
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type);
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type);
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg);
+
CPUState *qemu_kvm_cpu_env(int index);
void qemu_kvm_aio_wait_start(void);
Index: b/qemu/qemu-kvm-ia64.c
===================================================================
--- a/qemu/qemu-kvm-ia64.c
+++ b/qemu/qemu-kvm-ia64.c
@@ -65,3 +65,36 @@ void kvm_arch_update_regs_for_sipi(CPUSt
void kvm_arch_cpu_reset(CPUState *env)
{
}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ return -ENOSYS;
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ return -ENOSYS;
+}
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
+{
+ return 0;
+}
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
+{
+}
Index: b/qemu/qemu-kvm-powerpc.c
===================================================================
--- a/qemu/qemu-kvm-powerpc.c
+++ b/qemu/qemu-kvm-powerpc.c
@@ -217,3 +217,36 @@ int handle_powerpc_dcr_write(int vcpu, u
void kvm_arch_cpu_reset(CPUState *env)
{
}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ return -ENOSYS;
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ return -ENOSYS;
+}
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
+{
+ return 0;
+}
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
+{
+}
Index: b/qemu/qemu-kvm-x86.c
===================================================================
--- a/qemu/qemu-kvm-x86.c
+++ b/qemu/qemu-kvm-x86.c
@@ -687,3 +687,170 @@ void kvm_arch_cpu_reset(CPUState *env)
}
}
}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ uint8_t int3 = 0xcc;
+
+ if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 0) ||
+ cpu_memory_rw_debug(env, bp->pc, &int3, 1, 1))
+ return -EINVAL;
+ return 0;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1))
+ return -EINVAL;
+ return 0;
+}
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+static struct {
+ target_ulong addr;
+ int len;
+ int type;
+} hw_breakpoint[4];
+
+static int nb_hw_breakpoint;
+
+static int find_hw_breakpoint(target_ulong addr, int len, int type)
+{
+ int n;
+
+ for (n = 0; n < nb_hw_breakpoint; n++)
+ if (hw_breakpoint[n].addr == addr && hw_breakpoint[n].type == type &&
+ (hw_breakpoint[n].len == len || len == -1))
+ return n;
+ return -1;
+}
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ switch (type) {
+ case GDB_BREAKPOINT_HW:
+ len = 1;
+ break;
+ case GDB_WATCHPOINT_WRITE:
+ case GDB_WATCHPOINT_ACCESS:
+ switch (len) {
+ case 1:
+ break;
+ case 2:
+ case 4:
+ case 8:
+ if (addr & (len - 1))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ if (nb_hw_breakpoint == 4)
+ return -ENOBUFS;
+
+ if (find_hw_breakpoint(addr, len, type) >= 0)
+ return -EEXIST;
+
+ hw_breakpoint[nb_hw_breakpoint].addr = addr;
+ hw_breakpoint[nb_hw_breakpoint].len = len;
+ hw_breakpoint[nb_hw_breakpoint].type = type;
+ nb_hw_breakpoint++;
+
+ return 0;
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ int n;
+
+ n = find_hw_breakpoint(addr, (type == GDB_BREAKPOINT_HW) ? 1 : len, type);
+ if (n < 0)
+ return -ENOENT;
+
+ nb_hw_breakpoint--;
+ hw_breakpoint[n] = hw_breakpoint[nb_hw_breakpoint];
+
+ return 0;
+}
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
+{
+ int handle = 0;
+ int n;
+
+ if (arch_info->exception == 1) {
+ if (arch_info->dr6 & (1 << 14)) {
+ if (cpu_single_env->singlestep_enabled)
+ handle = 1;
+ } else {
+ for (n = 0; n < 4; n++)
+ if (arch_info->dr6 & (1 << n))
+ switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) {
+ case 0x0:
+ handle = 1;
+ break;
+ case 0x1:
+ handle = 1;
+ cpu_single_env->watchpoint_hit = 1;
+ cpu_single_env->watchpoint[0].vaddr =
+ hw_breakpoint[n].addr;
+ cpu_single_env->watchpoint[0].type =
+ GDB_WATCHPOINT_WRITE;
+ break;
+ case 0x3:
+ handle = 1;
+ cpu_single_env->watchpoint_hit = 1;
+ cpu_single_env->watchpoint[0].vaddr =
+ hw_breakpoint[n].addr;
+ cpu_single_env->watchpoint[0].type =
+ GDB_WATCHPOINT_ACCESS;
+ break;
+ }
+ }
+ } else if (kvm_find_sw_breakpoint(arch_info->pc))
+ handle = 1;
+
+ if (!handle)
+ kvm_update_guest_debug(cpu_single_env,
+ (arch_info->exception == 1) ?
+ KVM_GUESTDBG_INJECT_DB : KVM_GUESTDBG_INJECT_BP);
+
+ return handle;
+}
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
+{
+ const uint8_t type_code[] = {
+ [GDB_BREAKPOINT_HW] = 0x0,
+ [GDB_WATCHPOINT_WRITE] = 0x1,
+ [GDB_WATCHPOINT_ACCESS] = 0x3
+ };
+ const uint8_t len_code[] = {
+ [1] = 0x0, [2] = 0x1, [4] = 0x3, [8] = 0x2
+ };
+ int n;
+
+ if (first_sw_breakpoint)
+ dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
+
+ if (nb_hw_breakpoint > 0) {
+ dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
+ dbg->arch.debugreg[7] = 0x0600;
+ for (n = 0; n < nb_hw_breakpoint; n++) {
+ dbg->arch.debugreg[n] = hw_breakpoint[n].addr;
+ dbg->arch.debugreg[7] |= (2 << (n * 2)) |
+ (type_code[hw_breakpoint[n].type] << (16 + n*4)) |
+ (len_code[hw_breakpoint[n].len] << (18 + n*4));
+ }
+ }
+}
+#endif
Index: b/user/main.c
===================================================================
--- a/user/main.c
+++ b/user/main.c
@@ -284,11 +284,14 @@ static int test_outl(void *opaque, uint1
return 0;
}
-static int test_debug(void *opaque, int vcpu)
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+static int test_debug(void *opaque, int vcpu,
+ struct kvm_debug_exit_arch *arch_info)
{
printf("test_debug\n");
return 0;
}
+#endif
static int test_halt(void *opaque, int vcpu)
{
@@ -343,7 +346,9 @@ static struct kvm_callbacks test_callbac
.outl = test_outl,
.mmio_read = test_mem_read,
.mmio_write = test_mem_write,
+#ifdef KVM_CAP_SET_GUEST_DEBUG
.debug = test_debug,
+#endif
.halt = test_halt,
.io_window = test_io_window,
.try_push_interrupts = test_try_push_interrupts,
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 7/11] QEMU/KVM: New guest debugging interface
2008-05-26 22:10 ` [PATCH 7/11] QEMU/KVM: New guest debugging interface Jan Kiszka
@ 2008-05-27 18:31 ` Jan Kiszka
0 siblings, 0 replies; 21+ messages in thread
From: Jan Kiszka @ 2008-05-27 18:31 UTC (permalink / raw)
To: Jan Kiszka
Cc: kvm-devel, Avi Kivity, Hollis Blanchard, Jerone Young,
Joerg Roedel
[ Updated to latest git. Patch 6/11 is obsolete now. ]
This patch switches both libkvm as well as the qemu pieces over to the
new guest debug interface. It comes with full support for software-based
breakpoints (via guest code modification), hardware-assisted breakpoints
and watchpoints (x86-only so far).
Breakpoint management is done inside qemu-kvm, transparent to gdbstub,
and also avoiding that the gdb frontend takes over. This allows for
running debuggers inside the guest while guest debugging it active,
because the host can cleanly tell apart host- and guest-originated
breakpoint events.
Yet improvable are x86 corner cases when using single-step ("forgotten"
debug flags on the guest's stack). And, of courese, the yet empty
non-x86 helper functions have to be populated.
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
libkvm/libkvm.c | 14 +++-
libkvm/libkvm.h | 9 ++
qemu/exec.c | 31 +++++---
qemu/qemu-kvm-ia64.c | 33 +++++++++
qemu/qemu-kvm-powerpc.c | 33 +++++++++
qemu/qemu-kvm-x86.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++
qemu/qemu-kvm.c | 160 ++++++++++++++++++++++++++++++++++++++-------
qemu/qemu-kvm.h | 33 ++++++++-
user/main.c | 7 +-
9 files changed, 442 insertions(+), 45 deletions(-)
Index: b/libkvm/libkvm.c
===================================================================
--- a/libkvm/libkvm.c
+++ b/libkvm/libkvm.c
@@ -740,7 +740,13 @@ static int handle_io(kvm_context_t kvm,
int handle_debug(kvm_context_t kvm, int vcpu)
{
- return kvm->callbacks->debug(kvm->opaque, vcpu);
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ struct kvm_run *run = kvm->run[vcpu];
+
+ return kvm->callbacks->debug(kvm->opaque, vcpu, &run->debug.arch);
+#else
+ return 0;
+#endif
}
int kvm_get_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs)
@@ -945,10 +951,12 @@ int kvm_inject_irq(kvm_context_t kvm, in
return ioctl(kvm->vcpu_fd[vcpu], KVM_INTERRUPT, &intr);
}
-int kvm_guest_debug(kvm_context_t kvm, int vcpu, struct kvm_debug_guest *dbg)
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_set_guest_debug(kvm_context_t kvm, int vcpu, struct kvm_guest_debug *dbg)
{
- return ioctl(kvm->vcpu_fd[vcpu], KVM_DEBUG_GUEST, dbg);
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_GUEST_DEBUG, dbg);
}
+#endif
int kvm_set_signal_mask(kvm_context_t kvm, int vcpu, const sigset_t *sigset)
{
Index: b/qemu/exec.c
===================================================================
--- a/qemu/exec.c
+++ b/qemu/exec.c
@@ -1110,6 +1110,9 @@ int cpu_watchpoint_insert(CPUState *env,
CPUState *env_iter;
int i;
+ if (kvm_enabled())
+ return kvm_insert_breakpoint(env, addr, len, type);
+
if (type != GDB_WATCHPOINT_WRITE)
return -ENOSYS;
@@ -1139,6 +1142,9 @@ int cpu_watchpoint_remove(CPUState *env,
CPUState *env_iter;
int i;
+ if (kvm_enabled())
+ return kvm_remove_breakpoint(env, addr, len, type);
+
if (type != GDB_WATCHPOINT_WRITE)
return -ENOSYS;
@@ -1165,6 +1171,9 @@ int cpu_breakpoint_insert(CPUState *env,
CPUState *env_iter;
int i;
+ if (kvm_enabled())
+ return kvm_insert_breakpoint(env, pc, len, type);
+
for(i = 0; i < env->nb_breakpoints; i++) {
if (env->breakpoints[i] == pc)
return 0;
@@ -1175,9 +1184,6 @@ int cpu_breakpoint_insert(CPUState *env,
foreach_cpu(env_iter)
env_iter->breakpoints[env_iter->nb_breakpoints++] = pc;
- if (kvm_enabled())
- kvm_update_debugger(env);
-
breakpoint_invalidate(env, pc);
return 0;
#else
@@ -1192,6 +1198,10 @@ int cpu_breakpoint_remove(CPUState *env,
#if defined(TARGET_HAS_ICE)
CPUState *env_iter;
int i;
+
+ if (kvm_enabled())
+ return kvm_remove_breakpoint(env, pc, len, type);
+
for(i = 0; i < env->nb_breakpoints; i++) {
if (env->breakpoints[i] == pc)
goto found;
@@ -1204,9 +1214,6 @@ int cpu_breakpoint_remove(CPUState *env,
env_iter->breakpoints[env_iter->nb_breakpoints];
}
- if (kvm_enabled())
- kvm_update_debugger(env);
-
breakpoint_invalidate(env, pc);
return 0;
#else
@@ -1221,12 +1228,14 @@ void cpu_single_step(CPUState *env, int
#if defined(TARGET_HAS_ICE)
if (env->singlestep_enabled != enabled) {
env->singlestep_enabled = enabled;
- /* must flush all the translated code to avoid inconsistancies */
- /* XXX: only flush what is necessary */
- tb_flush(env);
+ if (kvm_enabled())
+ kvm_update_guest_debug(env, 0);
+ else {
+ /* must flush all the translated code to avoid inconsistancies */
+ /* XXX: only flush what is necessary */
+ tb_flush(env);
+ }
}
- if (kvm_enabled())
- kvm_update_debugger(env);
#endif
}
Index: b/qemu/qemu-kvm.c
===================================================================
--- a/qemu/qemu-kvm.c
+++ b/qemu/qemu-kvm.c
@@ -268,7 +268,7 @@ static void kvm_main_loop_wait(CPUState
exit(1);
}
-
+ cpu_single_env = env;
flush_queued_work(env);
if (vcpu_info[env->cpu_index].stop) {
@@ -595,14 +595,18 @@ int kvm_main_loop(void)
return 0;
}
-static int kvm_debug(void *opaque, int vcpu)
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_debug(void *opaque, int vcpu, struct kvm_debug_exit_arch *arch_info)
{
- CPUState *env = cpu_single_env;
+ int handle = kvm_arch_debug(arch_info);
- kvm_debug_stop_requested = 1;
- vcpu_info[vcpu].stopped = 1;
- return 1;
+ if (handle) {
+ kvm_debug_stop_requested = 1;
+ vcpu_info[vcpu].stopped = 1;
+ }
+ return handle;
}
+#endif
static int kvm_inb(void *opaque, uint16_t addr, uint8_t *data)
{
@@ -704,7 +708,9 @@ static int kvm_shutdown(void *opaque, in
}
static struct kvm_callbacks qemu_kvm_ops = {
+#ifdef KVM_CAP_SET_GUEST_DEBUG
.debug = kvm_debug,
+#endif
.inb = kvm_inb,
.inw = kvm_inw,
.inl = kvm_inl,
@@ -816,38 +822,142 @@ int kvm_qemu_init_env(CPUState *cenv)
return kvm_arch_qemu_init_env(cenv);
}
-struct kvm_guest_debug_data {
- struct kvm_debug_guest dbg;
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+struct kvm_sw_breakpoint *first_sw_breakpoint;
+
+struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(target_ulong pc)
+{
+ struct kvm_sw_breakpoint *bp = first_sw_breakpoint;
+
+ while (bp) {
+ if (bp->pc == pc)
+ break;
+ bp = bp->next;
+ }
+ return bp;
+}
+
+struct kvm_set_guest_debug_data {
+ struct kvm_guest_debug dbg;
int err;
};
-void kvm_invoke_guest_debug(void *data)
+void kvm_invoke_set_guest_debug(void *data)
{
- struct kvm_guest_debug_data *dbg_data = data;
+ struct kvm_set_guest_debug_data *dbg_data = data;
- dbg_data->err = kvm_guest_debug(kvm_context, cpu_single_env->cpu_index,
- &dbg_data->dbg);
+ dbg_data->err = kvm_set_guest_debug(kvm_context, cpu_single_env->cpu_index,
+ &dbg_data->dbg);
}
-int kvm_update_debugger(CPUState *env)
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap)
{
- struct kvm_guest_debug_data data;
- int i;
+ struct kvm_set_guest_debug_data data;
+
+ data.dbg.control = 0;
+ if (env->singlestep_enabled)
+ data.dbg.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP;
+
+ kvm_arch_update_guest_debug(env, &data.dbg);
+ data.dbg.control |= reinject_trap;
+
+ on_vcpu(env, kvm_invoke_set_guest_debug, &data);
+ return data.err;
+}
+
+int kvm_insert_breakpoint(CPUState *env, target_ulong addr,
+ target_ulong len, int type)
+{
+ struct kvm_sw_breakpoint *bp;
+ int err;
+
+ if (type == GDB_BREAKPOINT_SW) {
+ bp = kvm_find_sw_breakpoint(addr);
+ if (bp) {
+ bp->usecount++;
+ return 0;
+ }
+
+ bp = qemu_malloc(sizeof(struct kvm_sw_breakpoint));
+ if (!bp)
+ return -ENOMEM;
+
+ bp->pc = addr;
+ bp->usecount = 1;
+ bp->prev = NULL;
+ bp->next = first_sw_breakpoint;
+ err = kvm_arch_insert_sw_breakpoint(env, bp);
+ if (err) {
+ free(bp);
+ return err;
+ }
+
+ first_sw_breakpoint = bp;
+ if (bp->next)
+ bp->next->prev = bp;
+ } else {
+ err = kvm_arch_insert_hw_breakpoint(addr, len, type);
+ if (err)
+ return err;
+ }
+ return kvm_update_guest_debug(env, 0);
+}
- memset(data.dbg.breakpoints, 0, sizeof(data.dbg.breakpoints));
+int kvm_remove_breakpoint(CPUState *env, target_ulong addr,
+ target_ulong len, int type)
+{
+ struct kvm_sw_breakpoint *bp;
+ int err;
+
+ if (type == GDB_BREAKPOINT_SW) {
+ bp = kvm_find_sw_breakpoint(addr);
+ if (!bp)
+ return -ENOENT;
- data.dbg.enabled = 0;
- if (env->nb_breakpoints || env->singlestep_enabled) {
- data.dbg.enabled = 1;
- for (i = 0; i < 4 && i < env->nb_breakpoints; ++i) {
- data.dbg.breakpoints[i].enabled = 1;
- data.dbg.breakpoints[i].address = env->breakpoints[i];
+ if (bp->usecount > 1) {
+ bp->usecount--;
+ return 0;
}
- data.dbg.singlestep = env->singlestep_enabled;
+
+ err = kvm_arch_remove_sw_breakpoint(env, bp);
+ if (err)
+ return err;
+
+ if (bp->prev)
+ bp->prev->next = bp->next;
+ else
+ first_sw_breakpoint = bp->next;
+ if (bp->next)
+ bp->next->prev = bp->prev;
+
+ qemu_free(bp);
+ } else {
+ err = kvm_arch_remove_hw_breakpoint(addr, len, type);
+ if (err)
+ return err;
}
- on_vcpu(env, kvm_invoke_guest_debug, &data);
- return data.err;
+ return kvm_update_guest_debug(env, 0);
+}
+
+#else /* !KVM_CAP_SET_GUEST_DEBUG */
+
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap)
+{
+ return -EINVAL;
+}
+
+int kvm_insert_breakpoint(CPUState *env, target_ulong addr,
+ target_ulong len, int type)
+{
+ return -EINVAL;
+}
+
+int kvm_remove_breakpoint(CPUState *env, target_ulong addr,
+ target_ulong len, int type)
+{
+ return -EINVAL;
}
+#endif /* !KVM_CAP_SET_GUEST_DEBUG */
/*
Index: b/libkvm/libkvm.h
===================================================================
--- a/libkvm/libkvm.h
+++ b/libkvm/libkvm.h
@@ -51,7 +51,10 @@ struct kvm_callbacks {
/// generic memory writes to unmapped memory (For MMIO devices)
int (*mmio_write)(void *opaque, uint64_t addr, uint8_t *data,
int len);
- int (*debug)(void *opaque, int vcpu);
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ int (*debug)(void *opaque, int vcpu,
+ struct kvm_debug_exit_arch *arch_info);
+#endif
/*!
* \brief Called when the VCPU issues an 'hlt' instruction.
*
@@ -337,7 +340,9 @@ static inline int kvm_reset_mpstate(kvm_
*/
int kvm_inject_irq(kvm_context_t kvm, int vcpu, unsigned irq);
-int kvm_guest_debug(kvm_context_t, int vcpu, struct kvm_debug_guest *dbg);
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_set_guest_debug(kvm_context_t, int vcpu, struct kvm_guest_debug *dbg);
+#endif
#if defined(__i386__) || defined(__x86_64__)
/*!
Index: b/qemu/qemu-kvm.h
===================================================================
--- a/qemu/qemu-kvm.h
+++ b/qemu/qemu-kvm.h
@@ -12,8 +12,6 @@
#include <signal.h>
-#include <signal.h>
-
int kvm_main_loop(void);
int kvm_qemu_init(void);
int kvm_qemu_create_context(void);
@@ -25,7 +23,11 @@ void kvm_save_registers(CPUState *env);
void kvm_load_mpstate(CPUState *env);
void kvm_save_mpstate(CPUState *env);
int kvm_cpu_exec(CPUState *env);
-int kvm_update_debugger(CPUState *env);
+int kvm_insert_breakpoint(CPUState *env, target_ulong addr,
+ target_ulong len, int type);
+int kvm_remove_breakpoint(CPUState *env, target_ulong addr,
+ target_ulong len, int type);
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap);
int kvm_qemu_init_env(CPUState *env);
int kvm_qemu_check_extension(int ext);
void kvm_apic_init(CPUState *env);
@@ -61,6 +63,31 @@ int kvm_arch_try_push_interrupts(void *o
void kvm_arch_update_regs_for_sipi(CPUState *env);
void kvm_arch_cpu_reset(CPUState *env);
+struct kvm_guest_debug;
+struct kvm_debug_exit_arch;
+
+struct kvm_sw_breakpoint {
+ target_ulong pc;
+ target_ulong saved_insn;
+ int usecount;
+ struct kvm_sw_breakpoint *prev;
+ struct kvm_sw_breakpoint *next;
+};
+
+extern struct kvm_sw_breakpoint *first_sw_breakpoint;
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info);
+struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(target_ulong pc);
+int kvm_arch_insert_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp);
+int kvm_arch_remove_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp);
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type);
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type);
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg);
+
CPUState *qemu_kvm_cpu_env(int index);
void qemu_kvm_aio_wait_start(void);
Index: b/qemu/qemu-kvm-ia64.c
===================================================================
--- a/qemu/qemu-kvm-ia64.c
+++ b/qemu/qemu-kvm-ia64.c
@@ -65,3 +65,36 @@ void kvm_arch_update_regs_for_sipi(CPUSt
void kvm_arch_cpu_reset(CPUState *env)
{
}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ return -ENOSYS;
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ return -ENOSYS;
+}
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
+{
+ return 0;
+}
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
+{
+}
Index: b/qemu/qemu-kvm-powerpc.c
===================================================================
--- a/qemu/qemu-kvm-powerpc.c
+++ b/qemu/qemu-kvm-powerpc.c
@@ -217,3 +217,36 @@ int handle_powerpc_dcr_write(int vcpu, u
void kvm_arch_cpu_reset(CPUState *env)
{
}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ return -ENOSYS;
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ return -ENOSYS;
+}
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
+{
+ return 0;
+}
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
+{
+}
Index: b/qemu/qemu-kvm-x86.c
===================================================================
--- a/qemu/qemu-kvm-x86.c
+++ b/qemu/qemu-kvm-x86.c
@@ -687,3 +687,170 @@ void kvm_arch_cpu_reset(CPUState *env)
}
}
}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ uint8_t int3 = 0xcc;
+
+ if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 0) ||
+ cpu_memory_rw_debug(env, bp->pc, &int3, 1, 1))
+ return -EINVAL;
+ return 0;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1))
+ return -EINVAL;
+ return 0;
+}
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+static struct {
+ target_ulong addr;
+ int len;
+ int type;
+} hw_breakpoint[4];
+
+static int nb_hw_breakpoint;
+
+static int find_hw_breakpoint(target_ulong addr, int len, int type)
+{
+ int n;
+
+ for (n = 0; n < nb_hw_breakpoint; n++)
+ if (hw_breakpoint[n].addr == addr && hw_breakpoint[n].type == type &&
+ (hw_breakpoint[n].len == len || len == -1))
+ return n;
+ return -1;
+}
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ switch (type) {
+ case GDB_BREAKPOINT_HW:
+ len = 1;
+ break;
+ case GDB_WATCHPOINT_WRITE:
+ case GDB_WATCHPOINT_ACCESS:
+ switch (len) {
+ case 1:
+ break;
+ case 2:
+ case 4:
+ case 8:
+ if (addr & (len - 1))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ if (nb_hw_breakpoint == 4)
+ return -ENOBUFS;
+
+ if (find_hw_breakpoint(addr, len, type) >= 0)
+ return -EEXIST;
+
+ hw_breakpoint[nb_hw_breakpoint].addr = addr;
+ hw_breakpoint[nb_hw_breakpoint].len = len;
+ hw_breakpoint[nb_hw_breakpoint].type = type;
+ nb_hw_breakpoint++;
+
+ return 0;
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ int n;
+
+ n = find_hw_breakpoint(addr, (type == GDB_BREAKPOINT_HW) ? 1 : len, type);
+ if (n < 0)
+ return -ENOENT;
+
+ nb_hw_breakpoint--;
+ hw_breakpoint[n] = hw_breakpoint[nb_hw_breakpoint];
+
+ return 0;
+}
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
+{
+ int handle = 0;
+ int n;
+
+ if (arch_info->exception == 1) {
+ if (arch_info->dr6 & (1 << 14)) {
+ if (cpu_single_env->singlestep_enabled)
+ handle = 1;
+ } else {
+ for (n = 0; n < 4; n++)
+ if (arch_info->dr6 & (1 << n))
+ switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) {
+ case 0x0:
+ handle = 1;
+ break;
+ case 0x1:
+ handle = 1;
+ cpu_single_env->watchpoint_hit = 1;
+ cpu_single_env->watchpoint[0].vaddr =
+ hw_breakpoint[n].addr;
+ cpu_single_env->watchpoint[0].type =
+ GDB_WATCHPOINT_WRITE;
+ break;
+ case 0x3:
+ handle = 1;
+ cpu_single_env->watchpoint_hit = 1;
+ cpu_single_env->watchpoint[0].vaddr =
+ hw_breakpoint[n].addr;
+ cpu_single_env->watchpoint[0].type =
+ GDB_WATCHPOINT_ACCESS;
+ break;
+ }
+ }
+ } else if (kvm_find_sw_breakpoint(arch_info->pc))
+ handle = 1;
+
+ if (!handle)
+ kvm_update_guest_debug(cpu_single_env,
+ (arch_info->exception == 1) ?
+ KVM_GUESTDBG_INJECT_DB : KVM_GUESTDBG_INJECT_BP);
+
+ return handle;
+}
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
+{
+ const uint8_t type_code[] = {
+ [GDB_BREAKPOINT_HW] = 0x0,
+ [GDB_WATCHPOINT_WRITE] = 0x1,
+ [GDB_WATCHPOINT_ACCESS] = 0x3
+ };
+ const uint8_t len_code[] = {
+ [1] = 0x0, [2] = 0x1, [4] = 0x3, [8] = 0x2
+ };
+ int n;
+
+ if (first_sw_breakpoint)
+ dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
+
+ if (nb_hw_breakpoint > 0) {
+ dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
+ dbg->arch.debugreg[7] = 0x0600;
+ for (n = 0; n < nb_hw_breakpoint; n++) {
+ dbg->arch.debugreg[n] = hw_breakpoint[n].addr;
+ dbg->arch.debugreg[7] |= (2 << (n * 2)) |
+ (type_code[hw_breakpoint[n].type] << (16 + n*4)) |
+ (len_code[hw_breakpoint[n].len] << (18 + n*4));
+ }
+ }
+}
+#endif
Index: b/user/main.c
===================================================================
--- a/user/main.c
+++ b/user/main.c
@@ -284,11 +284,14 @@ static int test_outl(void *opaque, uint1
return 0;
}
-static int test_debug(void *opaque, int vcpu)
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+static int test_debug(void *opaque, int vcpu,
+ struct kvm_debug_exit_arch *arch_info)
{
printf("test_debug\n");
return 0;
}
+#endif
static int test_halt(void *opaque, int vcpu)
{
@@ -343,7 +346,9 @@ static struct kvm_callbacks test_callbac
.outl = test_outl,
.mmio_read = test_mem_read,
.mmio_write = test_mem_write,
+#ifdef KVM_CAP_SET_GUEST_DEBUG
.debug = test_debug,
+#endif
.halt = test_halt,
.io_window = test_io_window,
.try_push_interrupts = test_try_push_interrupts,
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 8/11] QEMU/KVM: Support for SMP guest debugging
2008-05-26 22:05 [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Jan Kiszka
` (6 preceding siblings ...)
2008-05-26 22:10 ` [PATCH 7/11] QEMU/KVM: New guest debugging interface Jan Kiszka
@ 2008-05-26 22:10 ` Jan Kiszka
2008-05-26 22:10 ` [PATCH 9/11] KVM: New guest debugging interface Jan Kiszka
` (3 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Jan Kiszka @ 2008-05-26 22:10 UTC (permalink / raw)
To: kvm-devel; +Cc: Avi Kivity, Hollis Blanchard, Jerone Young, Joerg Roedel
Enables debugging of SMP guests via the new KVM debug interface. It
updates the monitored CPU on breakpoints and unsures that breakpoint
changes are propagested to all virtual CPUs.
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
qemu/qemu-kvm.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
Index: b/qemu/qemu-kvm.c
===================================================================
--- a/qemu/qemu-kvm.c
+++ b/qemu/qemu-kvm.c
@@ -19,6 +19,7 @@ int kvm_pit = 1;
#include "qemu-common.h"
#include "console.h"
#include "block.h"
+#include "monitor.h"
#include "qemu-kvm.h"
#include <libkvm.h>
@@ -58,7 +59,7 @@ pthread_t io_thread;
static int io_thread_fd = -1;
static int io_thread_sigfd = -1;
-static int kvm_debug_stop_requested;
+static CPUState *kvm_debug_cpu_requested;
static inline unsigned long kvm_get_thread_id(void)
{
@@ -534,9 +535,10 @@ int kvm_main_loop(void)
qemu_system_powerdown();
else if (qemu_reset_requested())
qemu_kvm_system_reset();
- else if (kvm_debug_stop_requested) {
+ else if (kvm_debug_cpu_requested) {
+ mon_set_cpu(kvm_debug_cpu_requested);
vm_stop(EXCP_DEBUG);
- kvm_debug_stop_requested = 0;
+ kvm_debug_cpu_requested = NULL;
}
}
@@ -552,7 +554,7 @@ int kvm_debug(void *opaque, int vcpu, st
int handle = kvm_arch_debug(arch_info);
if (handle) {
- kvm_debug_stop_requested = 1;
+ kvm_debug_cpu_requested = cpu_single_env;
vcpu_info[vcpu].stopped = 1;
}
return handle;
@@ -846,7 +848,12 @@ int kvm_insert_breakpoint(CPUState *env,
if (err)
return err;
}
- return kvm_update_guest_debug(env, 0);
+ foreach_cpu(env) {
+ err = kvm_update_guest_debug(env, 0);
+ if (err)
+ break;
+ }
+ return err;
}
int kvm_remove_breakpoint(CPUState *env, target_ulong addr,
@@ -882,7 +889,12 @@ int kvm_remove_breakpoint(CPUState *env,
if (err)
return err;
}
- return kvm_update_guest_debug(env, 0);
+ foreach_cpu(env) {
+ err = kvm_update_guest_debug(env, 0);
+ if (err)
+ break;
+ }
+ return err;
}
#else /* !KVM_CAP_SET_GUEST_DEBUG */
^ permalink raw reply [flat|nested] 21+ messages in thread* [PATCH 9/11] KVM: New guest debugging interface
2008-05-26 22:05 [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Jan Kiszka
` (7 preceding siblings ...)
2008-05-26 22:10 ` [PATCH 8/11] QEMU/KVM: Support for SMP guest debugging Jan Kiszka
@ 2008-05-26 22:10 ` Jan Kiszka
2008-05-26 22:10 ` [PATCH 10/11] KVM-x86: Properly virtualize debug registers Jan Kiszka
` (2 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Jan Kiszka @ 2008-05-26 22:10 UTC (permalink / raw)
To: kvm-devel; +Cc: Avi Kivity, Hollis Blanchard, Jerone Young, Joerg Roedel
This rips out the support for KVM_DEBUG_GUEST and introduces a new IOCTL
instead: KVM_SET_GUEST_DEBUG. The IOCTL payload consists of a generic
part, controlling the "main switch" and the single-step feature. The
arch specific part adds an x86 interface for intercepting both types of
debug exceptions separately and re-injecting them when the host was not
interested. Moveover, the foundation for guest debugging via debug
registers is layed.
To signal breakpoint events properly back to userland, an arch-specific
data block is now returned along KVM_EXIT_DEBUG. For x86, the arch block
contains the PC, the debug exception, and relevant debug registers to
tell debug events properly apart.
The availability of this new interface is signaled by
KVM_CAP_SET_GUEST_DEBUG. Empty stubs for not yet supported archs are
provided.
Note that both svm and vtx are supported, but only the latter was tested
yet.
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
arch/ia64/kvm/kvm-ia64.c | 4 +-
arch/powerpc/kvm/powerpc.c | 4 +-
arch/s390/kvm/kvm-s390.c | 4 +-
arch/x86/kvm/svm.c | 45 +++++++++++++++++++++++-
arch/x86/kvm/vmx.c | 83 ++++++++++++++-------------------------------
arch/x86/kvm/x86.c | 17 ++++-----
include/asm-ia64/kvm.h | 7 +++
include/asm-powerpc/kvm.h | 7 +++
include/asm-s390/kvm.h | 7 +++
include/asm-x86/kvm.h | 18 +++++++++
include/asm-x86/kvm_host.h | 11 +----
include/linux/kvm.h | 51 ++++++++++++++++++---------
include/linux/kvm_host.h | 6 +--
virt/kvm/kvm_main.c | 6 +--
14 files changed, 168 insertions(+), 102 deletions(-)
Index: b/arch/x86/kvm/x86.c
===================================================================
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2729,10 +2729,6 @@ static int __vcpu_run(struct kvm_vcpu *v
down_read(&vcpu->kvm->slots_lock);
vapic_enter(vcpu);
-preempted:
- if (vcpu->guest_debug.enabled)
- kvm_x86_ops->guest_debug_pre(vcpu);
-
again:
if (vcpu->requests)
if (test_and_clear_bit(KVM_REQ_MMU_RELOAD, &vcpu->requests))
@@ -2868,7 +2864,7 @@ out:
if (r > 0) {
kvm_resched(vcpu);
down_read(&vcpu->kvm->slots_lock);
- goto preempted;
+ goto again;
}
post_kvm_run_save(vcpu, kvm_run);
@@ -2972,7 +2968,7 @@ int kvm_arch_vcpu_ioctl_get_regs(struct
/*
* Don't leak debug flags in case they were set for guest debugging
*/
- if (vcpu->guest_debug.enabled && vcpu->guest_debug.singlestep)
+ if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
regs->rflags &= ~(X86_EFLAGS_TF | X86_EFLAGS_RF);
vcpu_put(vcpu);
@@ -3588,8 +3584,8 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct
return 0;
}
-int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu,
- struct kvm_debug_guest *dbg)
+int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug *dbg)
{
int r;
@@ -3597,6 +3593,11 @@ int kvm_arch_vcpu_ioctl_debug_guest(stru
r = kvm_x86_ops->set_guest_debug(vcpu, dbg);
+ if (dbg->control & KVM_GUESTDBG_INJECT_DB)
+ kvm_queue_exception(vcpu, DB_VECTOR);
+ else if (dbg->control & KVM_GUESTDBG_INJECT_BP)
+ kvm_queue_exception(vcpu, BP_VECTOR);
+
vcpu_put(vcpu);
return r;
Index: b/include/asm-x86/kvm.h
===================================================================
--- a/include/asm-x86/kvm.h
+++ b/include/asm-x86/kvm.h
@@ -230,4 +230,22 @@ struct kvm_pit_state {
#define KVM_TRC_APIC_ACCESS (KVM_TRC_HANDLER + 0x14)
#define KVM_TRC_TDP_FAULT (KVM_TRC_HANDLER + 0x15)
+struct kvm_debug_exit_arch {
+ __u32 exception;
+ __u32 pad;
+ __u64 pc;
+ __u64 dr6;
+ __u64 dr7;
+};
+
+#define KVM_GUESTDBG_USE_SW_BP 0x00010000
+#define KVM_GUESTDBG_USE_HW_BP 0x00020000
+#define KVM_GUESTDBG_INJECT_DB 0x00040000
+#define KVM_GUESTDBG_INJECT_BP 0x00080000
+
+/* for KVM_SET_GUEST_DEBUG */
+struct kvm_guest_debug_arch {
+ __u64 debugreg[8];
+};
+
#endif
Index: b/include/asm-x86/kvm_host.h
===================================================================
--- a/include/asm-x86/kvm_host.h
+++ b/include/asm-x86/kvm_host.h
@@ -54,6 +54,8 @@
#define KVM_PAGES_PER_HPAGE (KVM_HPAGE_SIZE / PAGE_SIZE)
#define DE_VECTOR 0
+#define DB_VECTOR 1
+#define BP_VECTOR 3
#define UD_VECTOR 6
#define NM_VECTOR 7
#define DF_VECTOR 8
@@ -122,12 +124,6 @@ enum {
#define KVM_NR_MEM_OBJS 40
-struct kvm_guest_debug {
- int enabled;
- unsigned long bp[4];
- int singlestep;
-};
-
/*
* We don't want allocation failures within the mmu code, so we preallocate
* enough memory for a single page fault in a cache.
@@ -383,8 +379,7 @@ struct kvm_x86_ops {
void (*vcpu_put)(struct kvm_vcpu *vcpu);
int (*set_guest_debug)(struct kvm_vcpu *vcpu,
- struct kvm_debug_guest *dbg);
- void (*guest_debug_pre)(struct kvm_vcpu *vcpu);
+ struct kvm_guest_debug *dbg);
int (*get_msr)(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata);
int (*set_msr)(struct kvm_vcpu *vcpu, u32 msr_index, u64 data);
u64 (*get_segment_base)(struct kvm_vcpu *vcpu, int seg);
Index: b/include/linux/kvm.h
===================================================================
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -125,6 +125,7 @@ struct kvm_run {
__u64 data_offset; /* relative to kvm_run start */
} io;
struct {
+ struct kvm_debug_exit_arch arch;
} debug;
/* KVM_EXIT_MMIO */
struct {
@@ -192,21 +193,6 @@ struct kvm_interrupt {
__u32 irq;
};
-struct kvm_breakpoint {
- __u32 enabled;
- __u32 padding;
- __u64 address;
-};
-
-/* for KVM_DEBUG_GUEST */
-struct kvm_debug_guest {
- /* int */
- __u32 enabled;
- __u32 pad;
- struct kvm_breakpoint breakpoints[4];
- __u32 singlestep;
-};
-
/* for KVM_GET_DIRTY_LOG */
struct kvm_dirty_log {
__u32 slot;
@@ -267,6 +253,17 @@ struct kvm_s390_interrupt {
__u64 parm64;
};
+/* for KVM_SET_GUEST_DEBUG */
+
+#define KVM_GUESTDBG_ENABLE 0x00000001
+#define KVM_GUESTDBG_SINGLESTEP 0x00000002
+
+struct kvm_guest_debug {
+ __u32 control;
+ __u32 pad;
+ struct kvm_guest_debug_arch arch;
+};
+
#define KVM_TRC_SHIFT 16
/*
* kvm trace categories
@@ -346,6 +343,7 @@ struct kvm_trace_rec {
#define KVM_CAP_NOP_IO_DELAY 12
#define KVM_CAP_PV_MMU 13
#define KVM_CAP_MP_STATE 14
+#define KVM_CAP_SET_GUEST_DEBUG 15
/*
* ioctls for VM fds
@@ -382,7 +380,8 @@ struct kvm_trace_rec {
#define KVM_SET_SREGS _IOW(KVMIO, 0x84, struct kvm_sregs)
#define KVM_TRANSLATE _IOWR(KVMIO, 0x85, struct kvm_translation)
#define KVM_INTERRUPT _IOW(KVMIO, 0x86, struct kvm_interrupt)
-#define KVM_DEBUG_GUEST _IOW(KVMIO, 0x87, struct kvm_debug_guest)
+/* KVM_DEBUG_GUEST is no longer supported, use KVM_SET_GUEST_DEBUG instead */
+#define KVM_DEBUG_GUEST __KVM_DEPRECATED_DEBUG_GUEST
#define KVM_GET_MSRS _IOWR(KVMIO, 0x88, struct kvm_msrs)
#define KVM_SET_MSRS _IOW(KVMIO, 0x89, struct kvm_msrs)
#define KVM_SET_CPUID _IOW(KVMIO, 0x8a, struct kvm_cpuid)
@@ -409,5 +408,25 @@ struct kvm_trace_rec {
#define KVM_S390_INITIAL_RESET _IO(KVMIO, 0x97)
#define KVM_GET_MP_STATE _IOR(KVMIO, 0x98, struct kvm_mp_state)
#define KVM_SET_MP_STATE _IOW(KVMIO, 0x99, struct kvm_mp_state)
+/* Available with KVM_CAP_SET_GUEST_DEBUG */
+#define KVM_SET_GUEST_DEBUG _IOW(KVMIO, 0x9a, struct kvm_guest_debug)
+
+/*
+ * Deprecated interfaces
+ */
+struct kvm_breakpoint {
+ __u32 enabled;
+ __u32 padding;
+ __u64 address;
+};
+
+struct kvm_debug_guest {
+ __u32 enabled;
+ __u32 pad;
+ struct kvm_breakpoint breakpoints[4];
+ __u32 singlestep;
+};
+
+#define __KVM_DEPRECATED_DEBUG_GUEST _IOW(KVMIO, 0x87, struct kvm_debug_guest)
#endif
Index: b/include/linux/kvm_host.h
===================================================================
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -66,7 +66,7 @@ struct kvm_vcpu {
struct kvm_run *run;
int guest_mode;
unsigned long requests;
- struct kvm_guest_debug guest_debug;
+ unsigned long guest_debug;
int fpu_active;
int guest_fpu_loaded;
wait_queue_head_t wq;
@@ -237,8 +237,8 @@ int kvm_arch_vcpu_ioctl_get_mpstate(stru
struct kvm_mp_state *mp_state);
int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
struct kvm_mp_state *mp_state);
-int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu,
- struct kvm_debug_guest *dbg);
+int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug *dbg);
int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run);
int kvm_arch_init(void *opaque);
Index: b/arch/x86/kvm/vmx.c
===================================================================
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -465,8 +465,13 @@ static void update_exception_bitmap(stru
eb = (1u << PF_VECTOR) | (1u << UD_VECTOR);
if (!vcpu->fpu_active)
eb |= 1u << NM_VECTOR;
- if (vcpu->guest_debug.enabled)
- eb |= 1u << 1;
+ if (vcpu->guest_debug & KVM_GUESTDBG_ENABLE) {
+ if (vcpu->guest_debug &
+ (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))
+ eb |= 1u << DB_VECTOR;
+ if (vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP)
+ eb |= 1u << BP_VECTOR;
+ }
if (vcpu->arch.rmode.active)
eb = ~0;
if (vm_need_ept())
@@ -950,40 +955,23 @@ static void vcpu_put_rsp_rip(struct kvm_
vmcs_writel(GUEST_RIP, vcpu->arch.rip);
}
-static int set_guest_debug(struct kvm_vcpu *vcpu, struct kvm_debug_guest *dbg)
+static int set_guest_debug(struct kvm_vcpu *vcpu, struct kvm_guest_debug *dbg)
{
- unsigned long dr7 = 0x400;
- int old_singlestep;
-
- old_singlestep = vcpu->guest_debug.singlestep;
-
- vcpu->guest_debug.enabled = dbg->enabled;
- if (vcpu->guest_debug.enabled) {
- int i;
-
- dr7 |= 0x200; /* exact */
- for (i = 0; i < 4; ++i) {
- if (!dbg->breakpoints[i].enabled)
- continue;
- vcpu->guest_debug.bp[i] = dbg->breakpoints[i].address;
- dr7 |= 2 << (i*2); /* global enable */
- dr7 |= 0 << (i*4+16); /* execution breakpoint */
- }
-
- vcpu->guest_debug.singlestep = dbg->singlestep;
- } else
- vcpu->guest_debug.singlestep = 0;
+ int old_debug = vcpu->guest_debug;
+ unsigned long flags;
- if (old_singlestep && !vcpu->guest_debug.singlestep) {
- unsigned long flags;
+ vcpu->guest_debug = dbg->control;
+ if (!(vcpu->guest_debug & KVM_GUESTDBG_ENABLE))
+ vcpu->guest_debug = 0;
- flags = vmcs_readl(GUEST_RFLAGS);
+ flags = vmcs_readl(GUEST_RFLAGS);
+ if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
+ flags |= X86_EFLAGS_TF | X86_EFLAGS_RF;
+ else if (old_debug & KVM_GUESTDBG_SINGLESTEP)
flags &= ~(X86_EFLAGS_TF | X86_EFLAGS_RF);
- vmcs_writel(GUEST_RFLAGS, flags);
- }
+ vmcs_writel(GUEST_RFLAGS, flags);
update_exception_bitmap(vcpu);
- vmcs_writel(GUEST_DR7, dr7);
return 0;
}
@@ -2196,24 +2184,6 @@ static int vmx_set_tss_addr(struct kvm *
return 0;
}
-static void kvm_guest_debug_pre(struct kvm_vcpu *vcpu)
-{
- struct kvm_guest_debug *dbg = &vcpu->guest_debug;
-
- set_debugreg(dbg->bp[0], 0);
- set_debugreg(dbg->bp[1], 1);
- set_debugreg(dbg->bp[2], 2);
- set_debugreg(dbg->bp[3], 3);
-
- if (dbg->singlestep) {
- unsigned long flags;
-
- flags = vmcs_readl(GUEST_RFLAGS);
- flags |= X86_EFLAGS_TF | X86_EFLAGS_RF;
- vmcs_writel(GUEST_RFLAGS, flags);
- }
-}
-
static int handle_rmode_exception(struct kvm_vcpu *vcpu,
int vec, u32 err_code)
{
@@ -2233,7 +2203,7 @@ static int handle_rmode_exception(struct
static int handle_exception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
- u32 intr_info, error_code;
+ u32 intr_info, ex_no, error_code;
unsigned long cr2, rip;
u32 vect_info;
enum emulation_result er;
@@ -2291,14 +2261,16 @@ static int handle_exception(struct kvm_v
return 1;
}
- if ((intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VECTOR_MASK)) ==
- (INTR_TYPE_EXCEPTION | 1)) {
+ ex_no = intr_info & INTR_INFO_VECTOR_MASK;
+ if (ex_no == DB_VECTOR || ex_no == BP_VECTOR) {
kvm_run->exit_reason = KVM_EXIT_DEBUG;
- return 0;
+ kvm_run->debug.arch.pc = vmcs_readl(GUEST_CS_BASE) + rip;
+ kvm_run->debug.arch.exception = ex_no;
+ } else {
+ kvm_run->exit_reason = KVM_EXIT_EXCEPTION;
+ kvm_run->ex.exception = ex_no;
+ kvm_run->ex.error_code = error_code;
}
- kvm_run->exit_reason = KVM_EXIT_EXCEPTION;
- kvm_run->ex.exception = intr_info & INTR_INFO_VECTOR_MASK;
- kvm_run->ex.error_code = error_code;
return 0;
}
@@ -3194,7 +3166,6 @@ static struct kvm_x86_ops vmx_x86_ops =
.vcpu_put = vmx_vcpu_put,
.set_guest_debug = set_guest_debug,
- .guest_debug_pre = kvm_guest_debug_pre,
.get_msr = vmx_get_msr,
.set_msr = vmx_set_msr,
.get_segment_base = vmx_get_segment_base,
Index: b/arch/x86/kvm/svm.c
===================================================================
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -903,9 +903,32 @@ static void svm_set_segment(struct kvm_v
}
-static int svm_guest_debug(struct kvm_vcpu *vcpu, struct kvm_debug_guest *dbg)
+static int svm_guest_debug(struct kvm_vcpu *vcpu, struct kvm_guest_debug *dbg)
{
- return -EOPNOTSUPP;
+ int old_debug = vcpu->guest_debug;
+ struct vcpu_svm *svm = to_svm(vcpu);
+
+ vcpu->guest_debug = dbg->control;
+
+ svm->vmcb->control.intercept_exceptions &=
+ ~((1 << DB_VECTOR) | (1 << BP_VECTOR));
+ if (vcpu->guest_debug & KVM_GUESTDBG_ENABLE) {
+ if (vcpu->guest_debug &
+ (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))
+ svm->vmcb->control.intercept_exceptions |=
+ 1 << DB_VECTOR;
+ if (vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP)
+ svm->vmcb->control.intercept_exceptions |=
+ 1 << BP_VECTOR;
+ } else
+ vcpu->guest_debug = 0;
+
+ if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
+ svm->vmcb->save.rflags |= X86_EFLAGS_TF | X86_EFLAGS_RF;
+ else if (old_debug & KVM_GUESTDBG_SINGLESTEP)
+ svm->vmcb->save.rflags &= ~(X86_EFLAGS_TF | X86_EFLAGS_RF);
+
+ return 0;
}
static int svm_get_irq(struct kvm_vcpu *vcpu)
@@ -1017,6 +1040,22 @@ static int pf_interception(struct vcpu_s
return kvm_mmu_page_fault(&svm->vcpu, fault_address, error_code);
}
+static int db_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
+{
+ kvm_run->exit_reason = KVM_EXIT_DEBUG;
+ kvm_run->debug.arch.pc = svm->vmcb->save.cs.base + svm->vmcb->save.rip;
+ kvm_run->debug.arch.exception = DB_VECTOR;
+ return 0;
+}
+
+static int bp_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
+{
+ kvm_run->exit_reason = KVM_EXIT_DEBUG;
+ kvm_run->debug.arch.pc = svm->vmcb->save.cs.base + svm->vmcb->save.rip;
+ kvm_run->debug.arch.exception = BP_VECTOR;
+ return 0;
+}
+
static int ud_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
{
int er;
@@ -1395,6 +1434,8 @@ static int (*svm_exit_handlers[])(struct
[SVM_EXIT_WRITE_DR3] = emulate_on_interception,
[SVM_EXIT_WRITE_DR5] = emulate_on_interception,
[SVM_EXIT_WRITE_DR7] = emulate_on_interception,
+ [SVM_EXIT_EXCP_BASE + DB_VECTOR] = db_interception,
+ [SVM_EXIT_EXCP_BASE + BP_VECTOR] = bp_interception,
[SVM_EXIT_EXCP_BASE + UD_VECTOR] = ud_interception,
[SVM_EXIT_EXCP_BASE + PF_VECTOR] = pf_interception,
[SVM_EXIT_EXCP_BASE + NM_VECTOR] = nm_interception,
Index: b/virt/kvm/kvm_main.c
===================================================================
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -1042,13 +1042,13 @@ out_free2:
r = 0;
break;
}
- case KVM_DEBUG_GUEST: {
- struct kvm_debug_guest dbg;
+ case KVM_SET_GUEST_DEBUG: {
+ struct kvm_guest_debug dbg;
r = -EFAULT;
if (copy_from_user(&dbg, argp, sizeof dbg))
goto out;
- r = kvm_arch_vcpu_ioctl_debug_guest(vcpu, &dbg);
+ r = kvm_arch_vcpu_ioctl_set_guest_debug(vcpu, &dbg);
if (r)
goto out;
r = 0;
Index: b/arch/ia64/kvm/kvm-ia64.c
===================================================================
--- a/arch/ia64/kvm/kvm-ia64.c
+++ b/arch/ia64/kvm/kvm-ia64.c
@@ -1299,8 +1299,8 @@ int kvm_arch_vcpu_ioctl_set_fpu(struct k
return -EINVAL;
}
-int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu,
- struct kvm_debug_guest *dbg)
+int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug *dbg)
{
return -EINVAL;
}
Index: b/arch/powerpc/kvm/powerpc.c
===================================================================
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -240,8 +240,8 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *
{
}
-int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu,
- struct kvm_debug_guest *dbg)
+int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug *dbg)
{
return -ENOTSUPP;
}
Index: b/arch/s390/kvm/kvm-s390.c
===================================================================
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -401,8 +401,8 @@ int kvm_arch_vcpu_ioctl_translate(struct
return -EINVAL; /* not implemented yet */
}
-int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu,
- struct kvm_debug_guest *dbg)
+int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug *dbg)
{
return -EINVAL; /* not implemented yet */
}
Index: b/include/asm-ia64/kvm.h
===================================================================
--- a/include/asm-ia64/kvm.h
+++ b/include/asm-ia64/kvm.h
@@ -208,4 +208,11 @@ struct kvm_sregs {
struct kvm_fpu {
};
+struct kvm_debug_exit_arch {
+};
+
+/* for KVM_SET_GUEST_DEBUG */
+struct kvm_guest_debug_arch {
+};
+
#endif
Index: b/include/asm-powerpc/kvm.h
===================================================================
--- a/include/asm-powerpc/kvm.h
+++ b/include/asm-powerpc/kvm.h
@@ -52,4 +52,11 @@ struct kvm_fpu {
__u64 fpr[32];
};
+struct kvm_debug_exit_arch {
+};
+
+/* for KVM_SET_GUEST_DEBUG */
+struct kvm_guest_debug_arch {
+};
+
#endif /* __LINUX_KVM_POWERPC_H */
Index: b/include/asm-s390/kvm.h
===================================================================
--- a/include/asm-s390/kvm.h
+++ b/include/asm-s390/kvm.h
@@ -42,4 +42,11 @@ struct kvm_fpu {
__u64 fprs[16];
};
+struct kvm_debug_exit_arch {
+};
+
+/* for KVM_SET_GUEST_DEBUG */
+struct kvm_guest_debug_arch {
+};
+
#endif
^ permalink raw reply [flat|nested] 21+ messages in thread* [PATCH 10/11] KVM-x86: Properly virtualize debug registers
2008-05-26 22:05 [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Jan Kiszka
` (8 preceding siblings ...)
2008-05-26 22:10 ` [PATCH 9/11] KVM: New guest debugging interface Jan Kiszka
@ 2008-05-26 22:10 ` Jan Kiszka
2008-05-26 22:10 ` [PATCH 11/11] KVM-x86: Wire up host-managed " Jan Kiszka
2008-05-27 9:50 ` [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Avi Kivity
11 siblings, 0 replies; 21+ messages in thread
From: Jan Kiszka @ 2008-05-26 22:10 UTC (permalink / raw)
To: kvm-devel; +Cc: Avi Kivity, Hollis Blanchard, Jerone Young, Joerg Roedel
So far KVM only had basic x86 debug register support, once introduced to
realize guest debugging that way. The guest itself was not able to use
those registers.
This patch now adds (almost) full support for guest self-debugging via
hardware registers. It refactors the code, moving generic parts out of
svm (vmx was already cleaned up by the KVM_SET_GUEST_DEBUG patches), it
ensures that the registers are properly switched between host and guest,
and it also wires up debug register usage by the host. The latter allows
for hardware breakpoints/watchpoints in guest code. If this is enabled,
the guest will only see faked debug registers without functionality,
but with content reflecting the guest's modifications.
Tested on Intel only, but SVM /should/ work as well.
Known limitations: Trapping on tss switch won't work - most probably on
Intel.
Credits also go to Joerg Roedel - I used his debugging series as
platform for this patch.
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
arch/x86/kvm/kvm_svm.h | 6 --
arch/x86/kvm/svm.c | 125 +++++++++++++++++----------------------------
arch/x86/kvm/vmx.c | 114 +++++++++++++++++++++++++++++++++++------
arch/x86/kvm/vmx.h | 2
arch/x86/kvm/x86.c | 32 +++++++++++
include/asm-x86/kvm_host.h | 22 +++++++
6 files changed, 200 insertions(+), 101 deletions(-)
Index: b/arch/x86/kvm/kvm_svm.h
===================================================================
--- a/arch/x86/kvm/kvm_svm.h
+++ b/arch/x86/kvm/kvm_svm.h
@@ -18,7 +18,6 @@ static const u32 host_save_user_msrs[] =
};
#define NR_HOST_SAVE_USER_MSRS ARRAY_SIZE(host_save_user_msrs)
-#define NUM_DB_REGS 4
struct kvm_vcpu;
@@ -29,16 +28,11 @@ struct vcpu_svm {
struct svm_cpu_data *svm_data;
uint64_t asid_generation;
- unsigned long db_regs[NUM_DB_REGS];
-
u64 next_rip;
u64 host_user_msrs[NR_HOST_SAVE_USER_MSRS];
u64 host_gs_base;
unsigned long host_cr2;
- unsigned long host_db_regs[NUM_DB_REGS];
- unsigned long host_dr6;
- unsigned long host_dr7;
u32 *msrpm;
};
Index: b/arch/x86/kvm/svm.c
===================================================================
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -35,13 +35,6 @@ MODULE_LICENSE("GPL");
#define IOPM_ALLOC_ORDER 2
#define MSRPM_ALLOC_ORDER 1
-#define DB_VECTOR 1
-#define UD_VECTOR 6
-#define GP_VECTOR 13
-
-#define DR7_GD_MASK (1 << 13)
-#define DR6_BD_MASK (1 << 13)
-
#define SEG_TYPE_LDT 2
#define SEG_TYPE_BUSY_TSS16 3
@@ -157,32 +150,6 @@ static inline void kvm_write_cr2(unsigne
asm volatile ("mov %0, %%cr2" :: "r" (val));
}
-static inline unsigned long read_dr6(void)
-{
- unsigned long dr6;
-
- asm volatile ("mov %%dr6, %0" : "=r" (dr6));
- return dr6;
-}
-
-static inline void write_dr6(unsigned long val)
-{
- asm volatile ("mov %0, %%dr6" :: "r" (val));
-}
-
-static inline unsigned long read_dr7(void)
-{
- unsigned long dr7;
-
- asm volatile ("mov %%dr7, %0" : "=r" (dr7));
- return dr7;
-}
-
-static inline void write_dr7(unsigned long val)
-{
- asm volatile ("mov %0, %%dr7" :: "r" (val));
-}
-
static inline void force_new_asid(struct kvm_vcpu *vcpu)
{
to_svm(vcpu)->asid_generation--;
@@ -645,7 +612,6 @@ static struct kvm_vcpu *svm_create_vcpu(
clear_page(svm->vmcb);
svm->vmcb_pa = page_to_pfn(page) << PAGE_SHIFT;
svm->asid_generation = 0;
- memset(svm->db_regs, 0, sizeof(svm->db_regs));
init_vmcb(svm);
fx_init(&svm->vcpu);
@@ -970,7 +936,29 @@ static void new_asid(struct vcpu_svm *sv
static unsigned long svm_get_dr(struct kvm_vcpu *vcpu, int dr)
{
- unsigned long val = to_svm(vcpu)->db_regs[dr];
+ struct vcpu_svm *svm = to_svm(vcpu);
+ unsigned long val;
+
+ switch (dr) {
+ case 0 ... 3:
+ val = vcpu->arch.db[dr];
+ break;
+ case 6:
+ if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
+ val = vcpu->arch.dr6;
+ else
+ val = svm->vmcb->save.dr6;
+ break;
+ case 7:
+ if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
+ val = vcpu->arch.dr7;
+ else
+ val = svm->vmcb->save.dr7;
+ break;
+ default:
+ val = 0;
+ }
+
KVMTRACE_2D(DR_READ, vcpu, (u32)dr, (u32)val, handler);
return val;
}
@@ -980,33 +968,40 @@ static void svm_set_dr(struct kvm_vcpu *
{
struct vcpu_svm *svm = to_svm(vcpu);
- *exception = 0;
+ KVMTRACE_2D(DR_WRITE, vcpu, (u32)dr, (u32)value, handler);
- if (svm->vmcb->save.dr7 & DR7_GD_MASK) {
- svm->vmcb->save.dr7 &= ~DR7_GD_MASK;
- svm->vmcb->save.dr6 |= DR6_BD_MASK;
- *exception = DB_VECTOR;
- return;
- }
+ *exception = 0;
switch (dr) {
case 0 ... 3:
- svm->db_regs[dr] = value;
+ vcpu->arch.db[dr] = value;
+ if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP))
+ vcpu->arch.eff_db[dr] = value;
return;
case 4 ... 5:
- if (vcpu->arch.cr4 & X86_CR4_DE) {
+ if (vcpu->arch.cr4 & X86_CR4_DE)
*exception = UD_VECTOR;
+ return;
+ case 6:
+ if (value & 0xffffffff00000000ULL) {
+ *exception = GP_VECTOR;
return;
}
- case 7: {
- if (value & ~((1ULL << 32) - 1)) {
+ vcpu->arch.dr6 = (value & DR6_VOLATILE) | DR6_FIXED_1;
+ return;
+ case 7:
+ if (value & 0xffffffff00000000ULL) {
*exception = GP_VECTOR;
return;
}
- svm->vmcb->save.dr7 = value;
+ vcpu->arch.dr7 = (value & DR7_VOLATILE) | DR7_FIXED_1;
+ if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)) {
+ svm->vmcb->save.dr7 = vcpu->arch.dr7;
+ vcpu->arch.switch_db_regs = (value & DR7_BP_EN_MASK);
+ }
return;
- }
default:
+ /* FIXME: Possible case? */
printk(KERN_DEBUG "%s: unexpected dr %u\n",
__func__, dr);
*exception = UD_VECTOR;
@@ -1042,6 +1037,11 @@ static int pf_interception(struct vcpu_s
static int db_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
{
+ if (!(svm->vcpu.guest_debug &
+ (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))) {
+ kvm_queue_exception(&svm->vcpu, DB_VECTOR);
+ return 1;
+ }
kvm_run->exit_reason = KVM_EXIT_DEBUG;
kvm_run->debug.arch.pc = svm->vmcb->save.cs.base + svm->vmcb->save.rip;
kvm_run->debug.arch.exception = DB_VECTOR;
@@ -1681,22 +1681,6 @@ static int svm_set_tss_addr(struct kvm *
return 0;
}
-static void save_db_regs(unsigned long *db_regs)
-{
- asm volatile ("mov %%dr0, %0" : "=r"(db_regs[0]));
- asm volatile ("mov %%dr1, %0" : "=r"(db_regs[1]));
- asm volatile ("mov %%dr2, %0" : "=r"(db_regs[2]));
- asm volatile ("mov %%dr3, %0" : "=r"(db_regs[3]));
-}
-
-static void load_db_regs(unsigned long *db_regs)
-{
- asm volatile ("mov %0, %%dr0" : : "r"(db_regs[0]));
- asm volatile ("mov %0, %%dr1" : : "r"(db_regs[1]));
- asm volatile ("mov %0, %%dr2" : : "r"(db_regs[2]));
- asm volatile ("mov %0, %%dr3" : : "r"(db_regs[3]));
-}
-
static void svm_flush_tlb(struct kvm_vcpu *vcpu)
{
force_new_asid(vcpu);
@@ -1745,19 +1729,11 @@ static void svm_vcpu_run(struct kvm_vcpu
gs_selector = read_gs();
ldt_selector = read_ldt();
svm->host_cr2 = kvm_read_cr2();
- svm->host_dr6 = read_dr6();
- svm->host_dr7 = read_dr7();
svm->vmcb->save.cr2 = vcpu->arch.cr2;
/* required for live migration with NPT */
if (npt_enabled)
svm->vmcb->save.cr3 = vcpu->arch.cr3;
- if (svm->vmcb->save.dr7 & 0xff) {
- write_dr7(0);
- save_db_regs(svm->host_db_regs);
- load_db_regs(svm->db_regs);
- }
-
clgi();
local_irq_enable();
@@ -1867,13 +1843,8 @@ static void svm_vcpu_run(struct kvm_vcpu
#endif
);
- if ((svm->vmcb->save.dr7 & 0xff))
- load_db_regs(svm->host_db_regs);
-
vcpu->arch.cr2 = svm->vmcb->save.cr2;
- write_dr6(svm->host_dr6);
- write_dr7(svm->host_dr7);
kvm_write_cr2(svm->host_cr2);
load_fs(fs_selector);
Index: b/arch/x86/kvm/vmx.c
===================================================================
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -2045,7 +2045,6 @@ static int vmx_vcpu_reset(struct kvm_vcp
vmcs_writel(GUEST_RIP, 0);
vmcs_writel(GUEST_RSP, 0);
- /* todo: dr0 = dr1 = dr2 = dr3 = 0; dr6 = 0xffff0ff0 */
vmcs_writel(GUEST_DR7, 0x400);
vmcs_writel(GUEST_GDTR_BASE, 0);
@@ -2204,7 +2203,7 @@ static int handle_exception(struct kvm_v
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
u32 intr_info, ex_no, error_code;
- unsigned long cr2, rip;
+ unsigned long cr2, rip, dr6;
u32 vect_info;
enum emulation_result er;
@@ -2262,14 +2261,28 @@ static int handle_exception(struct kvm_v
}
ex_no = intr_info & INTR_INFO_VECTOR_MASK;
- if (ex_no == DB_VECTOR || ex_no == BP_VECTOR) {
+ switch (ex_no) {
+ case DB_VECTOR:
+ dr6 = vmcs_readl(EXIT_QUALIFICATION);
+ if (!(vcpu->guest_debug &
+ (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))) {
+ vcpu->arch.dr6 = dr6 | DR6_FIXED_1;
+ kvm_queue_exception(vcpu, DB_VECTOR);
+ return 1;
+ }
+ kvm_run->debug.arch.dr6 = dr6 | DR6_FIXED_1;
+ kvm_run->debug.arch.dr7 = vmcs_readl(GUEST_DR7);
+ /* fall through */
+ case BP_VECTOR:
kvm_run->exit_reason = KVM_EXIT_DEBUG;
kvm_run->debug.arch.pc = vmcs_readl(GUEST_CS_BASE) + rip;
kvm_run->debug.arch.exception = ex_no;
- } else {
+ break;
+ default:
kvm_run->exit_reason = KVM_EXIT_EXCEPTION;
kvm_run->ex.exception = ex_no;
kvm_run->ex.error_code = error_code;
+ break;
}
return 0;
}
@@ -2415,22 +2428,45 @@ static int handle_dr(struct kvm_vcpu *vc
unsigned long val;
int dr, reg;
- /*
- * FIXME: this code assumes the host is debugging the guest.
- * need to deal with guest debugging itself too.
- */
+ dr = vmcs_readl(GUEST_DR7);
+ if (dr & DR7_GD) {
+ /*
+ * As the vm-exit takes precedence over the debug trap, we
+ * need to emulate the latter, either for the host or the
+ * guest debugging itself.
+ */
+ if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP) {
+ kvm_run->debug.arch.dr6 = vcpu->arch.dr6;
+ kvm_run->debug.arch.dr7 = dr;
+ kvm_run->debug.arch.pc =
+ vmcs_readl(GUEST_CS_BASE) +
+ vmcs_readl(GUEST_RIP);
+ kvm_run->debug.arch.exception = DB_VECTOR;
+ kvm_run->exit_reason = KVM_EXIT_DEBUG;
+ return 0;
+ } else {
+ vcpu->arch.dr7 &= ~DR7_GD;
+ vcpu->arch.dr6 |= DR6_BD;
+ vmcs_writel(GUEST_DR7, vcpu->arch.dr7);
+ kvm_queue_exception(vcpu, DB_VECTOR);
+ return 1;
+ }
+ }
+
exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
- dr = exit_qualification & 7;
- reg = (exit_qualification >> 8) & 15;
+ dr = exit_qualification & DEBUG_REG_ACCESS_NUM;
+ reg = DEBUG_REG_ACCESS_REG(exit_qualification);
vcpu_load_rsp_rip(vcpu);
- if (exit_qualification & 16) {
- /* mov from dr */
+ if (exit_qualification & TYPE_MOV_FROM_DR) {
switch (dr) {
+ case 0 ... 3:
+ val = vcpu->arch.db[dr];
+ break;
case 6:
- val = 0xffff0ff0;
+ val = vcpu->arch.dr6;
break;
case 7:
- val = 0x400;
+ val = vcpu->arch.dr7;
break;
default:
val = 0;
@@ -2438,7 +2474,38 @@ static int handle_dr(struct kvm_vcpu *vc
vcpu->arch.regs[reg] = val;
KVMTRACE_2D(DR_READ, vcpu, (u32)dr, (u32)val, handler);
} else {
- /* mov to dr */
+ val = vcpu->arch.regs[reg];
+ switch (dr) {
+ case 0 ... 3:
+ vcpu->arch.db[dr] = val;
+ if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP))
+ vcpu->arch.eff_db[dr] = val;
+ break;
+ case 4 ... 5:
+ if (vcpu->arch.cr4 & X86_CR4_DE)
+ kvm_queue_exception(vcpu, UD_VECTOR);
+ break;
+ case 6:
+ if (val & 0xffffffff00000000ULL) {
+ kvm_queue_exception(vcpu, GP_VECTOR);
+ break;
+ }
+ vcpu->arch.dr6 = (val & DR6_VOLATILE) | DR6_FIXED_1;
+ break;
+ case 7:
+ if (val & 0xffffffff00000000ULL) {
+ kvm_queue_exception(vcpu, GP_VECTOR);
+ break;
+ }
+ vcpu->arch.dr7 = (val & DR7_VOLATILE) | DR7_FIXED_1;
+ if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)) {
+ vmcs_writel(GUEST_DR7, vcpu->arch.dr7);
+ vcpu->arch.switch_db_regs =
+ (val & DR7_BP_EN_MASK);
+ }
+ break;
+ }
+ KVMTRACE_2D(DR_WRITE, vcpu, (u32)dr, (u32)val, handler);
}
vcpu_put_rsp_rip(vcpu);
skip_emulated_instruction(vcpu);
@@ -2571,7 +2638,18 @@ static int handle_task_switch(struct kvm
reason = (u32)exit_qualification >> 30;
tss_selector = exit_qualification;
- return kvm_task_switch(vcpu, tss_selector, reason);
+ if (!kvm_task_switch(vcpu, tss_selector, reason))
+ return 0;
+
+ /* clear all local breakpoint enable flags */
+ vmcs_writel(GUEST_DR7, vmcs_readl(GUEST_DR7) & ~55);
+
+ /*
+ * TODO: What about debug traps on tss switch?
+ * Are we supposed to inject them and update dr6?
+ */
+
+ return 1;
}
static int handle_ept_violation(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
@@ -2896,6 +2974,8 @@ static void vmx_vcpu_run(struct kvm_vcpu
*/
vmcs_writel(HOST_CR0, read_cr0());
+ set_debugreg(vcpu->arch.dr6, 6);
+
asm(
/* Store host registers */
#ifdef CONFIG_X86_64
@@ -3011,6 +3091,8 @@ static void vmx_vcpu_run(struct kvm_vcpu
#endif
);
+ get_debugreg(vcpu->arch.dr6, 6);
+
vmx->idt_vectoring_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);
if (vmx->rmode.irq.pending)
fixup_rmode_irq(vmx);
Index: b/arch/x86/kvm/vmx.h
===================================================================
--- a/arch/x86/kvm/vmx.h
+++ b/arch/x86/kvm/vmx.h
@@ -304,7 +304,7 @@ enum vmcs_field {
#define DEBUG_REG_ACCESS_TYPE 0x10 /* 4, direction of access */
#define TYPE_MOV_TO_DR (0 << 4)
#define TYPE_MOV_FROM_DR (1 << 4)
-#define DEBUG_REG_ACCESS_REG 0xf00 /* 11:8, general purpose reg. */
+#define DEBUG_REG_ACCESS_REG(eq) (((eq) >> 8) & 0xf) /* 11:8, general purpose reg. */
/* segment AR */
Index: b/arch/x86/kvm/x86.c
===================================================================
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2720,7 +2720,7 @@ static int __vcpu_run(struct kvm_vcpu *v
pr_debug("vcpu %d received sipi with vector # %x\n",
vcpu->vcpu_id, vcpu->arch.sipi_vector);
kvm_lapic_reset(vcpu);
- r = kvm_x86_ops->vcpu_reset(vcpu);
+ r = kvm_arch_vcpu_reset(vcpu);
if (r)
return r;
vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
@@ -2811,9 +2811,34 @@ again:
if (test_and_clear_bit(KVM_REQ_TLB_FLUSH, &vcpu->requests))
kvm_x86_ops->tlb_flush(vcpu);
+ get_debugreg(vcpu->arch.host_dr6, 6);
+ get_debugreg(vcpu->arch.host_dr7, 7);
+ if (unlikely(vcpu->arch.switch_db_regs)) {
+ get_debugreg(vcpu->arch.host_db[0], 0);
+ get_debugreg(vcpu->arch.host_db[1], 1);
+ get_debugreg(vcpu->arch.host_db[2], 2);
+ get_debugreg(vcpu->arch.host_db[3], 3);
+
+ set_debugreg(0, 7);
+ set_debugreg(vcpu->arch.eff_db[0], 0);
+ set_debugreg(vcpu->arch.eff_db[1], 1);
+ set_debugreg(vcpu->arch.eff_db[2], 2);
+ set_debugreg(vcpu->arch.eff_db[3], 3);
+ }
+
KVMTRACE_0D(VMENTRY, vcpu, entryexit);
kvm_x86_ops->run(vcpu, kvm_run);
+ if (unlikely(vcpu->arch.switch_db_regs)) {
+ set_debugreg(0, 7);
+ set_debugreg(vcpu->arch.host_db[0], 0);
+ set_debugreg(vcpu->arch.host_db[1], 1);
+ set_debugreg(vcpu->arch.host_db[2], 2);
+ set_debugreg(vcpu->arch.host_db[3], 3);
+ }
+ set_debugreg(vcpu->arch.host_dr6, 6);
+ set_debugreg(vcpu->arch.host_dr7, 7);
+
vcpu->guest_mode = 0;
local_irq_enable();
@@ -3781,6 +3806,11 @@ void kvm_arch_vcpu_destroy(struct kvm_vc
int kvm_arch_vcpu_reset(struct kvm_vcpu *vcpu)
{
+ vcpu->arch.switch_db_regs = 0;
+ memset(vcpu->arch.db, 0, sizeof(vcpu->arch.db));
+ vcpu->arch.dr6 = DR6_FIXED_1;
+ vcpu->arch.dr7 = DR7_FIXED_1;
+
return kvm_x86_ops->vcpu_reset(vcpu);
}
Index: b/include/asm-x86/kvm_host.h
===================================================================
--- a/include/asm-x86/kvm_host.h
+++ b/include/asm-x86/kvm_host.h
@@ -124,6 +124,19 @@ enum {
#define KVM_NR_MEM_OBJS 40
+#define KVM_NR_DB_REGS 4
+
+#define DR6_BD (1 << 13)
+#define DR6_BS (1 << 14)
+#define DR6_FIXED_1 0xffff0ff0
+#define DR6_VOLATILE 0x0000e00f
+
+#define DR7_BP_EN_MASK 0x000000ff
+#define DR7_GE (1 << 9)
+#define DR7_GD (1 << 13)
+#define DR7_FIXED_1 0x00000400
+#define DR7_VOLATILE 0xffff23ff
+
/*
* We don't want allocation failures within the mmu code, so we preallocate
* enough memory for a single page fault in a cache.
@@ -284,6 +297,15 @@ struct kvm_vcpu_arch {
struct page *time_page;
bool nmi_pending;
+
+ int switch_db_regs;
+ unsigned long host_db[KVM_NR_DB_REGS];
+ unsigned long host_dr6;
+ unsigned long host_dr7;
+ unsigned long db[KVM_NR_DB_REGS];
+ unsigned long dr6;
+ unsigned long dr7;
+ unsigned long eff_db[KVM_NR_DB_REGS];
};
struct kvm_mem_alias {
^ permalink raw reply [flat|nested] 21+ messages in thread* [PATCH 11/11] KVM-x86: Wire up host-managed debug registers
2008-05-26 22:05 [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Jan Kiszka
` (9 preceding siblings ...)
2008-05-26 22:10 ` [PATCH 10/11] KVM-x86: Properly virtualize debug registers Jan Kiszka
@ 2008-05-26 22:10 ` Jan Kiszka
2008-05-27 9:50 ` [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Avi Kivity
11 siblings, 0 replies; 21+ messages in thread
From: Jan Kiszka @ 2008-05-26 22:10 UTC (permalink / raw)
To: kvm-devel; +Cc: Avi Kivity, Hollis Blanchard, Jerone Young, Joerg Roedel
Add the remaining bits to make use of debug registers also for guest
debugging, thus enabling the use of hardware breakpoints and
watchpoints.
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
arch/x86/kvm/svm.c | 5 +++++
arch/x86/kvm/vmx.c | 5 +++++
arch/x86/kvm/x86.c | 14 +++++++++++++-
3 files changed, 23 insertions(+), 1 deletion(-)
Index: b/arch/x86/kvm/svm.c
===================================================================
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -889,6 +889,11 @@ static int svm_guest_debug(struct kvm_vc
} else
vcpu->guest_debug = 0;
+ if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
+ svm->vmcb->save.dr7 = dbg->arch.debugreg[7];
+ else
+ svm->vmcb->save.dr7 = vcpu->arch.dr7;
+
if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
svm->vmcb->save.rflags |= X86_EFLAGS_TF | X86_EFLAGS_RF;
else if (old_debug & KVM_GUESTDBG_SINGLESTEP)
Index: b/arch/x86/kvm/vmx.c
===================================================================
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -964,6 +964,11 @@ static int set_guest_debug(struct kvm_vc
if (!(vcpu->guest_debug & KVM_GUESTDBG_ENABLE))
vcpu->guest_debug = 0;
+ if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
+ vmcs_writel(GUEST_DR7, dbg->arch.debugreg[7]);
+ else
+ vmcs_writel(GUEST_DR7, vcpu->arch.dr7);
+
flags = vmcs_readl(GUEST_RFLAGS);
if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
flags |= X86_EFLAGS_TF | X86_EFLAGS_RF;
Index: b/arch/x86/kvm/x86.c
===================================================================
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3612,10 +3612,22 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct
int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
struct kvm_guest_debug *dbg)
{
- int r;
+ int i, r;
vcpu_load(vcpu);
+ if ((dbg->control & (KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP)) ==
+ (KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP)) {
+ for (i = 0; i < KVM_NR_DB_REGS; ++i)
+ vcpu->arch.eff_db[i] = dbg->arch.debugreg[i];
+ vcpu->arch.switch_db_regs =
+ (dbg->arch.debugreg[7] & DR7_BP_EN_MASK);
+ } else {
+ for (i = 0; i < KVM_NR_DB_REGS; i++)
+ vcpu->arch.eff_db[i] = vcpu->arch.db[i];
+ vcpu->arch.switch_db_regs = (vcpu->arch.dr7 & DR7_BP_EN_MASK);
+ }
+
r = kvm_x86_ops->set_guest_debug(vcpu, dbg);
if (dbg->control & KVM_GUESTDBG_INJECT_DB)
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2
2008-05-26 22:05 [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Jan Kiszka
` (10 preceding siblings ...)
2008-05-26 22:10 ` [PATCH 11/11] KVM-x86: Wire up host-managed " Jan Kiszka
@ 2008-05-27 9:50 ` Avi Kivity
2008-05-27 10:44 ` Jan Kiszka
2008-05-27 18:46 ` Hollis Blanchard
11 siblings, 2 replies; 21+ messages in thread
From: Avi Kivity @ 2008-05-27 9:50 UTC (permalink / raw)
To: Jan Kiszka; +Cc: kvm-devel, Hollis Blanchard, Jerone Young, Joerg Roedel
Jan Kiszka wrote:
> Hi,
>
> here comes the full series of patches to fix and enhance debugging
> support of KVM. After completing hardware breakpoint support and
> reorganizing the patches, I felt like I should post them all in a single
> series, because my queue became quite long and potentially confusing in
> the meantime.
>
> The major changes or this revision are full hardware-assisted guest
> debugging and a lot of refactoring on the userspace side. The latter was
> motivated by an increasing inconsistency regarding which arch-specific
> part should be handled in libkvm and which by qemu. I decided to push
> everything into qemu, keeping the lib small (and easier to overcome one
> day :->).
>
> When I was trying to test the hardware breakpoint/watchpoint support
> with gdb, the first result with quite disappointing: current gdb is
> unable to handle such breakpoint remotely. There is hope, latest cvs has
> it fixed - but suffers from other regressions which make it unusable for
> kernel debugging. In the meantime, you can still write your own
> "maintenance packet Z2,..." requests. :p
>
> To give an overview of the series:
> Patch 1..3 - Critical debugger fixes for KVM (should be merge in any
> case)
>
Applied 2, 3 (and my version of 1).
> Patch 4..5 - QEMU refactoring, required for following KVM patches, but
> also of generic use for QEMU (rebase and feature
> enhancements are required to get them in shape for
> upstream)
>
These need to go into upstream first, otherwise we're just moving one
patch queue into another.
> Patch 6..8 - New guest debugging interface and SMP support
> Patch 9..11 - Kernel-side changes for new guest debugging interface and
> proper x86 debug register virtualization
>
I'll do a proper review of these, but from a cursory look, all is well.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2
2008-05-27 9:50 ` [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Avi Kivity
@ 2008-05-27 10:44 ` Jan Kiszka
2008-05-27 18:46 ` Hollis Blanchard
1 sibling, 0 replies; 21+ messages in thread
From: Jan Kiszka @ 2008-05-27 10:44 UTC (permalink / raw)
To: Avi Kivity
Cc: Jan Kiszka, kvm-devel, Hollis Blanchard, Jerone Young,
Joerg Roedel
Avi Kivity wrote:
> Jan Kiszka wrote:
>> Patch 4..5 - QEMU refactoring, required for following KVM patches, but
>> also of generic use for QEMU (rebase and feature
>> enhancements are required to get them in shape for
>> upstream)
>>
>
> These need to go into upstream first, otherwise we're just moving one
> patch queue into another.
I was afraid that you'll say this. This is definitely on my to-do list,
but will likely take some time to finish - and to get accepted upstream
(QEMU's feedback/merge round trip time is always hard to estimate :-/).
OK, let's see how far we get. Maybe it'll take an intermediate step for
KVM, ie. userspace patches with limited functionality, but without
dependencies on generic QEMU changes.
Jan
--
Siemens AG, Corporate Technology, CT SE 2
Corporate Competence Center Embedded Linux
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2
2008-05-27 9:50 ` [PATCH 0/11] Rework guest debug interface / x86 debug register support -v2 Avi Kivity
2008-05-27 10:44 ` Jan Kiszka
@ 2008-05-27 18:46 ` Hollis Blanchard
1 sibling, 0 replies; 21+ messages in thread
From: Hollis Blanchard @ 2008-05-27 18:46 UTC (permalink / raw)
To: Jan Kiszka; +Cc: Avi Kivity, kvm-devel, Jerone Young, Joerg Roedel
On Tuesday 27 May 2008 04:50:20 Avi Kivity wrote:
> > Patch 6..8 - New guest debugging interface and SMP support
> > Patch 9..11 - Kernel-side changes for new guest debugging interface and
> > proper x86 debug register virtualization
> >
>
> I'll do a proper review of these, but from a cursory look, all is well.
I agree; these look good, and good interface layering. We'll be able to
provide a PPC implementation for the arch-specific hooks at some point.
--
Hollis Blanchard
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 21+ messages in thread