Hi,

On Fri, May 01, 2026 at 03:21:19PM +0100, Alex Bennée wrote:
> Alexandru Elisei <[email protected]> writes:
> 
> > Hi Alex,
> >
> > On Mon, Apr 27, 2026 at 02:00:45PM +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 <[email protected]>
> >> ---
> >>  arm/Makefile.arm64        |   1 +
> >>  lib/arm64/asm/processor.h |   7 ++
> >>  lib/arm64/asm/sysreg.h    |   3 +
> >>  arm/wfx.c                 | 137 ++++++++++++++++++++++++++++++++++++++
> >>  arm/unittests.cfg         |   5 ++
> >>  5 files changed, 153 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..912e50e6
> >> --- /dev/null
> >> +++ b/arm/wfx.c
> >> @@ -0,0 +1,137 @@
> >> +/*
> >> + * 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 TIMEOUT 200000
> >> +
> >> +#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")
> >> +
> >> +static void timer_handler(struct pt_regs *regs)
> >> +{
> >> +  /* Disable timer to stop IRQ from re-firing */
> >> +  write_sysreg(0, cntv_ctl_el0);
> >> +}
> >
> > It is customary for the interrupt handler to ack the interrupt at the GIC 
> > level.
> >
> >> +
> >> +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)", test, elapsed);
> >> +
> >> +  if (!pass) {
> >> +          report_info("%s %s", test, more ? "woke too early" : "slept 
> >> despite SEV");
> >> +  }
> >> +  return pass;
> >> +}
> >> +
> >> +static void test_wfi(void)
> >> +{
> >> +  uint64_t start;
> >> +
> >> +  report_info("Testing WFI...");
> >> +
> >> +  start = read_sysreg(cntvct_el0);
> >> +  write_sysreg(TIMEOUT, cntv_tval_el0);
> >> +  write_sysreg(1, cntv_ctl_el0); /* Enable timer, no mask */
> >> +  isb();
> >> +
> >> +  local_irq_enable();
> >> +  wfi();
> >
> > Consider this scenario:
> >
> >     local_irq_enable();
> >     // CPU takes the interrupt, handles it and returns.
> >     wfi(); <- CPU now waits forever for an interrupt that will never come
> >
> > The proper way to do it on baremetal would be:
> >
> >     local_irq_disable();
> >     // Program timer to fire
> >     wfi();
> >     // Timer fires, GIC asserts the interrupt, WFI completes
> >     local_irq_enable();
> >     // CPU handles the interrupt
> >     check_elapsed(..)
> >
> > But this is not baremetal. KVM can decide not to trap WFI (look at
> > arch/arm64/kvm/arm.c::kvm_arch_vcpu_load() -> kvm_vpcu_should_clear_twi()). 
> > In
> > that case, the WFI might complete due a host interrupt, and check_elapsed() 
> > will
> > fail because the timer hasn't yet fired.
> >
> >> +  local_irq_disable();
> >> +
> >> +  check_elapsed(start, TIMEOUT, "WFI", true);
> >> +}
> >> +
> >> +static void test_wfe(void)
> >> +{
> >> +  uint64_t start;
> >> +
> >> +  report_info("Testing WFE/SEV...");
> >> +  sev();
> >> +  start = read_sysreg(cntvct_el0);
> >> +  wfe();
> >> +  check_elapsed(start, TIMEOUT, "WFE/SEV", false);
> >> +
> >> +  report_info("Testing WFE/SEVL...");
> >> +  sevl();
> >> +  start = read_sysreg(cntvct_el0);
> >> +  wfe();
> >> +  check_elapsed(start, TIMEOUT, "WFE/SEVL", false);
> >> +}
> >
> > I haven't thought about this too much, but it looks to me like the same
> > situation with WFI can happen with WFE
> >
> > Also, kvm-unit-tests() makes use of WFE and SEV for multithreading, and the 
> > fact
> > that multithreaded tests work at all might be taken as proof enough that 
> > the two
> > instructions are correctly implemented.
> 
> So I'm using kvm-unit-test to exercise QEMU's TCG modelling of the
> instructions, see:
> 
>   Message-ID: <[email protected]>
>   Date: Thu, 30 Apr 2026 11:44:27 +0100
>   Subject: [PATCH v4 0/7] target/arm: fully model WFxT instructions for 
> A-profile
> 
> So while I'm confident the current modelling doesn't cause issues
> (because we basically treat it as a NOP) I wanted to check the various
> modes behave as they should with the above patches.
> 
> I can limit the test to TCG only if it is likely to fail under KVM.

I think that would be best. But please also ack the interrupt in the timer
IRQ handler, as that is what software would do (you can have a look at
arm/timer.c::irq_handler() for an example).

Thanks,
Alex

Reply via email to