[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |