Kernel KVM virtualization development
 help / color / mirror / Atom feed
From: Alexandru Elisei <alexandru.elisei@arm.com>
To: "Alex Bennée" <alex.bennee@linaro.org>
Cc: qemu-devel@nongnu.org, Andrew Jones <andrew.jones@linux.dev>,
	Eric Auger <eric.auger@redhat.com>,
	"open list:ARM" <kvmarm@lists.linux.dev>,
	"open list:Default mailing list" <kvm@vger.kernel.org>
Subject: Re: [kvm-unit-tests PATCH v2] arm: add wfx test case
Date: Fri, 29 May 2026 15:12:32 +0100	[thread overview]
Message-ID: <ahme0HOEcQW9Sok-@raptor> (raw)
In-Reply-To: <ahmdci3dLMyaaZn8@raptor>

Hi Alex,

On Fri, May 29, 2026 at 03:06:42PM +0100, Alexandru Elisei wrote:
> Hi Alex,
> 
> When I tried to run the test this is what I got:
> 
> $ ./run_tests.sh wfx
> FAIL wfx (5 tests, 1 unexpected failures)
> $ cat logs/wfx.log
> timeout -k 1s --foreground 90s /usr/bin/qemu-system-aarch64 -nodefaults -machine virt -accel tcg -cpu max -device virtio-serial-device -device virtconsole,chardev=ctd -chardev testdev,id=ctd -device pci-testdev -display none -serial stdio -kernel arm/wfx.flat -smp 1 # -initrd /tmp/tmp.s0dfiRPbNO
> INFO: WFx: Testing WFI...
> PASS: WFx: WFI (10441943 ticks / 10441 ns)
> INFO: WFx: Testing WFE/SEV...
> PASS: WFx: WFE/SEV (8636 ticks / 8 ns)
> INFO: WFx: Testing WFE/SEVL...
> PASS: WFx: WFE/SEVL (5461 ticks / 5 ns)
> INFO: WFxT: Testing WFIT...
> PASS: WFxT: WFIT (10135025 ticks / 10135 ns)
> INFO: WFxT: Testing WFET...
> FAIL: WFxT: WFET (19391 ticks / 19 ns)
> INFO: WFxT: WFET woke too early
> SUMMARY: 5 tests, 1 unexpected failures
> 
> EXIT: STATUS=3
> 
> $ /usr/bin/qemu-system-aarch64 --version
> QEMU emulator version 11.0.0
> Copyright (c) 2003-2026 Fabrice Bellard and the QEMU Project developers
> 
> Any idea what I might be doing wrong?
> 
> On Wed, May 27, 2026 at 12:18:21PM +0100, Alex Bennée wrote:
> > This is based on a similar test case I wrote for QEMU's tcg tests although
> > obviously able to take advantage of kvm-unit-tests additional plumbing for
> > dealing with the GIC and IRQs.
> > 
> > Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> > 
> > ---
> > v2
> >   - drop TIMEOUT in favour of computed 10ms timeout
> >   - add comments for each test
> >   - use sevl for local clearing
> >   - set gic irc depending on CurrentEL
> >   - EOI the GIC
> >   - display elapsed ns (no float to do ms ;-)
> >   - fix some style issues
> >   - make test TCG only
> > ---
> >  arm/Makefile.arm64        |   1 +
> >  lib/arm64/asm/processor.h |   7 ++
> >  lib/arm64/asm/sysreg.h    |   3 +
> >  arm/wfx.c                 | 187 ++++++++++++++++++++++++++++++++++++++
> >  arm/unittests.cfg         |   7 ++
> >  5 files changed, 205 insertions(+)
> >  create mode 100644 arm/wfx.c
> > 
> > diff --git a/arm/Makefile.arm64 b/arm/Makefile.arm64
> > index a40c830d..52b3f35d 100644
> > --- a/arm/Makefile.arm64
> > +++ b/arm/Makefile.arm64
> > @@ -64,6 +64,7 @@ tests += $(TEST_DIR)/cache.$(exe)
> >  tests += $(TEST_DIR)/debug.$(exe)
> >  tests += $(TEST_DIR)/fpu.$(exe)
> >  tests += $(TEST_DIR)/mte.$(exe)
> > +tests += $(TEST_DIR)/wfx.$(exe)
> >  
> >  include $(SRCDIR)/$(TEST_DIR)/Makefile.common
> >  
> > diff --git a/lib/arm64/asm/processor.h b/lib/arm64/asm/processor.h
> > index 32ddc1b3..2104036d 100644
> > --- a/lib/arm64/asm/processor.h
> > +++ b/lib/arm64/asm/processor.h
> > @@ -173,5 +173,12 @@ static inline bool system_supports_rndr(void)
> >  	return ((id_aa64isar0_el1 >> ID_AA64ISAR0_EL1_RNDR_SHIFT) & 0xf) != 0;
> >  }
> >  
> > +static inline bool system_supports_wfxt(void)
> > +{
> > +	u64 id_aa64isar2_el1 = read_sysreg_s(ID_AA64ISAR2_EL1);
> > +
> > +	return ((id_aa64isar2_el1 >> ID_AA64ISAR2_EL1_WFxT_SHIFT) & 0xf) != 0;
> > +}
> > +
> >  #endif /* !__ASSEMBLER__ */
> >  #endif /* _ASMARM64_PROCESSOR_H_ */
> > diff --git a/lib/arm64/asm/sysreg.h b/lib/arm64/asm/sysreg.h
> > index f2d05018..cb96a649 100644
> > --- a/lib/arm64/asm/sysreg.h
> > +++ b/lib/arm64/asm/sysreg.h
> > @@ -77,6 +77,9 @@ asm(
> >  #define ID_AA64ISAR0_EL1_RNDR_SHIFT	60
> >  #define ID_AA64PFR1_EL1_MTE_SHIFT	8
> >  
> > +#define ID_AA64ISAR2_EL1		sys_reg(3, 0, 0, 6, 2)
> > +#define ID_AA64ISAR2_EL1_WFxT_SHIFT	0
> > +
> >  #define ID_AA64MMFR0_EL1_FGT_SHIFT	56
> >  #define ID_AA64MMFR0_EL1_FGT_FGT2	0x2
> >  
> > diff --git a/arm/wfx.c b/arm/wfx.c
> > new file mode 100644
> > index 00000000..0825c5cb
> > --- /dev/null
> > +++ b/arm/wfx.c
> > @@ -0,0 +1,187 @@
> > +/*
> > + * WFX Instructions Test (WFI, WFE, WFIT, WFET)
> > + *
> > + * Copyright (c) 2026 Linaro Ltd
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + */
> > +
> > +#include <libcflat.h>
> > +#include <asm/processor.h>
> > +#include <asm/gic.h>
> > +#include <asm/timer.h>
> > +#include <asm/io.h>
> > +
> > +#define sev() asm volatile("sev" : : : "memory")
> > +#define sevl() asm volatile("sevl" : : : "memory")
> > +#define wfi() asm volatile("wfi" : : : "memory")
> > +#define wfe() asm volatile("wfe" : : : "memory")
> > +
> > +#define wfit(reg) \
> > +	asm volatile(".arch armv8.7-a\n\twfit %0" : : "r" (reg) : "memory")
> > +#define wfet(reg) \
> > +	asm volatile(".arch armv8.7-a\n\twfet %0" : : "r" (reg) : "memory")
> > +
> > +uint64_t time_hz;
> > +uint64_t time_10ms;
> > +
> > +static void timer_handler(struct pt_regs *regs)
> > +{
> > +	u32 irqstat = gic_read_iar();
> > +	u32 irqnr = gic_iar_irqnr(irqstat);
> > +
> > +	/* Disable timer to stop IRQ from re-firing */
> > +	if (irqnr == TIMER_VTIMER_IRQ || irqnr == TIMER_HVTIMER_IRQ) {
> > +		write_sysreg(0, cntv_ctl_el0);
> > +		isb();
> > +	} else {
> > +		if (irqnr != GICC_INT_SPURIOUS)
> > +			gic_write_eoir(irqstat);
> > +		report_info("Unexpected interrupt: %d\n", irqnr);
> > +		return;
> > +	}
> > +
> > +	/* Also acknowledge EOI for the GIC */
> > +	gic_write_eoir(irqstat);
> > +}
> > +
> > +static bool check_elapsed(uint64_t start, uint64_t threshold, const char *test, bool more)
> > +{
> > +	uint64_t end = read_sysreg(cntvct_el0);
> > +	uint64_t elapsed = end - start;
> > +	bool pass = more ? elapsed >= threshold : elapsed <= threshold;
> > +
> > +	report(pass, "%s (%ld ticks / %ld ns)",
> > +	       test, elapsed, elapsed / (time_hz / 1000000));
> > +
> > +	if (!pass)
> > +		report_info("%s %s", test, more ? "woke too early" : "slept despite SEV");
> > +
> > +	return pass;
> > +}
> > +
> > +/*
> > + * Test WFI with timer interrupt
> > + *
> > + * WFI should wake up when the interrupt is pending. We unmask
> > + * interrupts here so they are taken and then check WFI actually
> > + * did sleep rather than return straight away.
> > + */
> > +static void test_wfi(void)
> > +{
> > +	uint64_t start;
> > +
> > +	report_info("Testing WFI...");
> > +
> > +	start = read_sysreg(cntvct_el0);
> > +	write_sysreg(time_10ms, cntv_tval_el0);
> > +	write_sysreg(1, cntv_ctl_el0); /* Enable timer, no mask */
> > +	isb();
> > +
> > +	local_irq_enable();
> 
> There's nothing to guarantee that the CPU won't handle the interrupt between
> local_irq_enable() above and the wfi() below, thus guaranteeing that the WFI
> will never complete.
> 
> The Arm ARM says that WFI will complete if there is a pending interrupt even if
> interrupts are **disabled** for the PE; this is also how Linux implements idle
> (it disables interrupt on the PE before doing WFI).
> 
> > +	wfi();
> > +	local_irq_disable();
> 
> So this code should probably be (untested):
> 
> diff --git a/arm/wfx.c b/arm/wfx.c
> index 0825c5cbfcec..522edd36af38 100644
> --- a/arm/wfx.c
> +++ b/arm/wfx.c
> @@ -78,8 +78,8 @@ static void test_wfi(void)
>         write_sysreg(1, cntv_ctl_el0); /* Enable timer, no mask */
>         isb();
> 
> -       local_irq_enable();
>         wfi();
> +       local_irq_enable();
>         local_irq_disable();
> 
>         check_elapsed(start, time_10ms, "WFI", true);
> 
> That is assuming that you want the timer interrupt handler to run, though it
> doesn't look to me like the interrupt handler is very useful from a testing
> perspective (i.e, the tests never check that the correct timer interrupt was
> asserted, or even that the timer interrupt fired at all).
> 
> If you don't care about the timer interrupt handler running, you can skip
> enabling the interrupt altogether, like the wfit test does; you know better than

Sorry, this was sloppy wording from my part, what I mean is that '[..] you
can skip enabling local interrupts (local_irq_enable()) altogether.'

Thanks,
Alex

> me what would be better.
> 
> > +
> > +	check_elapsed(start, time_10ms, "WFI", true);
> > +}
> > +
> > +/*
> > + * Test WFE and SEV[L]
> > + *
> > + * There are two SEV instructions, the normal one is a broadcast
> > + * from any PE on the system, the other is local only.
> > + * Functionally they have the same effect (setting the event
> > + * register) and should be immediately consumed by the WFE.
> > + *
> > + * As we want to detect an early exit the sense of the timeout
> > + * check is reversed.
> > + */
> > +static void test_wfe(void)
> > +{
> > +	uint64_t start;
> > +
> > +	report_info("Testing WFE/SEV...");
> > +	sev();
> > +	start = read_sysreg(cntvct_el0);
> > +	wfe();
> > +	check_elapsed(start, time_10ms, "WFE/SEV", false);
> > +
> > +	report_info("Testing WFE/SEVL...");
> > +	sevl();
> > +	start = read_sysreg(cntvct_el0);
> > +	wfe();
> > +	check_elapsed(start, time_10ms, "WFE/SEVL", false);
> > +}
> > +
> > +/*
> > + * Test WFIT
> > + *
> > + * With the timer disabled and no other IRQ sources firing the
> > + * WFIT instruction should timeout.
> > + */
> > +static void test_wfit(void)
> > +{
> > +	uint64_t start, timeout;
> > +
> > +	report_info("Testing WFIT...");
> > +	start = read_sysreg(cntvct_el0);
> > +	timeout = start + time_10ms;
> > +	wfit(timeout);
> > +	check_elapsed(start, time_10ms, "WFIT", true);
> > +}
> > +
> > +/*
> > + * Test WFET
> > + *
> > + * Much like WFIT there are no IRQs to wake us up. However the
> > + * event_register is a latch so we must first consume the event
> > + * register with a normal WFE before we do the timeout version.
> > + */
> > +static void test_wfet(void)
> > +{
> > +	uint64_t start, timeout;
> > +
> > +	report_info("Testing WFET...");
> > +	/* Ensure no pending events */
> > +	sevl();
> > +	wfe();
> > +
> > +	start = read_sysreg(cntvct_el0);
> > +	timeout = start + time_10ms;
> > +	wfet(timeout);
> > +	check_elapsed(start, time_10ms, "WFET", true);
> > +}
> > +
> > +int main(void)
> > +{
> > +	uint32_t irq = current_level() == CurrentEL_EL1 ? TIMER_VTIMER_IRQ : TIMER_HVTIMER_IRQ;
> > +
> > +	if (gic_init() < 0) {
> > +		report_abort("GIC init failed");
> > +		return 1;
> > +	}
> > +
> > +	/* Install timer handler for WFI wake-up */
> > +	install_irq_handler(EL1H_IRQ, timer_handler);
> > +	gic_enable_defaults();
> > +	time_hz = read_sysreg(cntfrq_el0);
> > +	time_10ms = time_hz / 100;
> > +
> > +	/* Enable Virtual Timer PPI */
> > +	gic_irq_set_clr_enable(irq, true);
> > +
> > +	report_prefix_push("WFx");
> > +	test_wfi();
> > +	test_wfe();
> > +	report_prefix_pop();
> > +
> > +	if (system_supports_wfxt()) {
> > +		report_prefix_push("WFxT");
> > +		test_wfit();
> > +		test_wfet();
> > +	} else {
> > +		report_skip("WFxT instructions not supported");
> > +	}
> 
> I think I'm nitpicking here, and I didn't test it. If the system supports WFxT
> the SUMMARY line shows 4 tests, but if the system doesn't have WFxT, the SUMMARY
> line shows 2 tests, and 1 skipped test, in total 3 tests, not 4. Might be
> confusing to users.
> 
> Thanks,
> Alex
> 
> > +
> > +	return report_summary();
> > +}
> > diff --git a/arm/unittests.cfg b/arm/unittests.cfg
> > index 12fc4468..ae8b5534 100644
> > --- a/arm/unittests.cfg
> > +++ b/arm/unittests.cfg
> > @@ -339,3 +339,10 @@ groups = mte
> >  test_args = asymm
> >  qemu_params = -machine mte=on
> >  arch = arm64
> > +
> > +[wfx]
> > +file = wfx.flat
> > +groups = wfx
> > +arch = arm64
> > +# This test exercise CPU emulation so limit to TCG to avoid confusion
> > +accel = tcg
> > -- 
> > 2.47.3
> > 
> 

      reply	other threads:[~2026-05-29 14:12 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-27 11:18 [kvm-unit-tests PATCH v2] arm: add wfx test case Alex Bennée
2026-05-28 16:23 ` Richard Henderson
2026-05-29 14:06 ` Alexandru Elisei
2026-05-29 14:12   ` Alexandru Elisei [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ahme0HOEcQW9Sok-@raptor \
    --to=alexandru.elisei@arm.com \
    --cc=alex.bennee@linaro.org \
    --cc=andrew.jones@linux.dev \
    --cc=eric.auger@redhat.com \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.linux.dev \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox