[RFC PATCH 35/35] tests/tcg: add basic test for aarch64 wf[ie][t] insns

Alex Bennée posted 35 patches 7 hours ago
Maintainers: Peter Maydell <peter.maydell@linaro.org>, Alexander Graf <agraf@csgraf.de>, Pedro Barbuda <pbarbuda@microsoft.com>, Mohamed Mediouni <mohamed@unpredictable.fr>
[RFC PATCH 35/35] tests/tcg: add basic test for aarch64 wf[ie][t] insns
Posted by Alex Bennée 7 hours ago
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 tests/tcg/aarch64/system/wfx.c            | 126 ++++++++++++++++++++++
 tests/tcg/aarch64/Makefile.softmmu-target |   8 ++
 tests/tcg/aarch64/system/boot.S           |  12 ++-
 3 files changed, 145 insertions(+), 1 deletion(-)
 create mode 100644 tests/tcg/aarch64/system/wfx.c

diff --git a/tests/tcg/aarch64/system/wfx.c b/tests/tcg/aarch64/system/wfx.c
new file mode 100644
index 00000000000..567d9e59c70
--- /dev/null
+++ b/tests/tcg/aarch64/system/wfx.c
@@ -0,0 +1,126 @@
+/*
+ * WFX Instructions Test (WFI, WFE, WFIT, WFET)
+ *
+ * Copyright (c) 2024 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdint.h>
+#include <minilib.h>
+#include "gicv3.h"
+
+#define __stringify_1(x...) #x
+#define __stringify(x...)   __stringify_1(x)
+
+#define read_sysreg(r) ({                                           \
+            uint64_t __val;                                         \
+            asm volatile("mrs %0, " __stringify(r) : "=r" (__val)); \
+            __val;                                                  \
+})
+
+#define write_sysreg(r, v) do {                     \
+        uint64_t __val = (uint64_t)(v);             \
+        asm volatile("msr " __stringify(r) ", %x0"  \
+                 : : "rZ" (__val));                 \
+} while (0)
+
+#define isb() asm volatile("isb" : : : "memory")
+#define sev() asm volatile("sev" : : : "memory")
+#define wfi() asm volatile("wfi" : : : "memory")
+#define wfe() asm volatile("wfe" : : : "memory")
+#define wfit(reg) asm volatile("wfit %0" : : "r" (reg) : "memory")
+#define wfet(reg) asm volatile("wfet %0" : : "r" (reg) : "memory")
+
+#define enable_irq()  asm volatile("msr daifclr, #2" : : : "memory")
+#define disable_irq() asm volatile("msr daifset, #2" : : : "memory")
+
+static void wait_ticks(uint64_t ticks)
+{
+    uint64_t start = read_sysreg(cntvct_el0);
+    while ((read_sysreg(cntvct_el0) - start) < ticks) {
+        /* spin */
+    }
+}
+
+int main(void)
+{
+    uint64_t start, end, elapsed;
+    uint64_t timeout;
+
+    gicv3_init();
+    gicv3_enable_irq(27); /* Virtual Timer PPI */
+
+    ml_printf("WFX Test\n");
+
+    /* 1. Test WFI with timer interrupt */
+    ml_printf("Testing WFI...");
+    /* Setup virtual timer to fire in 100000 ticks (~2ms at 50MHz) */
+    start = read_sysreg(cntvct_el0);
+    write_sysreg(cntv_tval_el0, 100000);
+    write_sysreg(cntv_ctl_el0, 1); /* Enable timer, no mask */
+    isb();
+
+    /*
+     * We don't have a full interrupt handler, but WFI should wake up
+     * when the interrupt is pending even if we have it masked at the CPU.
+     * PSTATE.I is set by boot code.
+     *
+     * We unmask interrupts here to ensure the CPU can take the minimal
+     * exception handler defined in boot.S.
+     */
+    enable_irq();
+    wfi();
+    disable_irq();
+    end = read_sysreg(cntvct_el0);
+    elapsed = end - start;
+    if (elapsed < 100000) {
+        ml_printf("FAILED: WFI woke too early (%ld ticks)\n", elapsed);
+        return 1;
+    }
+    ml_printf("PASSED (elapsed %ld ticks)\n", elapsed);
+    write_sysreg(cntv_ctl_el0, 0); /* Disable timer */
+
+    /* 2. Test WFE and SEV */
+    ml_printf("Testing WFE/SEV...");
+    sev(); /* Set event register */
+    start = read_sysreg(cntvct_el0);
+    wfe(); /* Should return immediately */
+    end = read_sysreg(cntvct_el0);
+    elapsed = end - start;
+    /* while this should be fast there is some overhead from TCG */
+    if (elapsed > 20000) {
+        ml_printf("FAILED: WFE slept despite SEV (%ld ticks)\n", elapsed);
+        return 1;
+    }
+    ml_printf("PASSED (%ld ticks)\n", elapsed);
+
+    /* 3. Test WFIT */
+    ml_printf("Testing WFIT...");
+    start = read_sysreg(cntvct_el0);
+    timeout = start + 200000;
+    wfit(timeout);
+    end = read_sysreg(cntvct_el0);
+    elapsed = end - start;
+    if (elapsed < 200000) {
+        ml_printf("FAILED: WFIT woke too early (%ld ticks)\n", elapsed);
+        return 1;
+    }
+    ml_printf("PASSED (elapsed %ld ticks)\n", elapsed);
+
+    /* 4. Test WFET */
+    ml_printf("Testing WFET...");
+    start = read_sysreg(cntvct_el0);
+    timeout = start + 200000;
+    wfet(timeout);
+    end = read_sysreg(cntvct_el0);
+    elapsed = end - start;
+    if (elapsed < 200000) {
+        ml_printf("FAILED: WFET woke too early (%ld ticks)\n", elapsed);
+        return 1;
+    }
+    ml_printf("PASSED (elapsed %ld ticks)\n", elapsed);
+
+    ml_printf("ALL WFX TESTS PASSED\n");
+    return 0;
+}
diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target
index c0939a0eeca..9a5b95de621 100644
--- a/tests/tcg/aarch64/Makefile.softmmu-target
+++ b/tests/tcg/aarch64/Makefile.softmmu-target
@@ -105,6 +105,14 @@ endif
 
 gicv3.o: gicv3.c gicv3.h
 	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@
+
+wfx: CFLAGS += -march=armv8.7-a
+wfx: LDFLAGS += gicv3.o
+wfx: gicv3.o
+
+QEMU_GICV3_MACHINE=-M virt,gic-version=3 -cpu max -display none
+run-wfx: QEMU_OPTS=$(QEMU_GICV3_MACHINE) $(QEMU_BASE_ARGS) -kernel
+
 ifneq ($(CROSS_CC_HAS_ARMV8_MTE),)
 QEMU_MTE_ENABLED_MACHINE=-M virt,mte=on -cpu max -display none
 QEMU_OPTS_WITH_MTE_ON = $(QEMU_MTE_ENABLED_MACHINE) $(QEMU_BASE_ARGS) -kernel
diff --git a/tests/tcg/aarch64/system/boot.S b/tests/tcg/aarch64/system/boot.S
index 03a5bad2ab0..6a71fc0da5a 100644
--- a/tests/tcg/aarch64/system/boot.S
+++ b/tests/tcg/aarch64/system/boot.S
@@ -60,7 +60,6 @@ curr_sp0_irq:
 curr_sp0_fiq:
 curr_sp0_serror:
 curr_spx_sync:
-curr_spx_irq:
 curr_spx_fiq:
 curr_spx_serror:
 lower_a64_sync:
@@ -379,6 +378,17 @@ _exit:
 	semihosting_call
 	/* never returns */
 
+	/*
+	 * IRQ handler
+	 */
+	.global curr_spx_irq
+curr_spx_irq:
+	/* Minimal IRQ handler: just mask the timer and return */
+	mrs	x0, cntv_ctl_el0
+	orr	x0, x0, #2		/* IMASK=1 */
+	msr	cntv_ctl_el0, x0
+	eret
+
 	/*
 	 * Helper Functions
 	*/
-- 
2.47.3