From: Vincent Donnefort <vdonnefort@google.com>
To: Fuad Tabba <fuad.tabba@linux.dev>
Cc: maz@kernel.org, oupton@kernel.org, kvmarm@lists.linux.dev,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, catalin.marinas@arm.com,
will@kernel.org, joey.gouly@arm.com, seiden@linux.ibm.com,
suzuki.poulose@arm.com, yuzenghui@huawei.com,
mark.rutland@arm.com, steven.price@arm.com, qperret@google.com,
tabba@google.com
Subject: Re: [PATCH v3 10/18] KVM: arm64: Handle PSCI calls for protected VMs at EL2
Date: Tue, 22 Sep 2026 17:37:49 +0100 [thread overview]
Message-ID: <arKu3SkgM9kftBEv@google.com> (raw)
In-Reply-To: <arKuPQ-w5EFXo-n8@google.com>
On Tue, Sep 22, 2026 at 05:35:09PM +0100, Vincent Donnefort wrote:
> On Mon, Sep 14, 2026 at 12:33:30PM +0100, Fuad Tabba wrote:
> > EL2 implements PSCI 1.1 for protected VMs: CPU_ON, CPU_OFF,
> > PSCI_VERSION and PSCI_FEATURES are decided at EL2 (CPU_ON and CPU_OFF
> > still exit to the host, which only schedules or parks the target),
> > AFFINITY_INFO, CPU_SUSPEND and the platform power operations are
> > forwarded to the host, and anything else returns NOT_SUPPORTED,
> > including the TRNG calls and the functions above 1.1, SYSTEM_OFF2
> > among them, that the host handled for a protected guest until now.
> > TRNG for protected guests is a follow-up. AFFINITY_INFO stays
> > with the host, which returns OFF only once it has parked the target:
> > the host is what a guest polls to see a CPU_OFF complete before it
> > issues the next CPU_ON, as Linux does on hotplug.
> >
> > Three consequences follow:
> >
> > - A protected VM has one primary vCPU, the first whose hyp vCPU is
> > created with mp_state RUNNABLE. A second one, or an mp_state other
> > than RUNNABLE or STOPPED, fails that vCPU's first KVM_RUN with
> > -EINVAL.
> >
> > - CPU_ON finds its target among the hyp vCPUs, which exist from the
> > target's first KVM_RUN; before that the guest gets
> > INVALID_PARAMETERS.
> >
> > - A vCPU EL2 holds powered off doesn't run: handle___kvm_vcpu_run()
> > returns ARM_EXCEPTION_IL, reported as KVM_EXIT_FAIL_ENTRY. Its
> > existing bail-outs return the same code instead of an -EINVAL that
> > handle_exit() didn't recognise, for every hyp vCPU.
> >
> > Non-protected VMs keep power_state ON and accept any mp_state.
> >
> > Each protected vCPU is OFF, ON_PENDING or ON. CPU_ON moves the target
> > to ON_PENDING, and the target's next run resets it and moves it to ON.
> > The racing transitions are cmpxchg, and the reset state is published
> > with a release/acquire pair, documented at each site. CPU_OFF publishes
> > OFF with a release, so the target's clear of reset_state.reset is
> > ordered before it and a CPU_ON that then wins on OFF republishes after
> > the clear. Rolling a CPU_ON the host failed back to OFF needs the
> > host's return value, which the per-EC marshalling patch delivers along
> > with the rollback. Until then such a target stays ON_PENDING, and the
> > reset has no observable effect: flush_hyp_vcpu() copies the host's
> > context in on every entry until that patch removes the copy, so the
> > target enters on the host's values rather than the ones EL2 reset.
> >
> > Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
> > ---
> > arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 14 ++
> > arch/arm64/kvm/hyp/nvhe/hyp-main.c | 25 ++-
> > arch/arm64/kvm/hyp/nvhe/pkvm.c | 283 ++++++++++++++++++++++++-
> > 3 files changed, 311 insertions(+), 11 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
> > index a04b7c04d5135..63b368baf0e72 100644
> > --- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
> > +++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
> > @@ -29,6 +29,12 @@ struct pkvm_hyp_vcpu {
> >
> > /* The previous exit's ARM_EXCEPTION_* code. */
> > u32 exit_code;
> > +
> > + /*
> > + * PSCI_0_2_AFFINITY_LEVEL_{OFF, ON_PENDING, ON}. A non-protected
> > + * vCPU is always ON.
> > + */
> > + int power_state;
> > };
> >
> > /*
> > @@ -46,6 +52,12 @@ struct pkvm_hyp_vm {
> > struct hyp_pool pool;
> > hyp_spinlock_t lock;
> >
> > + /*
> > + * The vCPU initialised RUNNABLE: claimed under vm_table_lock,
> > + * released only if its own init fails.
> > + */
> > + struct pkvm_hyp_vcpu *primary_vcpu;
> > +
> > /* Array of the hyp vCPU structures for this VM. */
> > struct pkvm_hyp_vcpu *vcpus[];
> > };
> > @@ -98,4 +110,6 @@ void kvm_init_pvm_id_regs(struct kvm_vcpu *vcpu);
> > void kvm_reset_pvm_sys_regs(struct kvm_vcpu *vcpu);
> > int kvm_check_pvm_sysreg_table(void);
> >
> > +int pkvm_reset_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu);
> > +struct pkvm_hyp_vcpu *pkvm_mpidr_to_hyp_vcpu(struct pkvm_hyp_vm *vm, u64 mpidr);
> > #endif /* __ARM64_KVM_NVHE_PKVM_H__ */
> > diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > index 9cc8ef16897c1..da8ab636063cf 100644
> > --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > @@ -8,6 +8,7 @@
> > #include <hyp/switch.h>
> >
> > #include <linux/irqchip/arm-gic-v3.h>
> > +#include <uapi/linux/psci.h>
> >
> > #include <asm/pgtable-types.h>
> > #include <asm/kvm_asm.h>
> > @@ -456,14 +457,12 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
> > {
> > struct pkvm_hyp_vcpu *hyp_vcpu;
> > struct kvm_vcpu *host_vcpu;
> > - int ret;
> > + int ret = ARM_EXCEPTION_IL;
> >
> > host_vcpu = get_host_hyp_vcpus(host_ctxt, 1, &hyp_vcpu);
> >
> > - if (!host_vcpu) {
> > - ret = -EINVAL;
> > + if (!host_vcpu)
> > goto out;
> > - }
> >
> > if (unlikely(hyp_vcpu)) {
> > /*
> > @@ -472,8 +471,22 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
> > * loading a vcpu. Therefore, if SME features enabled the host
> > * is misbehaving.
> > */
> > - if (unlikely(system_supports_sme() && read_sysreg_s(SYS_SVCR))) {
> > - ret = -EINVAL;
> > + if (unlikely(system_supports_sme() && read_sysreg_s(SYS_SVCR)))
> > + goto out;
> > +
> > + /*
> > + * ON has a single writer, pkvm_reset_vcpu() on this CPU, so
> > + * READ_ONCE suffices. ON_PENDING takes the reset; -ECANCELED
> > + * is a rollback that raced it.
> > + */
> > + switch (READ_ONCE(hyp_vcpu->power_state)) {
> > + case PSCI_0_2_AFFINITY_LEVEL_ON:
> > + break;
> > + case PSCI_0_2_AFFINITY_LEVEL_ON_PENDING:
> > + if (pkvm_reset_vcpu(hyp_vcpu))
> > + goto out;
> > + break;
> > + default:
> > goto out;
> > }
> >
> > diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
> > index 855cb77c8bba1..d970cba12ca47 100644
> > --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
> > +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
> > @@ -5,6 +5,7 @@
> > */
> >
> > #include <kvm/arm_hypercalls.h>
> > +#include <kvm/arm_psci.h>
> >
> > #include <linux/kvm_host.h>
> > #include <linux/mm.h>
> > @@ -433,6 +434,40 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
> > allowed_features, KVM_VCPU_MAX_FEATURES);
> > }
> >
> > +static int pkvm_vcpu_init_psci(struct pkvm_hyp_vcpu *hyp_vcpu, u32 mp_state)
> > +{
> > + struct vcpu_reset_state *reset_state = &hyp_vcpu->vcpu.arch.reset_state;
> > + struct pkvm_hyp_vm *hyp_vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
> > + struct kvm_vcpu *host_vcpu;
> > +
> > + if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {
> > + /* The host manages a non-protected vCPU: always ON at EL2. */
> > + hyp_vcpu->power_state = PSCI_0_2_AFFINITY_LEVEL_ON;
> > + return 0;
> > + }
> > +
> > + if (mp_state != KVM_MP_STATE_RUNNABLE && mp_state != KVM_MP_STATE_STOPPED)
> > + return -EINVAL;
> > +
> > + if (mp_state == KVM_MP_STATE_STOPPED) {
> > + reset_state->reset = false;
> > + hyp_vcpu->power_state = PSCI_0_2_AFFINITY_LEVEL_OFF;
> > + return 0;
> > + }
> > +
> > + hyp_assert_lock_held(&vm_table_lock);
> > + if (hyp_vm->primary_vcpu)
> > + return -EINVAL;
> > + hyp_vm->primary_vcpu = hyp_vcpu;
> > +
> > + host_vcpu = hyp_vcpu->host_vcpu;
> > + reset_state->pc = READ_ONCE(host_vcpu->arch.ctxt.regs.pc);
> > + reset_state->r0 = READ_ONCE(host_vcpu->arch.ctxt.regs.regs[0]);
> > + reset_state->reset = true;
> > + hyp_vcpu->power_state = PSCI_0_2_AFFINITY_LEVEL_ON_PENDING;
> > + return 0;
> > +}
> > +
> > static void unpin_host_vcpu(struct kvm_vcpu *host_vcpu)
> > {
> > if (host_vcpu)
> > @@ -447,6 +482,9 @@ static void unpin_host_sve_state(struct pkvm_hyp_vcpu *hyp_vcpu)
> > return;
> >
> > sve_state = hyp_vcpu->vcpu.arch.sve_state;
> > + if (!sve_state)
> > + return;
> > +
> > hyp_unpin_shared_mem(sve_state,
> > sve_state + vcpu_sve_state_size(&hyp_vcpu->vcpu));
> > }
> > @@ -559,10 +597,12 @@ static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
> > struct kvm_vcpu *host_vcpu)
> > {
> > int ret = 0;
> > + u32 mp_state;
> >
> > if (hyp_pin_shared_mem(host_vcpu, host_vcpu + 1))
> > return -EBUSY;
> >
> > + mp_state = READ_ONCE(host_vcpu->arch.mp_state.mp_state);
> > hyp_vcpu->host_vcpu = host_vcpu;
> >
> > hyp_vcpu->vcpu.kvm = &hyp_vm->kvm;
> > @@ -571,7 +611,6 @@ static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
> >
> > hyp_vcpu->vcpu.arch.hw_mmu = &hyp_vm->kvm.arch.mmu;
> > hyp_vcpu->vcpu.arch.cflags = READ_ONCE(host_vcpu->arch.cflags);
> > - hyp_vcpu->vcpu.arch.mp_state.mp_state = KVM_MP_STATE_STOPPED;
> >
> > if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {
> > /*
> > @@ -601,9 +640,12 @@ static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
> >
> > if (pkvm_hyp_vcpu_is_protected(hyp_vcpu))
> > kvm_reset_pvm_sys_regs(&hyp_vcpu->vcpu);
> > + ret = pkvm_vcpu_init_psci(hyp_vcpu, mp_state);
> > done:
> > - if (ret)
> > + if (ret) {
> > unpin_host_vcpu(host_vcpu);
> > + unpin_host_sve_state(hyp_vcpu);
> > + }
>
> Was it planned for a different commit of the series?
Ha no it wasn't, you're calling unpin_host_sve_state() unconditionally.
>
> > return ret;
> > }
> >
[...]
next prev parent reply other threads:[~2026-09-22 16:37 UTC|newest]
Thread overview: 55+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-14 11:33 [PATCH v3 00/18] KVM: arm64: Confine protected VM vCPU state to EL2 Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 01/18] KVM: arm64: Sync HCR_EL2.VSE back to the host vCPU under pKVM Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 02/18] KVM: arm64: Validate the host vCPU's VM before reading it " Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 03/18] KVM: arm64: Pin the host vCPU before adjusting its PC " Fuad Tabba
2026-09-14 13:01 ` sashiko-bot
2026-09-14 14:00 ` Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 04/18] KVM: arm64: Disable steal time for protected VMs Fuad Tabba
2026-09-22 14:34 ` Vincent Donnefort
2026-09-14 11:33 ` [PATCH v3 05/18] KVM: arm64: Introduce per-EC entry handlers for pKVM Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 06/18] KVM: arm64: Skip fixed-feature state flush for protected vCPUs Fuad Tabba
2026-09-14 13:42 ` sashiko-bot
2026-09-14 14:27 ` Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 07/18] KVM: arm64: Add {flush,sync}_hyp_timer_state() primitives Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 08/18] KVM: arm64: Add system register reset framework for protected VMs Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 09/18] KVM: arm64: Implement HVC handling for protected guests at EL2 Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 10/18] KVM: arm64: Handle PSCI calls for protected VMs " Fuad Tabba
2026-09-22 16:35 ` Vincent Donnefort
2026-09-22 16:37 ` Vincent Donnefort [this message]
2026-09-22 17:07 ` Vincent Donnefort
2026-09-23 9:51 ` Fuad Tabba
2026-09-24 8:30 ` Will Deacon
2026-09-24 11:26 ` Fuad Tabba
2026-09-24 12:14 ` Will Deacon
2026-09-24 15:21 ` Fuad Tabba
2026-10-01 12:58 ` Will Deacon
2026-10-01 13:11 ` Fuad Tabba
2026-10-01 12:59 ` Will Deacon
2026-10-01 13:11 ` Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 11/18] KVM: arm64: Restrict KVM_ARM_VCPU_INIT and PSCI version for protected VMs Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 12/18] KVM: arm64: Prevent host PC adjustments for protected vCPUs Fuad Tabba
2026-09-14 13:42 ` Marc Zyngier
2026-09-14 14:43 ` Fuad Tabba
2026-09-15 11:02 ` Marc Zyngier
2026-09-15 11:19 ` Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 13/18] KVM: arm64: Inject an UNDEF at EL2 for unhandled protected guest exits Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 14/18] KVM: arm64: Add per-EC entry/exit state marshalling for protected guests Fuad Tabba
2026-09-16 16:27 ` Marc Zyngier
2026-09-16 19:05 ` Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 15/18] KVM: arm64: Reject host access to protected VM private state Fuad Tabba
2026-09-16 16:30 ` Marc Zyngier
2026-09-16 19:07 ` Fuad Tabba
2026-09-17 8:06 ` Marc Zyngier
2026-09-17 18:42 ` Fuad Tabba
2026-09-18 13:21 ` Will Deacon
2026-09-18 13:24 ` Will Deacon
2026-09-27 8:20 ` Marc Zyngier
2026-09-27 12:37 ` Fuad Tabba
2026-09-28 19:00 ` Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 16/18] KVM: arm64: Reject host power-on of a vCPU that EL2 holds powered off Fuad Tabba
2026-09-16 16:43 ` Marc Zyngier
2026-09-16 19:08 ` Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 17/18] KVM: arm64: Advertise the capabilities that protected VMs support Fuad Tabba
2026-09-14 16:23 ` sashiko-bot
2026-09-14 18:01 ` Fuad Tabba
2026-09-14 11:33 ` [PATCH v3 18/18] KVM: arm64: Document the protected VM userspace API 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=arKu3SkgM9kftBEv@google.com \
--to=vdonnefort@google.com \
--cc=catalin.marinas@arm.com \
--cc=fuad.tabba@linux.dev \
--cc=joey.gouly@arm.com \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=maz@kernel.org \
--cc=oupton@kernel.org \
--cc=qperret@google.com \
--cc=seiden@linux.ibm.com \
--cc=steven.price@arm.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.