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 <[email protected]> --- 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
