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

[Xen-devel] [PATCH v2 2/2] 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>
---
Changes since v1:
 - Split HPET code into an HPET specific driver.
 - Use the pv timecounter for busy waiting.
 - Change test category to functional.
 - Reduce timer test period to 1ms.
---
 arch/x86/hpet.c              | 117 +++++++++++++++++++++++++++++++++++++++++++
 arch/x86/include/arch/hpet.h |  69 +++++++++++++++++++++++++
 build/files.mk               |   1 +
 docs/all-tests.dox           |   2 +
 tests/hpet/Makefile          |   9 ++++
 tests/hpet/main.c            | 100 ++++++++++++++++++++++++++++++++++++
 6 files changed, 298 insertions(+)
 create mode 100644 arch/x86/hpet.c
 create mode 100644 arch/x86/include/arch/hpet.h
 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
new file mode 100644
index 0000000..6a84f3f
--- /dev/null
+++ b/arch/x86/hpet.c
@@ -0,0 +1,117 @@
+/**
+ * @file arch/x86/hpet.c
+ *
+ * Basic x86 HPET driver.
+ */
+
+#include <xtf/lib.h>
+
+#include <xen/errno.h>
+
+#include <arch/hpet.h>
+
+static uint32_t period;
+unsigned int hpet_nr_timers;
+
+union hpet_timer {
+    uint32_t raw;
+    struct {
+        uint8_t                 :1;
+        bool level              :1;
+        bool enabled            :1;
+        bool periodic           :1;
+        uint8_t                 :4;
+        bool mode_32bit         :1;
+        uint8_t irq             :5;
+        uint32_t                :18;
+    };
+};
+
+#define MS_TO_NS        1000000
+#define NS_TO_FS        1000000
+
+uint32_t hpet_ms_to_ticks(unsigned int ms)
+{
+    return ((ms) * MS_TO_NS) / ((period) / 1000000);
+}
+
+int hpet_init(void)
+{
+    /* Sanity check main counter tick period. */
+    period = hpet_mmio_read(HPET_PERIOD);
+    if ( period == 0 || period > HPET_MAX_PERIOD )
+        return -ENODEV;
+
+    /* Get number of timers. */
+    hpet_nr_timers = ((hpet_mmio_read(HPET_ID) & HPET_ID_NUMBER) >>
+                      HPET_ID_NUMBER_SHIFT) + 1;
+
+    return 0;
+}
+
+uint32_t hpet_timer_avail_irqs(unsigned int nr)
+{
+    return hpet_mmio_read(HPET_Tn_CFG(nr) + 4);
+}
+
+bool hpet_timer_supports_periodic(unsigned int nr)
+{
+    return hpet_mmio_read(HPET_Tn_CFG(nr)) & HPET_TN_PERIODIC_CAP;
+}
+
+void hpet_init_timer(unsigned int nr, unsigned int irq, unsigned int ms,
+                     bool level, bool periodic)
+{
+    union hpet_timer tm = {
+        .level = level,
+        .enabled = true,
+        .periodic = periodic,
+        .mode_32bit = true,
+        .irq = irq,
+    };
+
+    /*
+     * Disable interrupts and reset main counter.
+     *
+     * Note that this is a testing HPET implementation and ATM we only expect
+     * a single timer to be tested simultaneously.
+     */
+    hpet_mmio_write(HPET_CFG, hpet_mmio_read(HPET_CFG) & ~HPET_CFG_ENABLE);
+    hpet_mmio_write(HPET_COUNTER, 0);
+
+    /* Configure timer and setup comparator. */
+    hpet_mmio_write(HPET_Tn_CFG(nr), tm.raw);
+    hpet_mmio_write(HPET_Tn_CMP(nr), hpet_ms_to_ticks(ms));
+
+    /* Enable main counter. */
+    hpet_mmio_write(HPET_CFG, hpet_mmio_read(HPET_CFG) | HPET_CFG_ENABLE);
+}
+
+void hpet_disable_timer(unsigned int nr)
+{
+    union hpet_timer tm = {
+        .enabled = false,
+    };
+
+    hpet_mmio_write(HPET_Tn_CFG(nr), tm.raw);
+}
+
+bool hpet_intr_pending(unsigned int nr)
+{
+    return (hpet_mmio_read(HPET_STATUS) >> nr) & 1;
+}
+
+void hpet_intr_pending_clear(unsigned int nr)
+{
+    hpet_mmio_write(HPET_STATUS, 1u << nr);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/arch/x86/include/arch/hpet.h b/arch/x86/include/arch/hpet.h
new file mode 100644
index 0000000..b944c88
--- /dev/null
+++ b/arch/x86/include/arch/hpet.h
@@ -0,0 +1,69 @@
+/**
+ * @file arch/x86/include/arch/hpet.h
+ *
+ * %x86 HPET register definitions and utility functions.
+ *
+ * NB: assume the HPET is at it's default address, and for simplicity only
+ * support 32bit accesses.
+ */
+
+#ifndef XTF_X86_HPET_H
+#define XTF_X86_HPET_H
+
+#include <xtf/numbers.h>
+#include <xtf/types.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_PERIODIC_CAP    0x010
+
+#define HPET_Tn_CMP(n)          (0x108 + (n) * 0x20)
+
+static inline uint32_t hpet_mmio_read(unsigned int reg)
+{
+    return *(volatile uint32_t *)(_p(HPET_BASE_ADDRESS) + reg);
+}
+
+static inline void hpet_mmio_write(unsigned int reg, uint32_t val)
+{
+    *(volatile uint32_t *)(_p(HPET_BASE_ADDRESS) + reg) = val;
+}
+
+extern unsigned int hpet_nr_timers;
+
+int hpet_init(void);
+uint32_t hpet_ms_to_ticks(unsigned int ms);
+uint32_t hpet_timer_avail_irqs(unsigned int nr);
+bool hpet_timer_supports_periodic(unsigned int nr);
+void hpet_init_timer(unsigned int nr, unsigned int irq, unsigned int ms,
+                     bool level, bool periodic);
+void hpet_disable_timer(unsigned int nr);
+bool hpet_intr_pending(unsigned int nr);
+void hpet_intr_pending_clear(unsigned int nr);
+
+#endif /* !XTF_X86_HPET_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/build/files.mk b/build/files.mk
index 489f28a..9fa6d85 100644
--- a/build/files.mk
+++ b/build/files.mk
@@ -30,6 +30,7 @@ obj-perenv += $(ROOT)/arch/x86/traps.o
 
 # HVM specific objects
 obj-hvm += $(ROOT)/arch/x86/apic.o
+obj-hvm += $(ROOT)/arch/x86/hpet.o
 obj-hvm += $(ROOT)/arch/x86/hvm/pagetables.o
 obj-hvm += $(ROOT)/arch/x86/hvm/traps.o
 
diff --git a/docs/all-tests.dox b/docs/all-tests.dox
index 355cb80..1d0422f 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 test.
+
 @subpage test-invlpg - `invlpg` instruction behaviour.
 
 @subpage test-lbr-tsx-vmentry - Haswell and later LBR/TSX Vmentry failure test.
diff --git a/tests/hpet/Makefile b/tests/hpet/Makefile
new file mode 100644
index 0000000..bb78b09
--- /dev/null
+++ b/tests/hpet/Makefile
@@ -0,0 +1,9 @@
+include $(ROOT)/build/common.mk
+
+NAME      := hpet
+CATEGORY  := functional
+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..ff59330
--- /dev/null
+++ b/tests/hpet/main.c
@@ -0,0 +1,100 @@
+/**
+ * @file tests/hpet/main.c
+ * @ref test-hpet
+ *
+ * @page test-hpet hpet
+ *
+ * HPET functionality testing.
+ *
+ * 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";
+
+/* Tests period in ms. */
+#define TEST_PERIOD 1
+
+void test_main(void)
+{
+    unsigned int irq, timer;
+    uint32_t irqs;
+
+    hpet_init();
+
+    /* Pick a timer that supports periodic mode. */
+    for ( timer = 0; timer < hpet_nr_timers; timer++ )
+        if ( hpet_timer_supports_periodic(timer) )
+            break;
+
+    if ( timer == hpet_nr_timers )
+        return xtf_error(
+"Error: cannot find timer that supports periodic mode\n");
+
+    /* Select a valid IRQ. */
+    irqs = hpet_timer_avail_irqs(timer);
+    for ( irq = 0; irq < 32; irq++ )
+        if ( test_bit(irq, &irqs) )
+            break;
+    if ( irq == 32 )
+        return xtf_error("Error: cannot find valid IRQ routing for timer %u\n",
+                         timer);
+    printk("Routing timer %u to IRQ %u\n", timer, irq);
+
+    /* Test oneshot mode. */
+    hpet_init_timer(timer, irq, TEST_PERIOD, true, false);
+    msleep(TEST_PERIOD);
+    if ( !hpet_intr_pending(timer) )
+        return xtf_failure(
+"Fail: status bit unset for level interrupt in oneshot mode\n");
+
+    /* Clean status bit. */
+    hpet_intr_pending_clear(timer);
+
+    /* Try periodic mode. */
+    hpet_init_timer(timer, irq, TEST_PERIOD, true, true);
+    msleep(TEST_PERIOD);
+    if ( !hpet_intr_pending(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.
+     */
+    msleep(TEST_PERIOD);
+    if ( hpet_mmio_read(HPET_Tn_CMP(timer)) <
+         hpet_ms_to_ticks(TEST_PERIOD * 3) )
+        return xtf_failure("Fail: comparator not updated in periodic mode\n");
+
+    /* Clear the status bit and wait for it to be set again. */
+    hpet_intr_pending_clear(timer);
+    msleep(TEST_PERIOD);
+    if ( !hpet_intr_pending(timer) )
+        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_disable_timer(timer);
+    hpet_intr_pending_clear(timer);
+    hpet_init_timer(timer, irq, TEST_PERIOD, false, false);
+    msleep(TEST_PERIOD);
+    if ( hpet_intr_pending(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.16.3


_______________________________________________
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®.