[PATCH kvm-unit-tests 3/3] arm64: timer: Add support for phys timer testing

Christoffer Dall
2017-07-13T19:19:47Z

Rearrange the code to be able to reuse as much as posible and add
support for testing the physical timer as well.

Also change the default unittests configuration to run a separate vtimer
and ptimer test so that older kernels without ptimer support just show a
failure.

Signed-off-by: Christoffer Dall <cdall@linaro.org>
---
arm/timer.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++--------
arm/unittests.cfg | 9 ++-
2 files changed, 177 insertions(+), 31 deletions(-)

diff --git a/arm/timer.c b/arm/timer.c
index 77d257c..33dfc6f 100644
--- a/arm/timer.c
+++ b/arm/timer.c
@@ -11,34 +11,119 @@
#include <asm/gic.h>
#include <asm/io.h>

-#define CNTV_CTL_ENABLE (1 << 0)
-#define CNTV_CTL_IMASK (1 << 1)
-#define CNTV_CTL_ISTATUS (1 << 2)
+#define ARCH_TIMER_CTL_ENABLE (1 << 0)
+#define ARCH_TIMER_CTL_IMASK (1 << 1)
+#define ARCH_TIMER_CTL_ISTATUS (1 << 2)

-static u32 vtimer_irq, vtimer_irq_flags;
static void *gic_ispendr;
-static bool vtimer_irq_received;
+
+static u64 read_vtimer_counter(void)
+{
+ return read_sysreg(cntvct_el0);
+}
+
+static u64 read_vtimer_cval(void)
+{
+ return read_sysreg(cntv_cval_el0);
+}
+
+static void write_vtimer_cval(u64 val)
+{
+ write_sysreg(val, cntv_cval_el0);
+}
+
+static u64 read_vtimer_ctl(void)
+{
+ return read_sysreg(cntv_ctl_el0);
+}
+
+static void write_vtimer_ctl(u64 val)
+{
+ write_sysreg(val, cntv_ctl_el0);
+}
+
+static u64 read_ptimer_counter(void)
+{
+ return read_sysreg(cntpct_el0);
+}
+
+static u64 read_ptimer_cval(void)
+{
+ return read_sysreg(cntp_cval_el0);
+}
+
+static void write_ptimer_cval(u64 val)
+{
+ write_sysreg(val, cntp_cval_el0);
+}
+
+static u64 read_ptimer_ctl(void)
+{
+ return read_sysreg(cntp_ctl_el0);
+}
+
+static void write_ptimer_ctl(u64 val)
+{
+ write_sysreg(val, cntp_ctl_el0);
+}
+
+struct timer_info {
+ u32 irq;
+ u32 irq_flags;
+ bool irq_received;
+ u64 (*read_counter)(void);
+ u64 (*read_cval)(void);
+ void (*write_cval)(u64);
+ u64 (*read_ctl)(void);
+ void (*write_ctl)(u64);
+};
+
+static struct timer_info vtimer_info = {
+ .irq_received = false,
+ .read_counter = read_vtimer_counter,
+ .read_cval = read_vtimer_cval,
+ .write_cval = write_vtimer_cval,
+ .read_ctl = read_vtimer_ctl,
+ .write_ctl = write_vtimer_ctl,
+};
+
+static struct timer_info ptimer_info = {
+ .irq_received = false,
+ .read_counter = read_ptimer_counter,
+ .read_cval = read_ptimer_cval,
+ .write_cval = write_ptimer_cval,
+ .read_ctl = read_ptimer_ctl,
+ .write_ctl = write_ptimer_ctl,
+};

static void irq_handler(struct pt_regs *regs)
{
+ struct timer_info *info;
u32 irqstat = gic_read_iar();
u32 irqnr = gic_iar_irqnr(irqstat);

if (irqnr != GICC_INT_SPURIOUS)
gic_write_eoir(irqstat);

- if (irqnr == PPI(vtimer_irq)) {
- write_sysreg(CNTV_CTL_IMASK | CNTV_CTL_ENABLE, cntv_ctl_el0);
- ++vtimer_irq_received;
+ if (irqnr == PPI(vtimer_info.irq)) {
+ info = &vtimer_info;
+ } else if (irqnr == PPI(ptimer_info.irq)) {
+ info = &ptimer_info;
+ } else {
+ report_info("Unexpected interrupt: %d\n", irqnr);
+ return;
}
+
+ info->write_ctl(ARCH_TIMER_CTL_IMASK | ARCH_TIMER_CTL_ENABLE);
+ info->irq_received = true;
}

-static bool gic_vtimer_pending(void)
+static bool gic_timer_pending(struct timer_info *info)
{
- return readl(gic_ispendr) & (1 << PPI(vtimer_irq));
+ return readl(gic_ispendr) & (1 << PPI(info->irq));
}

-static bool test_cval_10msec(void)
+static bool test_cval_10msec(struct timer_info *info)
{
u64 time_10ms = read_sysreg(cntfrq_el0) / 100;
u64 time_1us = time_10ms / 10000;
@@ -46,15 +131,15 @@ static bool test_cval_10msec(void)
s64 difference;

/* Program timer to fire in 10 ms */
- before_timer = read_sysreg(cntvct_el0);
- write_sysreg(before_timer + time_10ms, cntv_cval_el0);
+ before_timer = info->read_counter();
+ info->write_cval(before_timer + time_10ms);

/* Wait for the timer to fire */
- while (!(read_sysreg(cntv_ctl_el0) & CNTV_CTL_ISTATUS))
+ while (!(info->read_ctl() & ARCH_TIMER_CTL_ISTATUS))
;

/* It fired, check how long it took */
- after_timer = read_sysreg(cntvct_el0);
+ after_timer = info->read_counter();
difference = after_timer - (before_timer + time_10ms);

report_info("After timer: 0x%016lx", after_timer);
@@ -62,32 +147,43 @@ static bool test_cval_10msec(void)
report_info("Difference : %ld us", difference / time_1us);

if (difference < 0) {
- printf("CNTV_CTL_EL0.ISTATUS set too early\n");
+ printf("ISTATUS set too early\n");
return false;
}
return difference < time_10ms;
}

-static void test_vtimer(void)
+static void test_timer(struct timer_info *info)
{
u64 now = read_sysreg(cntvct_el0);
u64 time_10s = read_sysreg(cntfrq_el0) * 10;
u64 later = now + time_10s;

- report_prefix_push("vtimer-busy-loop");

/* Enable the timer, but schedule it for much later*/
- write_sysreg(later, cntv_cval_el0);
+ info->write_cval(later);
isb();
- write_sysreg(CNTV_CTL_ENABLE, cntv_ctl_el0);
+ info->write_ctl(ARCH_TIMER_CTL_ENABLE);

- report("not pending before", !gic_vtimer_pending());
- report("latency within 10 ms", test_cval_10msec());
- report("interrupt received", vtimer_irq_received);
+ report("not pending before", !gic_timer_pending(info));
+ report("latency within 10 ms", test_cval_10msec(info));
+ report("interrupt received", info->irq_received);

/* Disable the timer again */
- write_sysreg(0, cntv_ctl_el0);
+ info->write_ctl(0);
+}
+
+static void test_vtimer(void)
+{
+ report_prefix_push("vtimer-busy-loop");
+ test_timer(&vtimer_info);
+ report_prefix_pop();
+}

+static void test_ptimer(void)
+{
+ report_prefix_push("ptimer-busy-loop");
+ test_timer(&ptimer_info);
report_prefix_pop();
}

@@ -102,20 +198,26 @@ static void test_init(void)
assert(node >= 0);
prop = fdt_get_property(fdt, node, "interrupts", &len);
assert(prop && len == (4 * 3 * sizeof(u32)));
+
data = (u32 *)prop->data;
+ assert(fdt32_to_cpu(data[3]) == 1);
+ ptimer_info.irq = fdt32_to_cpu(data[4]);
+ ptimer_info.irq_flags = fdt32_to_cpu(data[5]);
assert(fdt32_to_cpu(data[6]) == 1);
- vtimer_irq = fdt32_to_cpu(data[7]);
- vtimer_irq_flags = fdt32_to_cpu(data[8]);
+ vtimer_info.irq = fdt32_to_cpu(data[7]);
+ vtimer_info.irq_flags = fdt32_to_cpu(data[8]);

gic_enable_defaults();

switch (gic_version()) {
case 2:
- writel(1 << PPI(vtimer_irq), gicv2_dist_base() + GICD_ISENABLER + 0);
+ writel(1 << PPI(vtimer_info.irq), gicv2_dist_base() + GICD_ISENABLER + 0);
+ writel(1 << PPI(ptimer_info.irq), gicv2_dist_base() + GICD_ISENABLER + 0);
gic_ispendr = gicv2_dist_base() + GICD_ISPENDR;
break;
case 3:
- writel(1 << PPI(vtimer_irq), gicv3_sgi_base() + GICR_ISENABLER0);
+ writel(1 << PPI(vtimer_info.irq), gicv3_sgi_base() + GICR_ISENABLER0);
+ writel(1 << PPI(ptimer_info.irq), gicv3_sgi_base() + GICR_ISENABLER0);
gic_ispendr = gicv3_sgi_base() + GICD_ISPENDR;
break;
}
@@ -124,15 +226,52 @@ static void test_init(void)
local_irq_enable();
}

-int main(void)
+static void print_vtimer_info(void)
{
printf("CNTFRQ_EL0 : 0x%016lx\n", read_sysreg(cntfrq_el0));
printf("CNTVCT_EL0 : 0x%016lx\n", read_sysreg(cntvct_el0));
printf("CNTV_CTL_EL0 : 0x%016lx\n", read_sysreg(cntv_ctl_el0));
printf("CNTV_CVAL_EL0: 0x%016lx\n", read_sysreg(cntv_cval_el0));
+}
+
+static void print_ptimer_info(void)
+{
+ printf("CNTPCT_EL0 : 0x%016lx\n", read_sysreg(cntpct_el0));
+ printf("CNTP_CTL_EL0 : 0x%016lx\n", read_sysreg(cntp_ctl_el0));
+ printf("CNTP_CVAL_EL0: 0x%016lx\n", read_sysreg(cntp_cval_el0));
+}
+
+
+int main(int argc, char **argv)
+{
+ bool run_ptimer_test = false;
+ bool run_vtimer_test = false;
+
+ /* Check if we should also check the physical timer */
+ if (argc > 1) {
+ if (strcmp(argv[1], "vtimer") == 0) {
+ run_vtimer_test = true;
+ } else if (strcmp(argv[1], "ptimer") == 0) {
+ run_ptimer_test = true;
+ } else {
+ report_abort("Unknown option '%s'", argv[1]);
+ }
+ } else {
+ run_vtimer_test = true;
+ }
+
+ if (run_vtimer_test)
+ print_vtimer_info();
+ else if (run_ptimer_test)
+ print_ptimer_info();

test_init();
- test_vtimer();
+
+ if (run_vtimer_test)
+ test_vtimer();
+ else if (run_ptimer_test)
+ test_ptimer();
+

return report_summary();
}
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
index bdfedf8..d55e52e 100644
--- a/arm/unittests.cfg
+++ b/arm/unittests.cfg
@@ -111,7 +111,14 @@ smp = $MAX_SMP
groups = psci

# Timer tests
-[timer]
+[vtimer]
file = timer.flat
+extra_params = -append 'vtimer'
+groups = timer
+timeout = 2s
+
+[ptimer]
+file = timer.flat
+extra_params = -append 'ptimer'
groups = timer
timeout = 2s
--
2.9.0