[PATCH] kvm tools: Add NMI ability to 'kvm debug'

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This allows triggering NMI on guests using 'kvm debug -m [cpu]'.

Please note that the default behaviour of 'kvm debug' dumping guest's cpu
state has been modified to require a '-d'/--dump.

Signed-off-by: Sasha Levin <levinsasha928@xxxxxxxxx>
---
 tools/kvm/builtin-debug.c                |   22 +++++++++++----
 tools/kvm/builtin-run.c                  |   16 +++++++++++-
 tools/kvm/include/kvm/builtin-debug.h    |   11 ++++++++
 tools/kvm/include/kvm/kvm-cpu.h          |    1 +
 tools/kvm/kvm-cpu.c                      |    5 +++
 tools/kvm/x86/include/kvm/kvm-cpu-arch.h |    1 +
 tools/kvm/x86/kvm-cpu.c                  |   41 ++++++++++++++++++++++++++++++
 7 files changed, 90 insertions(+), 7 deletions(-)

diff --git a/tools/kvm/builtin-debug.c b/tools/kvm/builtin-debug.c
index 045dc2c..eee26c3 100644
--- a/tools/kvm/builtin-debug.c
+++ b/tools/kvm/builtin-debug.c
@@ -14,13 +14,10 @@
 
 static bool all;
 static int instance;
+static int nmi = -1;
+static bool dump;
 static const char *instance_name;
 
-struct debug_cmd {
-	u32 type;
-	u32 len;
-};
-
 static const char * const debug_usage[] = {
 	"kvm debug [--all] [-n name]",
 	NULL
@@ -30,6 +27,8 @@ static const struct option debug_options[] = {
 	OPT_GROUP("General options:"),
 	OPT_BOOLEAN('a', "all", &all, "Debug all instances"),
 	OPT_STRING('n', "name", &instance_name, "name", "Instance name"),
+	OPT_BOOLEAN('d', "dump", &dump, "Generate a debug dump from guest"),
+	OPT_INTEGER('m', "nmi", &nmi, "Generate NMI on VCPU"),
 	OPT_END()
 };
 
@@ -51,13 +50,24 @@ void kvm_debug_help(void)
 static int do_debug(const char *name, int sock)
 {
 	char buff[BUFFER_SIZE];
-	struct debug_cmd cmd = {KVM_IPC_DEBUG, 0};
+	struct debug_cmd cmd = {KVM_IPC_DEBUG, 2 * sizeof(u32)};
 	int r;
 
+	if (dump)
+		cmd.dbg_type |= KVM_DEBUG_CMD_TYPE_DUMP;
+
+	if (nmi != -1) {
+		cmd.dbg_type |= KVM_DEBUG_CMD_TYPE_NMI;
+		cmd.cpu = nmi;
+	}
+
 	r = xwrite(sock, &cmd, sizeof(cmd));
 	if (r < 0)
 		return r;
 
+	if (!dump)
+		return 0;
+
 	do {
 		r = xread(sock, buff, BUFFER_SIZE);
 		if (r < 0)
diff --git a/tools/kvm/builtin-run.c b/tools/kvm/builtin-run.c
index 7969901..7709edb 100644
--- a/tools/kvm/builtin-run.c
+++ b/tools/kvm/builtin-run.c
@@ -31,6 +31,7 @@
 #include "kvm/guest_compat.h"
 #include "kvm/pci-shmem.h"
 #include "kvm/kvm-ipc.h"
+#include "kvm/builtin-debug.h"
 
 #include <linux/types.h>
 
@@ -464,7 +465,7 @@ static void handle_sigusr1(int sig)
 	struct kvm_cpu *cpu = current_kvm_cpu;
 	int fd = kvm_cpu__get_debug_fd();
 
-	if (!cpu)
+	if (!cpu || cpu->needs_nmi)
 		return;
 
 	dprintf(fd, "\n #\n # vCPU #%ld's dump:\n #\n", cpu->cpu_id);
@@ -495,6 +496,19 @@ static void handle_pause(int fd, u32 type, u32 len, u8 *msg)
 static void handle_debug(int fd, u32 type, u32 len, u8 *msg)
 {
 	int i;
+	u32 dbg_type = *(u32 *)msg;
+	int vcpu = *(((u32 *)msg) + 1);
+
+	if (dbg_type & KVM_DEBUG_CMD_TYPE_NMI) {
+		if (vcpu >= kvm->nrcpus)
+			return;
+
+		kvm_cpus[vcpu]->needs_nmi = 1;
+		pthread_kill(kvm_cpus[vcpu]->thread, SIGUSR1);
+	}
+
+	if (!(dbg_type & KVM_DEBUG_CMD_TYPE_DUMP))
+		return;
 
 	for (i = 0; i < nrcpus; i++) {
 		struct kvm_cpu *cpu = kvm_cpus[i];
diff --git a/tools/kvm/include/kvm/builtin-debug.h b/tools/kvm/include/kvm/builtin-debug.h
index 3fc2469..b24b501 100644
--- a/tools/kvm/include/kvm/builtin-debug.h
+++ b/tools/kvm/include/kvm/builtin-debug.h
@@ -1,6 +1,17 @@
 #ifndef KVM__DEBUG_H
 #define KVM__DEBUG_H
 
+#include <linux/types.h>
+
+struct debug_cmd {
+	u32 type;
+	u32 len;
+	u32 dbg_type;
+#define KVM_DEBUG_CMD_TYPE_DUMP	(1 << 0)
+#define KVM_DEBUG_CMD_TYPE_NMI	(1 << 1)
+	u32 cpu;
+};
+
 int kvm_cmd_debug(int argc, const char **argv, const char *prefix);
 void kvm_debug_help(void);
 
diff --git a/tools/kvm/include/kvm/kvm-cpu.h b/tools/kvm/include/kvm/kvm-cpu.h
index 15618f1..d4448f6 100644
--- a/tools/kvm/include/kvm/kvm-cpu.h
+++ b/tools/kvm/include/kvm/kvm-cpu.h
@@ -19,5 +19,6 @@ void kvm_cpu__set_debug_fd(int fd);
 void kvm_cpu__show_code(struct kvm_cpu *vcpu);
 void kvm_cpu__show_registers(struct kvm_cpu *vcpu);
 void kvm_cpu__show_page_tables(struct kvm_cpu *vcpu);
+void kvm_cpu__arch_nmi(struct kvm_cpu *cpu);
 
 #endif /* KVM__KVM_CPU_H */
diff --git a/tools/kvm/kvm-cpu.c b/tools/kvm/kvm-cpu.c
index 884a89f..8ec4efa 100644
--- a/tools/kvm/kvm-cpu.c
+++ b/tools/kvm/kvm-cpu.c
@@ -94,6 +94,11 @@ int kvm_cpu__start(struct kvm_cpu *cpu)
 			cpu->paused = 0;
 		}
 
+		if (cpu->needs_nmi) {
+			kvm_cpu__arch_nmi(cpu);
+			cpu->needs_nmi = 0;
+		}
+
 		kvm_cpu__run(cpu);
 
 		switch (cpu->kvm_run->exit_reason) {
diff --git a/tools/kvm/x86/include/kvm/kvm-cpu-arch.h b/tools/kvm/x86/include/kvm/kvm-cpu-arch.h
index ed1c727..822d966 100644
--- a/tools/kvm/x86/include/kvm/kvm-cpu-arch.h
+++ b/tools/kvm/x86/include/kvm/kvm-cpu-arch.h
@@ -26,6 +26,7 @@ struct kvm_cpu {
 
 	u8			is_running;
 	u8			paused;
+	u8			needs_nmi;
 
 	struct kvm_coalesced_mmio_ring	*ring;
 };
diff --git a/tools/kvm/x86/kvm-cpu.c b/tools/kvm/x86/kvm-cpu.c
index a0d10cc..18b4906 100644
--- a/tools/kvm/x86/kvm-cpu.c
+++ b/tools/kvm/x86/kvm-cpu.c
@@ -5,6 +5,7 @@
 #include "kvm/kvm.h"
 
 #include <asm/msr-index.h>
+#include <asm/apicdef.h>
 
 #include <sys/ioctl.h>
 #include <sys/mman.h>
@@ -76,6 +77,26 @@ void kvm_cpu__delete(struct kvm_cpu *vcpu)
 	free(vcpu);
 }
 
+static int kvm_cpu__set_lint(struct kvm_cpu *vcpu)
+{
+	struct kvm_lapic_state klapic;
+	struct local_apic *lapic = (void *)&klapic;
+	u32 lvt;
+
+	if (ioctl(vcpu->vcpu_fd, KVM_GET_LAPIC, &klapic))
+		return -1;
+
+	lvt = *(u32 *)&lapic->lvt_lint0;
+	lvt = SET_APIC_DELIVERY_MODE(lvt, APIC_MODE_EXTINT);
+	*(u32 *)&lapic->lvt_lint0 = lvt;
+
+	lvt = *(u32 *)&lapic->lvt_lint0;
+	lvt = SET_APIC_DELIVERY_MODE(lvt, APIC_MODE_NMI);
+	*(u32 *)&lapic->lvt_lint0 = lvt;
+
+	return ioctl(vcpu->vcpu_fd, KVM_SET_LAPIC, &klapic);
+}
+
 struct kvm_cpu *kvm_cpu__init(struct kvm *kvm, unsigned long cpu_id)
 {
 	struct kvm_cpu *vcpu;
@@ -104,6 +125,9 @@ struct kvm_cpu *kvm_cpu__init(struct kvm *kvm, unsigned long cpu_id)
 	if (coalesced_offset)
 		vcpu->ring = (void *)vcpu->kvm_run + (coalesced_offset * PAGE_SIZE);
 
+	if (kvm_cpu__set_lint(vcpu))
+		die_perror("KVM_SET_LAPIC failed");
+
 	vcpu->is_running = true;
 
 	return vcpu;
@@ -386,3 +410,20 @@ void kvm_cpu__show_page_tables(struct kvm_cpu *vcpu)
 			"llx   pte1: %016llx\n",
 			*pte4, *pte3, *pte2, *pte1);
 }
+
+void kvm_cpu__arch_nmi(struct kvm_cpu *cpu)
+{
+	struct kvm_lapic_state klapic;
+	struct local_apic *lapic = (void *)&klapic;
+
+	if (ioctl(cpu->vcpu_fd, KVM_GET_LAPIC, &klapic) != 0)
+		return;
+
+	if (lapic->lvt_lint1.mask)
+		return;
+
+	if (lapic->lvt_lint1.delivery_mode != APIC_MODE_NMI)
+		return;
+
+	ioctl(cpu->vcpu_fd, KVM_NMI);
+}
-- 
1.7.8

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux