[PATCH kvm-unit-tests] debug: add testcase for singlestepping over STI;HLT

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

 



Test that HLT sets RIP correctly when returning from singlestep.
QEMU's emulation is currently not injecting a #DB exception
for single-step at all after an HLT instruction.  Also, after
single-step is injected EFLAGS.IF might very well be zero,
meaning that the CPU would not have to leave HLT.  Check
that the emulation is not confused, i.e. that it remembers
that it has _already_ left HLT.

Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx>
---
 x86/debug.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 60 insertions(+), 2 deletions(-)

diff --git a/x86/debug.c b/x86/debug.c
index 65784c5a..f493567c 100644
--- a/x86/debug.c
+++ b/x86/debug.c
@@ -85,6 +85,7 @@ typedef unsigned long (*db_test_fn)(void);
 typedef void (*db_report_fn)(unsigned long, const char *);
 
 static unsigned long singlestep_with_movss_blocking_and_dr7_gd(void);
+static unsigned long singlestep_with_sti_hlt(void);
 
 static void __run_single_step_db_test(db_test_fn test, db_report_fn report_fn)
 {
@@ -97,8 +98,12 @@ static void __run_single_step_db_test(db_test_fn test, db_report_fn report_fn)
 	start = test();
 	report_fn(start, "");
 
-	/* MOV DR #GPs at CPL>0, don't try to run the DR7.GD test in usermode. */
-	if (test == singlestep_with_movss_blocking_and_dr7_gd)
+	/*
+	 * MOV DR #GPs at CPL>0, don't try to run the DR7.GD test in usermode.
+	 * Likewise for HLT.
+	 */
+	if (test == singlestep_with_movss_blocking_and_dr7_gd
+	    || test == singlestep_with_sti_hlt)
 		return;
 
 	n = 0;
@@ -352,6 +357,58 @@ static noinline unsigned long singlestep_with_movss_blocking_and_dr7_gd(void)
 	return start_rip;
 }
 
+static void report_singlestep_with_sti_hlt(unsigned long start,
+						const char *usermode)
+{
+	report(n == 5 &&
+	       is_single_step_db(dr6[0]) && db_addr[0] == start &&
+	       is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
+	       is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 6 &&
+	       is_single_step_db(dr6[3]) && db_addr[3] == start + 1 + 6 + 1 &&
+	       is_single_step_db(dr6[4]) && db_addr[4] == start + 1 + 6 + 1 + 1,
+	       "%sSingle-step #DB w/ STI;HLT", usermode);
+}
+
+#define APIC_LVT_TIMER_VECTOR    (0xee)
+
+static void lvtt_handler(isr_regs_t *regs)
+{
+        eoi();
+}
+
+static noinline unsigned long singlestep_with_sti_hlt(void)
+{
+	unsigned long start_rip;
+
+	cli();
+
+	handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler);
+	apic_write(APIC_LVTT, APIC_LVT_TIMER_ONESHOT |
+		   APIC_LVT_TIMER_VECTOR);
+	apic_write(APIC_TDCR, 0x0000000b);
+	apic_write(APIC_TMICT, 1000000);
+
+	/*
+	 * STI blocking doesn't suppress #DBs, thus the first single-step #DB
+	 * should arrive after the standard one instruction delay.
+	 */
+	asm volatile(
+		"pushf\n\t"
+		"pop %%rax\n\t"
+		"or $(1<<8),%%rax\n\t"
+		"push %%rax\n\t"
+		"popf\n\t"
+		"sti\n\t"
+		"1:hlt;\n\t"
+		"and $~(1<<8),%%rax\n\t"
+		"push %%rax\n\t"
+		"popf\n\t"
+		"lea 1b(%%rip),%0\n\t"
+		: "=r" (start_rip) : : "rax"
+	);
+	return start_rip;
+}
+
 int main(int ac, char **av)
 {
 	unsigned long cr4;
@@ -416,6 +473,7 @@ int main(int ac, char **av)
 	run_ss_db_test(singlestep_with_movss_blocking);
 	run_ss_db_test(singlestep_with_movss_blocking_and_icebp);
 	run_ss_db_test(singlestep_with_movss_blocking_and_dr7_gd);
+	run_ss_db_test(singlestep_with_sti_hlt);
 
 	n = 0;
 	write_dr1((void *)&value);
-- 
2.45.1





[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