@@ -393,6 +393,41 @@ static void test_apic_timer_one_shot(void)
(tsc2 - tsc1 >= interval));
}
+static void test_apic_timer_periodic(void)
+{
+ uint64_t tsc1, tsc2;
+ static const uint32_t interval = 0x10000;
+
+#define APIC_LVT_TIMER_VECTOR (0xee)
+#define LVTT_PERIODIC_ROUND 100
+
+ handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler);
+ irq_enable();
+
+ /* Periodic mode */
+ apic_write(APIC_LVTT, APIC_LVT_TIMER_PERIODIC |
+ APIC_LVT_TIMER_VECTOR);
+ /* Divider == 1 */
+ apic_write(APIC_TDCR, 0x0000000b);
+
+ tsc1 = rdtsc();
+ /* Set "Initial Counter Register", which starts the timer */
+ apic_write(APIC_TMICT, interval);
+ while (lvtt_counter != LVTT_PERIODIC_ROUND);
+ apic_write(APIC_TMICT, 0);
+ tsc2 = rdtsc();
+
+ /*
+ * For LVT Timer clock, SDM vol 3 10.5.4 says it should be
+ * derived from processor's bus clock (IIUC which is the same
+ * as TSC), however QEMU seems to be using nanosecond. In all
+ * cases, the following should satisfy on all modern
+ * processors.
+ */
+ report("APIC LVT timer periodic", (lvtt_counter ==
+ LVTT_PERIODIC_ROUND) && (tsc2 - tsc1 >= interval));
+}
+
int main()
{
setup_vm();
@@ -412,6 +447,7 @@ int main()
test_multiple_nmi();
test_apic_timer_one_shot();
+ test_apic_timer_periodic();
test_tsc_deadline_timer();
return report_summary();