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

[Xen-devel] [PATCH XTF v4] add HPET functional test



In order to test HPET level trigger interrupts.

Note that the test doesn't check that the interrupt is injected
correctly, only that the status bits are properly set an acknowledged
when using HPET with level triggered interrupts.

Signed-off-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
---
Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
---
 arch/x86/hpet.c              |  3 ++
 arch/x86/include/arch/hpet.h | 55 +++++++++++++++++++++
 docs/all-tests.dox           |  2 +
 include/xtf/libc.h           |  3 ++
 tests/hpet/Makefile          |  9 ++++
 tests/hpet/main.c            | 96 ++++++++++++++++++++++++++++++++++++
 6 files changed, 168 insertions(+)
 create mode 100644 tests/hpet/Makefile
 create mode 100644 tests/hpet/main.c

diff --git a/arch/x86/hpet.c b/arch/x86/hpet.c
index 8c18dac..f45927e 100644
--- a/arch/x86/hpet.c
+++ b/arch/x86/hpet.c
@@ -68,6 +68,9 @@ void hpet_init_timer(unsigned int nr, unsigned int irq, 
uint64_t ticks,
     hpet_write64(HPET_CFG, cfg & ~HPET_CFG_ENABLE);
     hpet_write64(HPET_COUNTER, 0);
 
+    /* Clear any pending status bit. */
+    hpet_timer_status_clear(nr);
+
     /* Configure timer and setup comparator. */
     hpet_write32(HPET_Tn_CFG(nr), tm.raw);
     hpet_write64(HPET_Tn_CMP(nr), ticks);
diff --git a/arch/x86/include/arch/hpet.h b/arch/x86/include/arch/hpet.h
index ea882ef..8264b0e 100644
--- a/arch/x86/include/arch/hpet.h
+++ b/arch/x86/include/arch/hpet.h
@@ -20,9 +20,12 @@
 #define HPET_CFG                0x010
 #define   HPET_CFG_ENABLE       0x001
 
+#define HPET_STATUS             0x020
+
 #define HPET_COUNTER            0x0f0
 
 #define HPET_Tn_CFG(n)         (0x100 + (n) * 0x20)
+#define   HPET_TN_PERIODIC_CAP  0x010
 
 #define HPET_Tn_CMP(n)         (0x108 + (n) * 0x20)
 
@@ -76,6 +79,58 @@ static inline uint64_t hpet_read_counter(void)
     }
 }
 
+/**
+ * Fetch the HPET comparator register of a timer.
+ */
+static inline uint64_t hpet_read_cmp(unsigned int timer)
+{
+    if ( IS_DEFINED(CONFIG_64BIT) )
+        return hpet_read64(HPET_Tn_CMP(timer));
+    else
+    {
+        uint32_t lo, hi;
+
+        do {
+            hi = hpet_read32(HPET_Tn_CMP(timer) + 4);
+            lo = hpet_read32(HPET_Tn_CMP(timer));
+        } while ( hi != hpet_read32(HPET_Tn_CMP(timer) + 4) );
+
+        return ((uint64_t)hi << 32) | lo;
+    }
+}
+
+/**
+ * Fetch the bitmap of supported IRQs of a timer.
+ */
+static inline uint32_t hpet_timer_irqs(unsigned int timer)
+{
+    return hpet_read32(HPET_Tn_CFG(timer) + 4);
+}
+
+/**
+ * Check if a timer supports periodic mode.
+ */
+static inline bool hpet_timer_periodic(unsigned int timer)
+{
+    return hpet_read32(HPET_Tn_CFG(timer)) & HPET_TN_PERIODIC_CAP;
+}
+
+/**
+ * Check the status bit of a timer (only valid for level mode).
+ */
+static inline bool hpet_timer_status(unsigned int timer)
+{
+    return (hpet_read32(HPET_STATUS) >> timer) & 1;
+}
+
+/**
+ * Clear the status bit of a timer.
+ */
+static inline void hpet_timer_status_clear(unsigned int timer)
+{
+    hpet_write32(HPET_STATUS, 1u << timer);
+}
+
 /**
  * Setup and enable a specific HPET timer.
  */
diff --git a/docs/all-tests.dox b/docs/all-tests.dox
index f8a495a..624c365 100644
--- a/docs/all-tests.dox
+++ b/docs/all-tests.dox
@@ -20,6 +20,8 @@ and functionality.
 
 @subpage test-fpu-exception-emulation - FPU Exception Emulation.  Covers 
XSA-190.
 
+@subpage test-hpet - HPET functional test.
+
 @subpage test-invlpg - `invlpg` instruction behaviour.
 
 @subpage test-lbr-tsx-vmentry - Haswell and later LBR/TSX Vmentry failure test.
diff --git a/include/xtf/libc.h b/include/xtf/libc.h
index 66f834b..55328f4 100644
--- a/include/xtf/libc.h
+++ b/include/xtf/libc.h
@@ -35,6 +35,9 @@ void *memcpy(void *dst, const void *src, size_t n);
 int memcmp(const void *s1, const void *s2, size_t n);
 #define memcmp(s1, s2, n)           __builtin_memcmp(s1, s2, n)
 
+int ffs(int v);
+#define ffs(v)                      __builtin_ffs(v)
+
 size_t strnlen(const char *str, size_t max);
 
 int __printf(3, 0)
diff --git a/tests/hpet/Makefile b/tests/hpet/Makefile
new file mode 100644
index 0000000..4e71587
--- /dev/null
+++ b/tests/hpet/Makefile
@@ -0,0 +1,9 @@
+include $(ROOT)/build/common.mk
+
+NAME      := hpet
+CATEGORY  := functional
+TEST-ENVS := hvm64
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
diff --git a/tests/hpet/main.c b/tests/hpet/main.c
new file mode 100644
index 0000000..197b3db
--- /dev/null
+++ b/tests/hpet/main.c
@@ -0,0 +1,96 @@
+/**
+ * @file tests/hpet/main.c
+ * @ref test-hpet - HPET functional test
+ *
+ * @page test-hpet HPET functional test
+ *
+ * HPET functional test.
+ *
+ * Only one timer is tested. No interrupt delivery tests.
+ *
+ * @see tests/hpet/main.c
+ */
+#include <xtf.h>
+
+#include <arch/hpet.h>
+
+const char test_title[] = "Test HPET";
+
+static void wait_cmp(unsigned int timer)
+{
+    uint64_t cmp = hpet_read_cmp(timer);
+
+    while ( hpet_read_counter() < cmp );
+}
+
+void test_main(void)
+{
+    unsigned int irq, timer;
+    uint64_t counter;
+
+    if ( hpet_init() || !hpet_nr_timers )
+        return xtf_skip("Error: cannot find working HPET\n");
+
+    /* Pick a timer that supports periodic mode. */
+    for ( timer = 0; timer < hpet_nr_timers; timer++ )
+        if ( hpet_timer_periodic(timer) )
+            break;
+
+    if ( timer == hpet_nr_timers )
+        return xtf_skip("Error: cannot find timer with periodic mode\n");
+
+    /* Select a valid IRQ. */
+    irq = ffs(hpet_timer_irqs(timer));
+    if ( !irq )
+        return xtf_skip("Error: cannot find a valid IRQ\n");
+    irq--;
+
+    printk("Routing timer %u to IRQ %u\n", timer, irq);
+
+    /* Test oneshot mode. */
+    hpet_init_timer(timer, irq, 1, true, false, false);
+    wait_cmp(timer);
+    if ( !hpet_timer_status(timer) )
+        return xtf_failure(
+"Fail: status bit unset for level interrupt in oneshot mode\n");
+
+    /* Try periodic mode. */
+    hpet_init_timer(timer, irq, 1, true, true, false);
+    wait_cmp(timer);
+    if ( !hpet_timer_status(timer) )
+        return xtf_failure(
+"Fail: status bit unset for level interrupt in periodic mode\n");
+
+    /*
+     * The comparator register should continue to be updated despite the status
+     * not being cleared.
+     */
+    counter = hpet_read_counter();
+    if ( hpet_read_cmp(timer) < counter )
+        return xtf_failure("Fail: comparator not updated in periodic mode\n");
+
+    /* Clear the status bit and wait for it to be set again. */
+    hpet_timer_status_clear(timer);
+    wait_cmp(timer);
+    if ( !hpet_timer_status(timer) )
+        return xtf_failure(
+"Fail: status bit unset for repeated level interrupt in periodic mode\n");
+
+    /* Switch to edge mode and check status bit it's not set again. */
+    hpet_init_timer(timer, irq, 1, false, false, false);
+    wait_cmp(timer);
+    if ( hpet_timer_status(timer) )
+        return xtf_failure("Fail: status bit set for edge interrupt\n");
+
+    xtf_success(NULL);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
-- 
2.17.1


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.