diff mbox

[2/2] Add vlapic timer checks

Message ID 20170320143846.21234-3-anthony.perard@citrix.com (mailing list archive)
State New, archived
Headers show

Commit Message

Anthony PERARD March 20, 2017, 2:38 p.m. UTC
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 tests/vlapic-timer/Makefile |   9 +++
 tests/vlapic-timer/main.c   | 131 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 140 insertions(+)
 create mode 100644 tests/vlapic-timer/Makefile
 create mode 100644 tests/vlapic-timer/main.c

Comments

Andrew Cooper March 29, 2017, 1:18 p.m. UTC | #1
On 20/03/17 14:38, Anthony PERARD wrote:
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
> ---
>  tests/vlapic-timer/Makefile |   9 +++
>  tests/vlapic-timer/main.c   | 131 ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 140 insertions(+)
>  create mode 100644 tests/vlapic-timer/Makefile
>  create mode 100644 tests/vlapic-timer/main.c
>
> diff --git a/tests/vlapic-timer/Makefile b/tests/vlapic-timer/Makefile
> new file mode 100644
> index 0000000..01fa4ea
> --- /dev/null
> +++ b/tests/vlapic-timer/Makefile
> @@ -0,0 +1,9 @@
> +include $(ROOT)/build/common.mk
> +
> +NAME      := vlapic-timer
> +CATEGORY  := functional
> +TEST-ENVS := $(HVM_ENVIRONMENTS)

Do you really need all HVM environments?  APIC emulation doesn't have
any interaction with paging or operating modes.

Speaking of this test, I should really see about upstreaming some of my
ad-hoc lapic tests, which have existed almost as long as XTF has.  I am
also going to have to see about getting the test revision logic working,
because I'd prefer to have one "apic functionaltiy test" than a large
number of individual tests each testing a different part of behaviour.

> +
> +obj-perenv += main.o
> +
> +include $(ROOT)/build/gen.mk
> diff --git a/tests/vlapic-timer/main.c b/tests/vlapic-timer/main.c
> new file mode 100644
> index 0000000..b081661
> --- /dev/null
> +++ b/tests/vlapic-timer/main.c
> @@ -0,0 +1,131 @@
> +/**
> + * @file tests/vlapic-timer/main.c
> + * @ref test-vlapic-timer - LAPIC Timer Emulation
> + *
> + * @page test-vlapic-timer LAPIC Timer Emulation
> + *
> + * Tests the behavior of the vlapic timer emulation by Xen.
> + *
> + * This tests should work on baremetal.
> + *
> + * It is testing switch between different mode, one-shot and periodic.
> + *
> + * @see tests/vlapic-timer/main.c
> + */
> +#include <stdbool.h>

No need for this include.  It will come in via xtf.h

> +#include <xtf.h>
> +#include <arch/apicdef.h>
> +
> +const char test_title[] = "Test vlapic-timer";
> +
> +static inline void apic_write(unsigned long reg, uint32_t v)
> +{
> +    *((volatile uint32_t *)(APIC_BASE + reg)) = v;
> +}
> +
> +static inline uint32_t apic_read(unsigned long reg)
> +{
> +    return *((volatile uint32_t *)(APIC_BASE + reg));
> +}

These should be in an piece of common apic library, along with an
initialisation function to probe and enable the lapic.  (after all,
restricted PVH environments one have one at all).

> +
> +static inline void change_mode(unsigned long new_mode)
> +{
> +    uint32_t lvtt;
> +
> +    lvtt = apic_read(APIC_LVTT);
> +    apic_write(APIC_LVTT, (lvtt & ~APIC_TIMER_MODE_MASK) | new_mode);
> +}
> +
> +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);
> +    }
> +}
> +
> +void test_main(void)
> +{
> +    uint32_t tmict = 0x999999;
> +
> +    apic_write(APIC_TMICT, tmict);
> +    /*
> +     * Assuming that the initial mode is one-shot, change it to periodic. TMICT
> +     * should not be reset.

It is not generally a good idea make assumptions like this.  The only
way it would be safe, is to state that one-shot is the reset value of
the APIC state.

> +     */
> +    change_mode(APIC_TIMER_MODE_PERIODIC);
> +    if ( apic_read(APIC_TMICT) != tmict )
> +        xtf_failure("Fail: TMICT value reset\n");
> +
> +    /* Testing one-shot */
> +    printk("Testing one-shot mode\n");
> +    change_mode(APIC_TIMER_MODE_ONESHOT);
> +    apic_write(APIC_TMICT, tmict);
> +    if ( !apic_read(APIC_TMCCT) )
> +        xtf_failure("Fail: TMCCT should have a non-zero value\n");
> +    wait_until_tmcct_is_zero(tmict, false);
> +    if ( apic_read(APIC_TMCCT) )
> +        xtf_failure("Fail: TMCCT should have reached 0\n");

Please spread this logic out with newlines, to make it easier to read.

~Andrew

> +
> +    /*
> +     * 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);
> +    printk("Testing periodic mode\n");
> +    change_mode(APIC_TIMER_MODE_PERIODIC);
> +    if ( !apic_read(APIC_TMCCT) )
> +        xtf_failure("Fail: TMCCT should have a non-zero value\n");
> +    /*
> +     * After the change of mode, the counter should not be reset and continue
> +     * counting down from where it was
> +     */
> +    if ( apic_read(APIC_TMCCT) > (tmict / 2) )
> +        xtf_failure("Fail: TMCCT should not be reset to TMICT value\n");
> +    wait_until_tmcct_is_zero(tmict, false);
> +    if ( apic_read(APIC_TMCCT) < (tmict / 2) )
> +        xtf_failure("Fail: TMCCT should be reset to the initial-count\n");
> +
> +    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
> +     */
> +    printk("Testing one-shot after periodic (no tmict reset)\n");
> +    change_mode(APIC_TIMER_MODE_ONESHOT);
> +    if ( !apic_read(APIC_TMCCT) )
> +        xtf_failure("Fail: TMCCT should have a non-zero value\n");
> +    if ( apic_read(APIC_TMCCT) > (tmict / 2) )
> +        xtf_failure("Fail: TMCCT should not be reset to init\n");
> +    wait_until_tmcct_is_zero(tmict, false);
> +    if ( apic_read(APIC_TMCCT) )
> +        xtf_failure("Fail: TMCCT should have reach zero\n");
> +
> +    /* now tmcct == 0 and tmict != 0 */
> +    change_mode(APIC_TIMER_MODE_PERIODIC);
> +    if ( apic_read(APIC_TMCCT) )
> +        xtf_failure("Fail: TMCCT should stay at zero\n");
> +
> +    xtf_success(NULL);
> +}
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * tab-width: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
diff mbox

Patch

diff --git a/tests/vlapic-timer/Makefile b/tests/vlapic-timer/Makefile
new file mode 100644
index 0000000..01fa4ea
--- /dev/null
+++ b/tests/vlapic-timer/Makefile
@@ -0,0 +1,9 @@ 
+include $(ROOT)/build/common.mk
+
+NAME      := vlapic-timer
+CATEGORY  := functional
+TEST-ENVS := $(HVM_ENVIRONMENTS)
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
diff --git a/tests/vlapic-timer/main.c b/tests/vlapic-timer/main.c
new file mode 100644
index 0000000..b081661
--- /dev/null
+++ b/tests/vlapic-timer/main.c
@@ -0,0 +1,131 @@ 
+/**
+ * @file tests/vlapic-timer/main.c
+ * @ref test-vlapic-timer - LAPIC Timer Emulation
+ *
+ * @page test-vlapic-timer LAPIC Timer Emulation
+ *
+ * Tests the behavior of the vlapic timer emulation by Xen.
+ *
+ * This tests should work on baremetal.
+ *
+ * It is testing switch between different mode, one-shot and periodic.
+ *
+ * @see tests/vlapic-timer/main.c
+ */
+#include <stdbool.h>
+#include <xtf.h>
+#include <arch/apicdef.h>
+
+const char test_title[] = "Test vlapic-timer";
+
+static inline void apic_write(unsigned long reg, uint32_t v)
+{
+    *((volatile uint32_t *)(APIC_BASE + reg)) = v;
+}
+
+static inline uint32_t apic_read(unsigned long reg)
+{
+    return *((volatile uint32_t *)(APIC_BASE + reg));
+}
+
+static inline void change_mode(unsigned long new_mode)
+{
+    uint32_t lvtt;
+
+    lvtt = apic_read(APIC_LVTT);
+    apic_write(APIC_LVTT, (lvtt & ~APIC_TIMER_MODE_MASK) | new_mode);
+}
+
+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);
+    }
+}
+
+void test_main(void)
+{
+    uint32_t tmict = 0x999999;
+
+    apic_write(APIC_TMICT, tmict);
+    /*
+     * Assuming that the initial mode is one-shot, change it to periodic. TMICT
+     * should not be reset.
+     */
+    change_mode(APIC_TIMER_MODE_PERIODIC);
+    if ( apic_read(APIC_TMICT) != tmict )
+        xtf_failure("Fail: TMICT value reset\n");
+
+    /* Testing one-shot */
+    printk("Testing one-shot mode\n");
+    change_mode(APIC_TIMER_MODE_ONESHOT);
+    apic_write(APIC_TMICT, tmict);
+    if ( !apic_read(APIC_TMCCT) )
+        xtf_failure("Fail: TMCCT should have a non-zero value\n");
+    wait_until_tmcct_is_zero(tmict, false);
+    if ( apic_read(APIC_TMCCT) )
+        xtf_failure("Fail: TMCCT should have reached 0\n");
+
+    /*
+     * 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);
+    printk("Testing periodic mode\n");
+    change_mode(APIC_TIMER_MODE_PERIODIC);
+    if ( !apic_read(APIC_TMCCT) )
+        xtf_failure("Fail: TMCCT should have a non-zero value\n");
+    /*
+     * After the change of mode, the counter should not be reset and continue
+     * counting down from where it was
+     */
+    if ( apic_read(APIC_TMCCT) > (tmict / 2) )
+        xtf_failure("Fail: TMCCT should not be reset to TMICT value\n");
+    wait_until_tmcct_is_zero(tmict, false);
+    if ( apic_read(APIC_TMCCT) < (tmict / 2) )
+        xtf_failure("Fail: TMCCT should be reset to the initial-count\n");
+
+    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
+     */
+    printk("Testing one-shot after periodic (no tmict reset)\n");
+    change_mode(APIC_TIMER_MODE_ONESHOT);
+    if ( !apic_read(APIC_TMCCT) )
+        xtf_failure("Fail: TMCCT should have a non-zero value\n");
+    if ( apic_read(APIC_TMCCT) > (tmict / 2) )
+        xtf_failure("Fail: TMCCT should not be reset to init\n");
+    wait_until_tmcct_is_zero(tmict, false);
+    if ( apic_read(APIC_TMCCT) )
+        xtf_failure("Fail: TMCCT should have reach zero\n");
+
+    /* now tmcct == 0 and tmict != 0 */
+    change_mode(APIC_TIMER_MODE_PERIODIC);
+    if ( apic_read(APIC_TMCCT) )
+        xtf_failure("Fail: TMCCT should stay at zero\n");
+
+    xtf_success(NULL);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */