diff mbox

x86: apic: add apic timer mode transition test

Message ID 1506421253-21300-1-git-send-email-wanpeng.li@hotmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Wanpeng Li Sept. 26, 2017, 10:20 a.m. UTC
From: Wanpeng Li <wanpeng.li@hotmail.com>

Add apic timer mode transition test.

Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Signed-off-by: Wanpeng Li <wanpeng.li@hotmail.com>
---
 lib/x86/apic-defs.h |  1 +
 x86/apic.c          | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 79 insertions(+)
diff mbox

Patch

diff --git a/lib/x86/apic-defs.h b/lib/x86/apic-defs.h
index e0c3cca..a7bc6a0 100644
--- a/lib/x86/apic-defs.h
+++ b/lib/x86/apic-defs.h
@@ -91,6 +91,7 @@ 
 #define		APIC_TIMER_BASE_CLKIN		0x0
 #define		APIC_TIMER_BASE_TMBASE		0x1
 #define		APIC_TIMER_BASE_DIV		0x2
+#define		APIC_LVT_TIMER_MASK      	(3 << 17)
 #define		APIC_LVT_TIMER_ONESHOT		(0 << 17)
 #define		APIC_LVT_TIMER_PERIODIC		(1 << 17)
 #define		APIC_LVT_TIMER_TSCDEADLINE	(2 << 17)
diff --git a/x86/apic.c b/x86/apic.c
index eb78579..6cfb52d 100644
--- a/x86/apic.c
+++ b/x86/apic.c
@@ -458,6 +458,83 @@  static void test_physical_broadcast(void)
 	report("APIC physical broadcast shorthand", broadcast_received(ncpus));
 }
 
+void wait_until_tmcct_is_zero(uint32_t initial_count, bool stop_when_half)
+{
+	uint32_t tmcct = apic_read(APIC_TMCCT);
+
+	if (tmcct) {
+		while (tmcct > (initial_count / 2))
+			tmcct = apic_read(APIC_TMCCT);
+
+		if ( stop_when_half )
+			return;
+
+		/* Wait until the counter reach 0 or wrap-around */
+		while ( tmcct <= (initial_count / 2) && tmcct > 0 )
+			tmcct = apic_read(APIC_TMCCT);
+	}
+}
+
+static inline void apic_change_mode(unsigned long new_mode)
+{
+	uint32_t lvtt;
+
+	lvtt = apic_read(APIC_LVTT);
+	apic_write(APIC_LVTT, (lvtt & ~APIC_LVT_TIMER_MASK) | new_mode);
+}
+
+static void test_apic_change_mode()
+{
+	uint32_t tmict = 0x999999;
+
+	printf("starting apic change mode\n");
+
+	apic_write(APIC_TMICT, tmict);
+
+	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
+
+	report("TMICT value reset", apic_read(APIC_TMICT) == tmict);
+
+	/* Testing one-shot */
+	apic_change_mode(APIC_LVT_TIMER_ONESHOT);
+	apic_write(APIC_TMICT, tmict);
+	report("TMCCT should have a non-zero value", apic_read(APIC_TMCCT));
+
+	wait_until_tmcct_is_zero(tmict, false);
+	report("TMCCT should have reached 0", !apic_read(APIC_TMCCT));
+
+	/*
+	 * Write TMICT before changing mode from one-shot to periodic TMCCT should
+	 * be reset to TMICT periodicly
+	 */
+	apic_write(APIC_TMICT, tmict);
+	wait_until_tmcct_is_zero(tmict, true);
+	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
+	report("TMCCT should have a non-zero value", apic_read(APIC_TMCCT));
+
+	/*
+	 * After the change of mode, the counter should not be reset and continue
+	 * counting down from where it was
+	 */
+	report("TMCCT should not be reset to TMICT value", apic_read(APIC_TMCCT) < (tmict / 2));
+	wait_until_tmcct_is_zero(tmict, false);
+	report("TMCCT should be reset to the initial-count", apic_read(APIC_TMCCT) > (tmict / 2));
+
+	wait_until_tmcct_is_zero(tmict, true);
+	/*
+	 * Keep the same TMICT and change timer mode to one-shot
+	 * TMCCT should be > 0 and count-down to 0
+	 */
+	apic_change_mode(APIC_LVT_TIMER_ONESHOT);
+	report("TMCCT should not be reset to init", apic_read(APIC_TMCCT) < (tmict / 2));
+	wait_until_tmcct_is_zero(tmict, false);
+	report("TMCCT should have reach zero", !apic_read(APIC_TMCCT));
+
+	/* now tmcct == 0 and tmict != 0 */
+	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
+	report("TMCCT should stay at zero", !apic_read(APIC_TMCCT));
+}
+
 int main()
 {
     setup_vm();
@@ -478,6 +555,7 @@  int main()
     test_multiple_nmi();
 
     test_apic_timer_one_shot();
+    test_apic_change_mode();
     test_tsc_deadline_timer();
 
     return report_summary();