Merge tag 's390x-2020-31-07' of https://github.com/frankjaa/kvm-unit-tests into HEAD
* IO tests from Pierre
* GCC 10 compile fix from Claudio
* CPU model test fix from Thomas
diff --git a/arm/micro-bench.c b/arm/micro-bench.c
index 4612f41..362f93e 100644
--- a/arm/micro-bench.c
+++ b/arm/micro-bench.c
@@ -20,29 +20,40 @@
*/
#include <libcflat.h>
#include <asm/gic.h>
+#include <asm/gic-v3-its.h>
+#include <asm/timer.h>
-#define NTIMES (1U << 16)
+#define NS_5_SECONDS (5 * 1000 * 1000 * 1000UL)
static u32 cntfrq;
-static volatile bool ipi_ready, ipi_received;
+static volatile bool irq_ready, irq_received;
+static int nr_ipi_received;
+
static void *vgic_dist_base;
static void (*write_eoir)(u32 irqstat);
-static void ipi_irq_handler(struct pt_regs *regs)
+static void gic_irq_handler(struct pt_regs *regs)
{
- ipi_ready = false;
- ipi_received = true;
- gic_write_eoir(gic_read_iar());
- ipi_ready = true;
+ u32 irqstat = gic_read_iar();
+ irq_ready = false;
+ irq_received = true;
+ gic_write_eoir(irqstat);
+
+ if (irqstat == PPI(TIMER_VTIMER_IRQ)) {
+ write_sysreg((ARCH_TIMER_CTL_IMASK | ARCH_TIMER_CTL_ENABLE),
+ cntv_ctl_el0);
+ isb();
+ }
+ irq_ready = true;
}
-static void ipi_secondary_entry(void *data)
+static void gic_secondary_entry(void *data)
{
- install_irq_handler(EL1H_IRQ, ipi_irq_handler);
+ install_irq_handler(EL1H_IRQ, gic_irq_handler);
gic_enable_defaults();
local_irq_enable();
- ipi_ready = true;
+ irq_ready = true;
while (true)
cpu_relax();
}
@@ -72,9 +83,9 @@
break;
}
- ipi_ready = false;
+ irq_ready = false;
gic_enable_defaults();
- on_cpu_async(1, ipi_secondary_entry, NULL);
+ on_cpu_async(1, gic_secondary_entry, NULL);
cntfrq = get_cntfrq();
printf("Timer Frequency %d Hz (Output in microseconds)\n", cntfrq);
@@ -82,29 +93,179 @@
return true;
}
-static void ipi_prep(void)
+static void gic_prep_common(void)
{
unsigned tries = 1 << 28;
- while (!ipi_ready && tries--)
+ while (!irq_ready && tries--)
cpu_relax();
- assert(ipi_ready);
+ assert(irq_ready);
+}
+
+static bool ipi_prep(void)
+{
+ u32 val;
+
+ val = readl(vgic_dist_base + GICD_CTLR);
+ if (readl(vgic_dist_base + GICD_TYPER2) & GICD_TYPER2_nASSGIcap) {
+ /* nASSGIreq can be changed only when GICD is disabled */
+ val &= ~GICD_CTLR_ENABLE_G1A;
+ val &= ~GICD_CTLR_nASSGIreq;
+ writel(val, vgic_dist_base + GICD_CTLR);
+ gicv3_dist_wait_for_rwp();
+
+ val |= GICD_CTLR_ENABLE_G1A;
+ writel(val, vgic_dist_base + GICD_CTLR);
+ gicv3_dist_wait_for_rwp();
+ }
+
+ nr_ipi_received = 0;
+ gic_prep_common();
+ return true;
+}
+
+static bool ipi_hw_prep(void)
+{
+ u32 val;
+
+ val = readl(vgic_dist_base + GICD_CTLR);
+ if (readl(vgic_dist_base + GICD_TYPER2) & GICD_TYPER2_nASSGIcap) {
+ /* nASSGIreq can be changed only when GICD is disabled */
+ val &= ~GICD_CTLR_ENABLE_G1A;
+ val |= GICD_CTLR_nASSGIreq;
+ writel(val, vgic_dist_base + GICD_CTLR);
+ gicv3_dist_wait_for_rwp();
+
+ val |= GICD_CTLR_ENABLE_G1A;
+ writel(val, vgic_dist_base + GICD_CTLR);
+ gicv3_dist_wait_for_rwp();
+ } else {
+ return false;
+ }
+
+ nr_ipi_received = 0;
+ gic_prep_common();
+ return true;
}
static void ipi_exec(void)
{
unsigned tries = 1 << 28;
- static int received = 0;
- ipi_received = false;
+ irq_received = false;
gic_ipi_send_single(1, 1);
- while (!ipi_received && tries--)
+ while (!irq_received && tries--)
cpu_relax();
- ++received;
- assert_msg(ipi_received, "failed to receive IPI in time, but received %d successfully\n", received);
+ if (irq_received)
+ ++nr_ipi_received;
+
+ assert_msg(irq_received, "failed to receive IPI in time, but received %d successfully\n", nr_ipi_received);
+}
+
+static bool lpi_prep(void)
+{
+ struct its_collection *col1;
+ struct its_device *dev2;
+
+ if (!gicv3_its_base())
+ return false;
+
+ its_enable_defaults();
+ dev2 = its_create_device(2 /* dev id */, 8 /* nb_ites */);
+ col1 = its_create_collection(1 /* col id */, 1 /* target PE */);
+ gicv3_lpi_set_config(8199, LPI_PROP_DEFAULT);
+
+ its_send_mapd_nv(dev2, true);
+ its_send_mapc_nv(col1, true);
+ its_send_invall_nv(col1);
+ its_send_mapti_nv(dev2, 8199 /* lpi id */, 20 /* event id */, col1);
+
+ gic_prep_common();
+ return true;
+}
+
+static void lpi_exec(void)
+{
+ struct its_device *dev2;
+ unsigned tries = 1 << 28;
+ static int received = 0;
+
+ irq_received = false;
+
+ dev2 = its_get_device(2);
+ its_send_int_nv(dev2, 20);
+
+ while (!irq_received && tries--)
+ cpu_relax();
+
+ if (irq_received)
+ ++received;
+
+ assert_msg(irq_received, "failed to receive LPI in time, but received %d successfully\n", received);
+}
+
+static bool timer_prep(void)
+{
+ void *gic_isenabler;
+
+ gic_enable_defaults();
+ install_irq_handler(EL1H_IRQ, gic_irq_handler);
+ local_irq_enable();
+
+ switch (gic_version()) {
+ case 2:
+ gic_isenabler = gicv2_dist_base() + GICD_ISENABLER;
+ break;
+ case 3:
+ gic_isenabler = gicv3_sgi_base() + GICR_ISENABLER0;
+ break;
+ default:
+ assert_msg(0, "Unreachable");
+ }
+
+ writel(1 << PPI(TIMER_VTIMER_IRQ), gic_isenabler);
+ write_sysreg(ARCH_TIMER_CTL_ENABLE, cntv_ctl_el0);
+ isb();
+
+ gic_prep_common();
+ return true;
+}
+
+static void timer_exec(void)
+{
+ u64 before_timer;
+ u64 timer_10ms;
+ unsigned tries = 1 << 28;
+ static int received = 0;
+
+ irq_received = false;
+
+ before_timer = read_sysreg(cntvct_el0);
+ timer_10ms = cntfrq / 100;
+ write_sysreg(before_timer + timer_10ms, cntv_cval_el0);
+ write_sysreg(ARCH_TIMER_CTL_ENABLE, cntv_ctl_el0);
+ isb();
+
+ while (!irq_received && tries--)
+ cpu_relax();
+
+ if (irq_received)
+ ++received;
+
+ assert_msg(irq_received, "failed to receive PPI in time, but received %d successfully\n", received);
+}
+
+static void timer_post(uint64_t ntimes, uint64_t *total_ticks)
+{
+ /*
+ * We use a 10msec timer to test the latency of PPI,
+ * so we substract the ticks of 10msec to get the
+ * actual latency
+ */
+ *total_ticks -= ntimes * (cntfrq / 100);
}
static void hvc_exec(void)
@@ -140,17 +301,22 @@
struct exit_test {
const char *name;
- void (*prep)(void);
+ bool (*prep)(void);
void (*exec)(void);
+ void (*post)(uint64_t ntimes, uint64_t *total_ticks);
+ u32 times;
bool run;
};
static struct exit_test tests[] = {
- {"hvc", NULL, hvc_exec, true},
- {"mmio_read_user", NULL, mmio_read_user_exec, true},
- {"mmio_read_vgic", NULL, mmio_read_vgic_exec, true},
- {"eoi", NULL, eoi_exec, true},
- {"ipi", ipi_prep, ipi_exec, true},
+ {"hvc", NULL, hvc_exec, NULL, 65536, true},
+ {"mmio_read_user", NULL, mmio_read_user_exec, NULL, 65536, true},
+ {"mmio_read_vgic", NULL, mmio_read_vgic_exec, NULL, 65536, true},
+ {"eoi", NULL, eoi_exec, NULL, 65536, true},
+ {"ipi", ipi_prep, ipi_exec, NULL, 65536, true},
+ {"ipi_hw", ipi_hw_prep, ipi_exec, NULL, 65536, true},
+ {"lpi", lpi_prep, lpi_exec, NULL, 65536, true},
+ {"timer_10ms", timer_prep, timer_exec, timer_post, 256, true},
};
struct ns_time {
@@ -171,23 +337,36 @@
static void loop_test(struct exit_test *test)
{
- uint64_t start, end, total_ticks, ntimes = NTIMES;
- struct ns_time total_ns, avg_ns;
+ uint64_t start, end, total_ticks, ntimes = 0;
+ struct ns_time avg_ns, total_ns = {};
- if (test->prep)
- test->prep();
+ total_ticks = 0;
+ if (test->prep) {
+ if(!test->prep()) {
+ printf("%s test skipped\n", test->name);
+ return;
+ }
+ }
- isb();
- start = read_sysreg(cntpct_el0);
- while (ntimes--)
+ while (ntimes < test->times && total_ns.ns < NS_5_SECONDS) {
+ isb();
+ start = read_sysreg(cntpct_el0);
test->exec();
- isb();
- end = read_sysreg(cntpct_el0);
+ isb();
+ end = read_sysreg(cntpct_el0);
- total_ticks = end - start;
- ticks_to_ns_time(total_ticks, &total_ns);
- avg_ns.ns = total_ns.ns / NTIMES;
- avg_ns.ns_frac = total_ns.ns_frac / NTIMES;
+ ntimes++;
+ total_ticks += (end - start);
+ ticks_to_ns_time(total_ticks, &total_ns);
+ }
+
+ if (test->post) {
+ test->post(ntimes, &total_ticks);
+ ticks_to_ns_time(total_ticks, &total_ns);
+ }
+
+ avg_ns.ns = total_ns.ns / ntimes;
+ avg_ns.ns_frac = total_ns.ns_frac / ntimes;
printf("%-30s%15" PRId64 ".%-15" PRId64 "%15" PRId64 ".%-15" PRId64 "\n",
test->name, total_ns.ns, total_ns.ns_frac, avg_ns.ns, avg_ns.ns_frac);
diff --git a/arm/timer.c b/arm/timer.c
index 44621b4..09e3f8f 100644
--- a/arm/timer.c
+++ b/arm/timer.c
@@ -8,15 +8,12 @@
#include <libcflat.h>
#include <devicetree.h>
#include <errata.h>
+#include <asm/timer.h>
#include <asm/delay.h>
#include <asm/processor.h>
#include <asm/gic.h>
#include <asm/io.h>
-#define ARCH_TIMER_CTL_ENABLE (1 << 0)
-#define ARCH_TIMER_CTL_IMASK (1 << 1)
-#define ARCH_TIMER_CTL_ISTATUS (1 << 2)
-
static void *gic_isenabler;
static void *gic_icenabler;
@@ -108,7 +105,6 @@
struct timer_info {
u32 irq;
- u32 irq_flags;
volatile bool irq_received;
u64 (*read_counter)(void);
u64 (*read_cval)(void);
@@ -304,23 +300,9 @@
static void test_init(void)
{
- const struct fdt_property *prop;
- const void *fdt = dt_fdt();
- int node, len;
- u32 *data;
-
- node = fdt_node_offset_by_compatible(fdt, -1, "arm,armv8-timer");
- 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_info.irq = fdt32_to_cpu(data[7]);
- vtimer_info.irq_flags = fdt32_to_cpu(data[8]);
+ assert(TIMER_PTIMER_IRQ != -1 && TIMER_VTIMER_IRQ != -1);
+ ptimer_info.irq = TIMER_PTIMER_IRQ;
+ vtimer_info.irq = TIMER_VTIMER_IRQ;
install_exception_handler(EL1H_SYNC, ESR_EL1_EC_UNKNOWN, ptimer_unsupported_handler);
ptimer_info.read_ctl();
diff --git a/lib/arm/asm/gic-v3.h b/lib/arm/asm/gic-v3.h
index cb72922..b4ce130 100644
--- a/lib/arm/asm/gic-v3.h
+++ b/lib/arm/asm/gic-v3.h
@@ -20,10 +20,13 @@
*/
#define GICD_CTLR 0x0000
#define GICD_CTLR_RWP (1U << 31)
+#define GICD_CTLR_nASSGIreq (1U << 8)
#define GICD_CTLR_ARE_NS (1U << 4)
#define GICD_CTLR_ENABLE_G1A (1U << 1)
#define GICD_CTLR_ENABLE_G1 (1U << 0)
+#define GICD_TYPER2_nASSGIcap (1U << 8)
+
/* Re-Distributor registers, offsets from RD_base */
#define GICR_TYPER 0x0008
diff --git a/lib/arm/asm/gic.h b/lib/arm/asm/gic.h
index 38e79b2..1898400 100644
--- a/lib/arm/asm/gic.h
+++ b/lib/arm/asm/gic.h
@@ -13,6 +13,7 @@
#define GICD_CTLR 0x0000
#define GICD_TYPER 0x0004
#define GICD_IIDR 0x0008
+#define GICD_TYPER2 0x000C
#define GICD_IGROUPR 0x0080
#define GICD_ISENABLER 0x0100
#define GICD_ICENABLER 0x0180
diff --git a/lib/arm/asm/timer.h b/lib/arm/asm/timer.h
new file mode 100644
index 0000000..f75cc67
--- /dev/null
+++ b/lib/arm/asm/timer.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#ifndef _ASMARM_TIMER_H_
+#define _ASMARM_TIMER_H_
+
+#define ARCH_TIMER_CTL_ENABLE (1 << 0)
+#define ARCH_TIMER_CTL_IMASK (1 << 1)
+#define ARCH_TIMER_CTL_ISTATUS (1 << 2)
+
+#ifndef __ASSEMBLY__
+
+struct timer_state {
+ struct {
+ u32 irq;
+ u32 irq_flags;
+ } ptimer;
+ struct {
+ u32 irq;
+ u32 irq_flags;
+ } vtimer;
+};
+extern struct timer_state __timer_state;
+
+#define TIMER_PTIMER_IRQ (__timer_state.ptimer.irq)
+#define TIMER_VTIMER_IRQ (__timer_state.vtimer.irq)
+
+#endif /* !__ASSEMBLY__ */
+#endif /* _ASMARM_TIMER_H_ */
diff --git a/lib/arm/setup.c b/lib/arm/setup.c
index 418b4e5..78562e4 100644
--- a/lib/arm/setup.c
+++ b/lib/arm/setup.c
@@ -22,6 +22,7 @@
#include <asm/page.h>
#include <asm/processor.h>
#include <asm/smp.h>
+#include <asm/timer.h>
#include "io.h"
@@ -29,6 +30,8 @@
extern unsigned long stacktop;
+struct timer_state __timer_state;
+
char *initrd;
u32 initrd_size;
@@ -156,6 +159,43 @@
page_alloc_ops_enable();
}
+static void timer_save_state(void)
+{
+ const struct fdt_property *prop;
+ const void *fdt = dt_fdt();
+ int node, len;
+ u32 *data;
+
+ node = fdt_node_offset_by_compatible(fdt, -1, "arm,armv8-timer");
+ assert(node >= 0 || node == -FDT_ERR_NOTFOUND);
+
+ if (node == -FDT_ERR_NOTFOUND) {
+ __timer_state.ptimer.irq = -1;
+ __timer_state.vtimer.irq = -1;
+ return;
+ }
+
+ /*
+ * From Linux devicetree timer binding documentation
+ *
+ * interrupts <type irq flags>:
+ * secure timer irq
+ * non-secure timer irq (ptimer)
+ * virtual timer irq (vtimer)
+ * hypervisor timer irq
+ */
+ 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 /* PPI */);
+ __timer_state.ptimer.irq = fdt32_to_cpu(data[4]);
+ __timer_state.ptimer.irq_flags = fdt32_to_cpu(data[5]);
+ assert(fdt32_to_cpu(data[6]) == 1 /* PPI */);
+ __timer_state.vtimer.irq = fdt32_to_cpu(data[7]);
+ __timer_state.vtimer.irq_flags = fdt32_to_cpu(data[8]);
+}
+
void setup(const void *fdt)
{
void *freemem = &stacktop;
@@ -211,6 +251,8 @@
io_init();
/* finish setup */
+ timer_save_state();
+
ret = dt_get_bootargs(&bootargs);
assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
setup_args_progname(bootargs);
diff --git a/lib/arm64/asm/timer.h b/lib/arm64/asm/timer.h
new file mode 100644
index 0000000..c0f5f88
--- /dev/null
+++ b/lib/arm64/asm/timer.h
@@ -0,0 +1 @@
+#include "../../arm/asm/timer.h"
diff --git a/lib/arm64/gic-v3-its-cmd.c b/lib/arm64/gic-v3-its-cmd.c
index 2c208d1..34574f7 100644
--- a/lib/arm64/gic-v3-its-cmd.c
+++ b/lib/arm64/gic-v3-its-cmd.c
@@ -164,8 +164,9 @@
{
struct its_cmd_block *cmd;
- assert((u64)its_data.cmd_write < (u64)its_data.cmd_base + SZ_64K);
cmd = its_data.cmd_write++;
+ if ((u64)its_data.cmd_write == (u64)its_data.cmd_base + SZ_64K)
+ its_data.cmd_write = its_data.cmd_base;
return cmd;
}