From: Marc Zyngier <maz@kernel.org>
To: Zenghui Yu <yuzenghui@huawei.com>
Cc: <kvmarm@lists.linux.dev>, <kvm@vger.kernel.org>,
<linux-arm-kernel@lists.infradead.org>,
James Morse <james.morse@arm.com>,
Suzuki K Poulose <suzuki.poulose@arm.com>,
Oliver Upton <oliver.upton@linux.dev>,
Ricardo Koller <ricarkol@google.com>,
Simon Veith <sveith@amazon.de>,
Reiji Watanabe <reijiw@google.com>,
Colton Lewis <coltonlewis@google.com>,
Joey Gouly <joey.gouly@arm.com>, <dwmw2@infradead.org>
Subject: Re: [PATCH v4 05/20] KVM: arm64: timers: Allow physical offset without CNTPOFF_EL2
Date: Sat, 19 Jul 2025 13:04:43 +0100 [thread overview]
Message-ID: <87zfd0e690.wl-maz@kernel.org> (raw)
In-Reply-To: <460258be-0102-e922-c342-4e87cd94b9e5@huawei.com>
On Wed, 09 Jul 2025 09:12:18 +0100,
Zenghui Yu <yuzenghui@huawei.com> wrote:
>
> [ Record some interesting bits noticed while testing
> arch_timer_edge_cases. ]
>
> On 2023/3/31 1:47, Marc Zyngier wrote:
> > CNTPOFF_EL2 is awesome, but it is mostly vapourware, and no publicly
> > available implementation has it. So for the common mortals, let's
> > implement the emulated version of this thing.
> >
> > It means trapping accesses to the physical counter and timer, and
> > emulate some of it as necessary.
> >
> > As for CNTPOFF_EL2, nobody sets the offset yet.
> >
> > Reviewed-by: Colton Lewis <coltonlewis@google.com>
> > Signed-off-by: Marc Zyngier <maz@kernel.org>
> > ---
> > arch/arm64/include/asm/sysreg.h | 2 +
> > arch/arm64/kvm/arch_timer.c | 98 +++++++++++++++++++++++-------
> > arch/arm64/kvm/hyp/nvhe/timer-sr.c | 18 ++++--
> > arch/arm64/kvm/sys_regs.c | 9 +++
> > 4 files changed, 98 insertions(+), 29 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> > index 9e3ecba3c4e6..f8da9e1b0c11 100644
> > --- a/arch/arm64/include/asm/sysreg.h
> > +++ b/arch/arm64/include/asm/sysreg.h
> > @@ -388,6 +388,7 @@
> >
> > #define SYS_CNTFRQ_EL0 sys_reg(3, 3, 14, 0, 0)
> >
> > +#define SYS_CNTPCT_EL0 sys_reg(3, 3, 14, 0, 1)
> > #define SYS_CNTPCTSS_EL0 sys_reg(3, 3, 14, 0, 5)
> > #define SYS_CNTVCTSS_EL0 sys_reg(3, 3, 14, 0, 6)
> >
> > @@ -400,6 +401,7 @@
> >
> > #define SYS_AARCH32_CNTP_TVAL sys_reg(0, 0, 14, 2, 0)
> > #define SYS_AARCH32_CNTP_CTL sys_reg(0, 0, 14, 2, 1)
> > +#define SYS_AARCH32_CNTPCT sys_reg(0, 0, 0, 14, 0)
> > #define SYS_AARCH32_CNTP_CVAL sys_reg(0, 2, 0, 14, 0)
> >
> > #define __PMEV_op2(n) ((n) & 0x7)
> > diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c
> > index 3118ea0a1b41..bb64a71ae193 100644
> > --- a/arch/arm64/kvm/arch_timer.c
> > +++ b/arch/arm64/kvm/arch_timer.c
> > @@ -458,6 +458,8 @@ static void timer_save_state(struct arch_timer_context *ctx)
> > goto out;
> >
> > switch (index) {
> > + u64 cval;
> > +
> > case TIMER_VTIMER:
> > timer_set_ctl(ctx, read_sysreg_el0(SYS_CNTV_CTL));
> > timer_set_cval(ctx, read_sysreg_el0(SYS_CNTV_CVAL));
> > @@ -485,7 +487,12 @@ static void timer_save_state(struct arch_timer_context *ctx)
> > break;
> > case TIMER_PTIMER:
> > timer_set_ctl(ctx, read_sysreg_el0(SYS_CNTP_CTL));
> > - timer_set_cval(ctx, read_sysreg_el0(SYS_CNTP_CVAL));
> > + cval = read_sysreg_el0(SYS_CNTP_CVAL);
> > +
> > + if (!has_cntpoff())
> > + cval -= timer_get_offset(ctx);
> > +
> > + timer_set_cval(ctx, cval);
> >
> > /* Disable the timer */
> > write_sysreg_el0(0, SYS_CNTP_CTL);
> > @@ -555,6 +562,8 @@ static void timer_restore_state(struct arch_timer_context *ctx)
> > goto out;
> >
> > switch (index) {
> > + u64 cval, offset;
> > +
> > case TIMER_VTIMER:
> > set_cntvoff(timer_get_offset(ctx));
> > write_sysreg_el0(timer_get_cval(ctx), SYS_CNTV_CVAL);
> > @@ -562,8 +571,12 @@ static void timer_restore_state(struct arch_timer_context *ctx)
> > write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTV_CTL);
> > break;
> > case TIMER_PTIMER:
> > - set_cntpoff(timer_get_offset(ctx));
> > - write_sysreg_el0(timer_get_cval(ctx), SYS_CNTP_CVAL);
> > + cval = timer_get_cval(ctx);
> > + offset = timer_get_offset(ctx);
> > + set_cntpoff(offset);
> > + if (!has_cntpoff())
> > + cval += offset;
> > + write_sysreg_el0(cval, SYS_CNTP_CVAL);
> > isb();
> > write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTP_CTL);
> > break;
>
> I tested arch_timer_edge_cases on my Kunpeng920 (has VHE, doesn't have
> ECV) and noticed that the test_timer_cval() below takes a long period to
> return when testing the _physical_ timer.
>
> static void test_timers_in_the_past(enum arch_timer timer)
> {
> [...]
>
> for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) {
> irq_wait_method_t wm = irq_wait_method[i];
>
> [...]
>
> /* Set a timer to counter=0 (in the past) */
> test_timer_cval(timer, 0, wm, DEF_CNT);
>
> The comment is obviously wrong. It should say "Set a timer to CVAL=0".
>
> No physical timer interrupt ("kvm guest ptimer") was triggered when I
> executed it separately.
>
> Let me try to explain _this_ test_timer_cval() in a bit more detail.
>
> |Guest KVM
> |----- ---
> |local_irq_disable()
> |
> |reset_timer_state()
> | set_counter() // SET_ONE_REG via user-space
> | // for KVM_REG_ARM_PTIMER_CNT
> | kvm_arm_timer_set_reg()
> | timer_set_offset() [1]
> | timer_set_ctl()
> | MSR CNTP_CTL_EL0, IMASK
> |
> |set_xval_irq()
> | timer_set_cval()
> | MSR CNTP_CVAL_EL0, cval=0
> | timer_set_ctl()
> | MSR CNTP_CTL_EL0, ENABLE // trap
> | kvm_arm_timer_write_sysreg()
> | timer_save_state()
> | kvm_arm_timer_write()
> | timer_restore_state() [2]
> |
> |/* This method re-enables IRQs to handle the one we're looking for. */
> |wm()
> |
> |assert_irqs_handled(1)
> |local_irq_enable()
>
> [1] kvm_phys_timer_read() = 0x7895c0ab2
> value = 0x7fffffffffffff
> offset = kvm_phys_timer_read() - value = 0xff800007895c0ab3
>
> ... which was observed in my test.
I think this denotes the same problem that was "fixed" in this test
recently: the arithmetic in the kernel is done on a 64bit value, while
we have no idea what the number of significant bits actually is. We
therefore end-up with some outlandish values, and since the timer is
more or less emulated, we end-up doing the wrong thing.
>
> [2] cval += offset; // cval = 0xff800007895c0ab3
> kvm_phys_timer_read() = 0x7895c1b86
>
> No ptimer interrupt was triggered with that. And we relied on the next
> kvm_timer_vcpu_load() to catch the timer expiration and inject an
> interrupt to the guest..
>
> It's apparent that this test case is not a practical use case. Not sure
> if we should (/can) emulate it properly. Or I may have already missed
> some obvious points.
Overall, I think the "set a counter value to compute an offset"
paradigm isn't really practical when we get to the limits. At least,
the VM_COUNTER_OFFSET approach is consistent, and avoids the above
calculations.
I really wish someone would teach QEMU to use it (and maybe even add a
test for it...).
Thanks,
M.
--
Jazz isn't dead. It just smells funny.
next prev parent reply other threads:[~2025-07-19 12:04 UTC|newest]
Thread overview: 52+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-03-30 17:47 [PATCH v4 00/20] KVM: arm64: Rework timer offsetting for fun and profit Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 01/20] KVM: arm64: timers: Use a per-vcpu, per-timer accumulator for fractional ns Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 02/20] arm64: Add CNTPOFF_EL2 register definition Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 03/20] arm64: Add HAS_ECV_CNTPOFF capability Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 04/20] KVM: arm64: timers: Use CNTPOFF_EL2 to offset the physical timer Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-04-10 15:48 ` Reiji Watanabe
2023-04-10 15:48 ` Reiji Watanabe
2023-03-30 17:47 ` [PATCH v4 05/20] KVM: arm64: timers: Allow physical offset without CNTPOFF_EL2 Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-04-10 15:34 ` Reiji Watanabe
2023-04-10 15:34 ` Reiji Watanabe
2023-04-13 12:47 ` Marc Zyngier
2023-04-13 12:47 ` Marc Zyngier
2023-04-17 3:34 ` Reiji Watanabe
2023-04-17 3:34 ` Reiji Watanabe
2025-07-09 8:12 ` Zenghui Yu
2025-07-19 12:04 ` Marc Zyngier [this message]
2023-03-30 17:47 ` [PATCH v4 06/20] KVM: arm64: Expose {un,}lock_all_vcpus() to the rest of KVM Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 07/20] KVM: arm64: timers: Allow userspace to set the global counter offset Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 08/20] KVM: arm64: timers: Allow save/restoring of the physical timer Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 09/20] KVM: arm64: timers: Rationalise per-vcpu timer init Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 10/20] KVM: arm64: timers: Abstract per-timer IRQ access Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 11/20] KVM: arm64: timers: Move the timer IRQs into arch_timer_vm_data Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 12/20] KVM: arm64: Elide kern_hyp_va() in VHE-specific parts of the hypervisor Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 13/20] KVM: arm64: timers: Fast-track CNTPCT_EL0 trap handling Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 14/20] KVM: arm64: timers: Abstract the number of valid timers per vcpu Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 15/20] KVM: arm64: Document KVM_ARM_SET_CNT_OFFSETS and co Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 16/20] KVM: arm64: nv: timers: Add a per-timer, per-vcpu offset Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 17/20] KVM: arm64: nv: timers: Support hyp timer emulation Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 18/20] KVM: arm64: selftests: Add physical timer registers to the sysreg list Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:47 ` [PATCH v4 19/20] KVM: arm64: selftests: Deal with spurious timer interrupts Marc Zyngier
2023-03-30 17:47 ` Marc Zyngier
2023-03-30 17:48 ` [PATCH v4 20/20] KVM: arm64: selftests: Augment existing timer test to handle variable offset Marc Zyngier
2023-03-30 17:48 ` Marc Zyngier
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=87zfd0e690.wl-maz@kernel.org \
--to=maz@kernel.org \
--cc=coltonlewis@google.com \
--cc=dwmw2@infradead.org \
--cc=james.morse@arm.com \
--cc=joey.gouly@arm.com \
--cc=kvm@vger.kernel.org \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=oliver.upton@linux.dev \
--cc=reijiw@google.com \
--cc=ricarkol@google.com \
--cc=suzuki.poulose@arm.com \
--cc=sveith@amazon.de \
--cc=yuzenghui@huawei.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.