This commit adds a new system-mode TCG test to verify the behavior of
WFI, WFE, WFIT, and WFET instructions on AArch64.
The test ensures:
- WFI correctly wakes on a timer interrupt.
- WFE returns immediately if the event register is set (via SEV).
- WFIT and WFET correctly sleep until the specified timeout.
🤖 Generated with [eca](https://eca.dev)
Co-Authored-By: eca <noreply@eca.dev>
---
ajb:
- removed an excess setting of QEMU_OPTS in the Makefile
---
tests/tcg/aarch64/system/wfx.c | 113 ++++++++++++++++++++++
tests/tcg/aarch64/Makefile.softmmu-target | 2 +
2 files changed, 115 insertions(+)
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..59436c381fd
--- /dev/null
+++ b/tests/tcg/aarch64/system/wfx.c
@@ -0,0 +1,113 @@
+/*
+ * 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>
+
+#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")
+
+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;
+
+ 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.
+ */
+ wfi();
+ 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;
+ if (elapsed > 1000) { /* Should be very fast */
+ ml_printf("FAILED: WFE slept despite SEV (%ld ticks)\n", elapsed);
+ return 1;
+ }
+ ml_printf("PASSED\n");
+
+ /* 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 f7a7d2b800f..84342c52cd7 100644
--- a/tests/tcg/aarch64/Makefile.softmmu-target
+++ b/tests/tcg/aarch64/Makefile.softmmu-target
@@ -102,6 +102,8 @@ run-pauth-3:
$(call skip-test, "RUN of pauth-3", "not built")
endif
+wfx: CFLAGS += -march=armv8.7-a
+
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
--
2.47.3