From: Hyunwoo Kim <imv4bel@gmail.com>
To: Marc Zyngier <maz@kernel.org>
Cc: tabba@google.com, oupton@kernel.org, joey.gouly@arm.com,
seiden@linux.ibm.com, suzuki.poulose@arm.com,
yuzenghui@huawei.com, catalin.marinas@arm.com, will@kernel.org,
linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
imv4bel@gmail.com
Subject: Re: [PATCH v2 2/2] KVM: arm64: Bound used_lrs when flushing the pKVM hyp vCPU
Date: Sat, 6 Jun 2026 17:52:08 +0900 [thread overview]
Message-ID: <aiPfuAmM8JbtT6GY@v4bel> (raw)
In-Reply-To: <86zf19tlcu.wl-maz@kernel.org>
On Fri, Jun 05, 2026 at 09:58:09AM +0100, Marc Zyngier wrote:
> On Thu, 04 Jun 2026 16:12:03 +0100,
> Hyunwoo Kim <imv4bel@gmail.com> wrote:
> >
> > flush_hyp_vcpu() copies the host vGIC state into the hyp's private vCPU
> > on every run. The vGIC list register save and restore use used_lrs as
> > their loop bound and expect it to stay within the number of implemented
> > list registers. While this is generally the case, flush_hyp_vcpu()
> > copies vgic_v3 verbatim and does not enforce this, so a value provided
> > by the host is used at EL2 to index vgic_lr[] and access ICH_LR<n>_EL2
> > (host -> EL2).
> >
> > Fix by clamping used_lrs to the number of implemented list registers
> > after the copy, as the trusted path already does in
> > vgic_flush_lr_state().
> >
> > Fixes: be66e67f1750 ("KVM: arm64: Use the pKVM hyp vCPU structure in handle___kvm_vcpu_run()")
> > Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
> > ---
> > arch/arm64/kvm/hyp/nvhe/hyp-main.c | 8 ++++++++
> > 1 file changed, 8 insertions(+)
> >
> > diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > index 02c5d6e5abcbf..cd807fdb11ba8 100644
> > --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > @@ -7,6 +7,7 @@
> > #include <hyp/adjust_pc.h>
> > #include <hyp/switch.h>
> >
> > +#include <asm/arch_gicv3.h>
> > #include <asm/pgtable-types.h>
> > #include <asm/kvm_asm.h>
> > #include <asm/kvm_emulate.h>
> > @@ -142,6 +143,13 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
> >
> > hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3;
> >
> > + /* Bound used_lrs by the number of implemented list registers. */
> > + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
>
> There is no pKVM support without a GICv3 CPU interface, and absolutely
> everything already assumes it. Why do we need this extra check?
>
> > + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs =
> > + min_t(unsigned int,
> > + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs,
> > + (read_gicreg(ICH_VTR_EL2) & 0xf) + 1);
> > +
>
> Reading ICH_VTR_EL2 on each entry is going to cause some really heavy
> trapping under NV, and we should avoid this.
>
> kvm_vgic_global_state.nr_lr contains this information, and it should
> only be a matter of replicating it (or compute it once) at init time.
Does this approach look reasonable to you?
---
diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
index 8d06b62e7188..25199769a1d6 100644
--- a/arch/arm64/include/asm/kvm_hyp.h
+++ b/arch/arm64/include/asm/kvm_hyp.h
@@ -157,5 +157,6 @@ extern unsigned long kvm_nvhe_sym(__icache_flags);
extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits);
extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl);
extern unsigned long kvm_nvhe_sym(hyp_nr_cpus);
+extern unsigned int kvm_nvhe_sym(hyp_vgic_nr_lr);
#endif /* __ARM64_KVM_HYP_H__ */
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 9453321ef8c6..891fe2c7b854 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -2426,6 +2426,7 @@ static int __init init_subsystems(void)
switch (err) {
case 0:
vgic_present = true;
+ kvm_nvhe_sym(hyp_vgic_nr_lr) = kvm_vgic_global_state.nr_lr;
break;
case -ENODEV:
case -ENXIO:
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 06db299c37a8..8bb9362bc284 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -128,6 +128,9 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt;
+ /* A guest context must keep a NULL __hyp_running_vcpu. */
+ hyp_vcpu->vcpu.arch.ctxt.__hyp_running_vcpu = NULL;
+
hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2;
hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE);
hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) &
@@ -139,6 +142,12 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3;
+ /* Bound used_lrs by the number of implemented list registers. */
+ hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs =
+ min_t(unsigned int,
+ hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs,
+ hyp_vgic_nr_lr);
+
hyp_vcpu->vcpu.arch.pid = host_vcpu->arch.pid;
}
diff --git a/arch/arm64/kvm/hyp/nvhe/setup.c b/arch/arm64/kvm/hyp/nvhe/setup.c
index d461981616d9..ebc6b4afc336 100644
--- a/arch/arm64/kvm/hyp/nvhe/setup.c
+++ b/arch/arm64/kvm/hyp/nvhe/setup.c
@@ -20,6 +20,7 @@
#include <nvhe/trap_handler.h>
unsigned long hyp_nr_cpus;
+unsigned int hyp_vgic_nr_lr;
#define hyp_percpu_size ((unsigned long)__per_cpu_end - \
(unsigned long)__per_cpu_start)
>
> Thanks,
>
> M.
>
> --
> Without deviation from the norm, progress is not possible.
Best regards,
Hyunwoo Kim
next prev parent reply other threads:[~2026-06-06 8:52 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-04 15:12 [PATCH v2 0/2] KVM: arm64: Sanitise host vCPU fields copied in flush_hyp_vcpu() Hyunwoo Kim
2026-06-04 15:12 ` [PATCH v2 1/2] KVM: arm64: Clear __hyp_running_vcpu when flushing the pKVM hyp vCPU Hyunwoo Kim
2026-06-04 15:12 ` [PATCH v2 2/2] KVM: arm64: Bound used_lrs " Hyunwoo Kim
2026-06-05 8:58 ` Marc Zyngier
2026-06-06 8:52 ` Hyunwoo Kim [this message]
2026-06-06 10:52 ` Marc Zyngier
2026-06-04 15:29 ` [PATCH v2 0/2] KVM: arm64: Sanitise host vCPU fields copied in flush_hyp_vcpu() Fuad Tabba
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=aiPfuAmM8JbtT6GY@v4bel \
--to=imv4bel@gmail.com \
--cc=catalin.marinas@arm.com \
--cc=joey.gouly@arm.com \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=maz@kernel.org \
--cc=oupton@kernel.org \
--cc=seiden@linux.ibm.com \
--cc=suzuki.poulose@arm.com \
--cc=tabba@google.com \
--cc=will@kernel.org \
--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.