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

[Xen-devel] [PATCH RFC 3/3] xtf: add minimal HPET functionality test



Add a basic HPET functionality test, note that this test requires the
HPET to support level triggered interrupts.

Further improvements should add support for interrupt delivery, and
testing all the available timers.

Signed-off-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
---
Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
---
 arch/x86/include/arch/lib.h |  14 ++++
 docs/all-tests.dox          |   2 +
 tests/hpet/Makefile         |   9 +++
 tests/hpet/main.c           | 187 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 212 insertions(+)
 create mode 100644 tests/hpet/Makefile
 create mode 100644 tests/hpet/main.c

diff --git a/arch/x86/include/arch/lib.h b/arch/x86/include/arch/lib.h
index 6714bdc..3400890 100644
--- a/arch/x86/include/arch/lib.h
+++ b/arch/x86/include/arch/lib.h
@@ -392,6 +392,20 @@ static inline void write_xcr0(uint64_t xcr0)
     xsetbv(0, xcr0);
 }
 
+static inline uint64_t rdtsc(void)
+{
+    uint32_t low, high;
+
+    asm volatile ("rdtsc" : "=a" (low), "=d" (high));
+
+    return ((uint64_t)high << 32) | low;
+}
+
+static inline void pause(void)
+{
+    asm volatile ("pause");
+}
+
 #endif /* XTF_X86_LIB_H */
 
 /*
diff --git a/docs/all-tests.dox b/docs/all-tests.dox
index 355cb80..122840c 100644
--- a/docs/all-tests.dox
+++ b/docs/all-tests.dox
@@ -127,4 +127,6 @@ guest breakout.
 @subpage test-nested-svm - Nested SVM tests.
 
 @subpage test-nested-vmx - Nested VT-x tests.
+
+@subpage test-hpet - HPET functional test.
 */
diff --git a/tests/hpet/Makefile b/tests/hpet/Makefile
new file mode 100644
index 0000000..934e63c
--- /dev/null
+++ b/tests/hpet/Makefile
@@ -0,0 +1,9 @@
+include $(ROOT)/build/common.mk
+
+NAME      := hpet
+CATEGORY  := utility
+TEST-ENVS := hvm32
+
+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..57be410
--- /dev/null
+++ b/tests/hpet/main.c
@@ -0,0 +1,187 @@
+/**
+ * @file tests/hpet/main.c
+ * @ref test-hpet
+ *
+ * @page test-hpet hpet
+ *
+ * HPET functionality testing.
+ *
+ * Quite limited, currently only Timer N is tested. No interrupt delivery
+ * tests.
+ *
+ * @see tests/hpet/main.c
+ */
+#include <xtf.h>
+
+#define HPET_BASE_ADDRESS       0xfed00000
+
+#define HPET_ID                 0
+#define HPET_ID_NUMBER          0x1f00
+#define HPET_ID_NUMBER_SHIFT    8
+
+#define HPET_PERIOD             0x004
+#define HPET_MAX_PERIOD         0x05f5e100
+
+#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_LEVEL           0x002
+#define HPET_TN_ENABLE          0x004
+#define HPET_TN_PERIODIC        0x008
+#define HPET_TN_32BIT           0x100
+#define HPET_TN_ROUTE_SHIFT     9
+
+#define HPET_Tn_CMP(n)          (0x108 + (n) * 0x20)
+
+/*
+ * NB: should probably be an explicit movl, but clang seems to generate good
+ * code.
+ */
+#define HPET_REG(reg) (*(volatile uint32_t *)(_p(HPET_BASE_ADDRESS) + (reg)))
+
+#define MS_TO_NS                1000000
+/* p is in fs */
+#define MS_TO_TICKS(ms, p)      (((ms) * MS_TO_NS) / ((p) / 1000000))
+
+const char test_title[] = "Test HPET";
+
+static uint32_t freq;
+
+static void set_freq(void)
+{
+    uint32_t eax, ebx, ecx, edx, base;
+    bool found = false;
+
+    /* Get tsc frequency from cpuid. */
+    for ( base = XEN_CPUID_FIRST_LEAF;
+          base < XEN_CPUID_FIRST_LEAF + 0x10000; base += 0x100 )
+    {
+        cpuid(base, &eax, &ebx, &ecx, &edx);
+
+        if ( (ebx == XEN_CPUID_SIGNATURE_EBX) &&
+             (ecx == XEN_CPUID_SIGNATURE_ECX) &&
+             (edx == XEN_CPUID_SIGNATURE_EDX) &&
+             ((eax - base) >= 2) )
+        {
+            found = true;
+            break;
+        }
+    }
+
+    if ( !found )
+        panic("Unable to locate Xen CPUID leaves\n");
+
+    cpuid_count(base + 3, 0, &eax, &ebx, &freq, &edx);
+    printk("TSC frequency %ukHz\n", freq);
+}
+
+/* Busy-wait implementation based on tsc value. */
+static void wait(unsigned int ms)
+{
+    uint64_t end = rdtsc() + (uint64_t)ms * (uint64_t)freq;
+
+    while ( rdtsc() < end )
+        pause();
+}
+
+void test_main(void)
+{
+    uint32_t period, route;
+    unsigned int nr, irq;
+
+    set_freq();
+
+    /* Sanity check main counter tick period. */
+    period = HPET_REG(HPET_PERIOD);
+    if ( period == 0 || period > HPET_MAX_PERIOD )
+    {
+        printk("Invalid period found in main counter tick: %u fs\n", period);
+        return xtf_error("Error: Cannot find valid HPET\n");
+    }
+
+    /* Get number of timers. */
+    nr = (HPET_REG(HPET_ID) & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT;
+    printk("HPET reports %u timers period %u fs\n", nr + 1, period);
+
+    /* Check possible interrupt routing. */
+    route = HPET_REG(HPET_Tn_CFG(nr) + 4);
+    for ( irq = 0; irq < 32; irq++ )
+        if ( test_bit(irq, &route) )
+            break;
+    if ( irq == 32 )
+        return xtf_error("Error: Cannot find valid IRQ routing\n");
+    printk("Routing timer %u to IRQ %u\n", nr, irq);
+
+    /* Test oneshot timer nr. */
+    HPET_REG(HPET_Tn_CFG(nr)) |= (irq << HPET_TN_ROUTE_SHIFT) | HPET_TN_32BIT |
+                                 HPET_TN_LEVEL | HPET_TN_ENABLE;
+
+    /* Reset main counter. */
+    HPET_REG(HPET_COUNTER) = 0;
+    /* Set the comparator to fire in 50ms. */
+    HPET_REG(HPET_Tn_CMP(nr)) = MS_TO_TICKS(50, period);
+
+    /* Enable interrupts. */
+    HPET_REG(HPET_CFG) |= HPET_CFG_ENABLE;
+
+    /* Wait for 100ms. */
+    wait(100);
+    if ( !((HPET_REG(HPET_STATUS) >> nr) & 1) )
+        return xtf_failure("Fail: Status bit unset for level interrupt in 
oneshot mode\n");
+
+    /* Clean status bit. */
+    HPET_REG(HPET_STATUS) = 1 << nr;
+
+    /* Try periodic mode. */
+    HPET_REG(HPET_CFG) &= ~HPET_CFG_ENABLE;
+    /* Reset main counter. */
+    HPET_REG(HPET_COUNTER) = 0;
+    /* Enable periodic interrupts. */
+    HPET_REG(HPET_Tn_CFG(nr)) |= HPET_TN_PERIODIC;
+    /* Set comparator to 100ms period. */
+    HPET_REG(HPET_Tn_CMP(nr)) = MS_TO_TICKS(100, period);
+    HPET_REG(HPET_CFG) |= HPET_CFG_ENABLE;
+
+    /* Wait for 200ms. */
+    wait(200);
+    if ( !((HPET_REG(HPET_STATUS) >> nr) & 1) )
+        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.
+     */
+    wait(300);
+    if ( HPET_REG(HPET_Tn_CMP(nr)) < MS_TO_TICKS(400, period) )
+        return xtf_failure("Fail: Comparator not updated in periodic mode\n");
+
+    /* Clear the status bit and wait for it to be set again. */
+    HPET_REG(HPET_STATUS) = 1 << nr;
+    wait(200);
+    if ( !((HPET_REG(HPET_STATUS) >> nr) & 1) )
+        return xtf_failure("Fail: Status bit unset for level interrupt in 
periodic mode\n");
+
+    /* Switch to edge mode, clear status bit and check it's not set again. */
+    HPET_REG(HPET_Tn_CFG(nr)) &= ~HPET_TN_LEVEL;
+    HPET_REG(HPET_STATUS) = 1 << nr;
+    wait(200);
+    if ( ((HPET_REG(HPET_STATUS) >> nr) & 1) )
+        return xtf_failure("Fail: Status bit set for edge interrupt in 
periodic mode\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.16.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®.