From nobody Sat Jul 25 19:28:29 2026 Received: from out-184.mta0.migadu.com (out-184.mta0.migadu.com [91.218.175.184]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C876B2FF147 for ; Tue, 14 Jul 2026 10:16:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024203; cv=none; b=aR+t/pDmL/dpHkvicMUg9ell3kGHf0YTzgMUvJ5hnqZT29uZfWqMWcdXrnCQcuj5WZSDbBQy7MK+A7gvgVTW/5+nz/0BCPUFa0heU3vQU9jsEwL+icfH9p6Um3FVH2MVAA5HkRv064rDBdY+043cDoK+rFpKLkeZTYvbrVPLv4M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024203; c=relaxed/simple; bh=dxjalqj5BqGRPZJJwgSTA+UoOThYr98Cj1w7xDsJj0k=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=U1PmwEx77Y45+l+uHtNwHY8oZjxOx+7kXklcHRZEsmpDAlO2qTzT+a15a1CdYkvVMwNJP/C7AceqSgHWxBcqqZKKUk/ZNNrDYjEdQZ5agVdz6Cc23IMkF8TS/ouI6WOzIAHUepAcjt8DmwKMcVUxgVpLOypsS9K1NTOnMNVfhEk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=mi+2i6qX; arc=none smtp.client-ip=91.218.175.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="mi+2i6qX" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1784024199; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=JD+xMDKjS2IygHN9Wf+Ijexh057Y2GhvdMNbnCBzQRE=; b=mi+2i6qXYHu+ogaI3CFTmYPy92JbpRAb+2jUHu1MikFfvFDgEcJb3cNGbyfxcu57FJGDdI HY5r3ewsnDdNgJmfvxBbSLZlewCK04/Ywmh/s/V0zSc5UvfX/8tTek9Kdu13FISH9Or79Z lNelM1ASkBFNNJDMHvl7s+K0dq7z8o8= From: Fuad Tabba To: Marc Zyngier , Oliver Upton , kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Cc: Catalin Marinas , Will Deacon , Joey Gouly , Steffen Eiden , Suzuki K Poulose , Zenghui Yu , Vincent Donnefort , Quentin Perret , Sebastian Ene , Hyunwoo Kim , Fuad Tabba Subject: [PATCH v5 1/8] KVM: arm64: Extract MPIDR computation into a shared header Date: Tue, 14 Jul 2026 11:15:54 +0100 Message-Id: <20260714101601.4142645-2-fuad.tabba@linux.dev> In-Reply-To: <20260714101601.4142645-1-fuad.tabba@linux.dev> References: <20260714101601.4142645-1-fuad.tabba@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Extract the vCPU MPIDR computation embedded in reset_mpidr() into a kvm_calculate_mpidr() inline in sys_regs.h, so it can be computed without duplicating the logic. A follow-up series reuses it to reset protected vCPUs at EL2. No functional change intended. Reviewed-by: Vincent Donnefort Signed-off-by: Fuad Tabba --- arch/arm64/kvm/sys_regs.c | 14 +------------- arch/arm64/kvm/sys_regs.h | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 5d5c579d45790..08ba882799d48 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -976,21 +976,9 @@ static u64 reset_actlr(struct kvm_vcpu *vcpu, const st= ruct sys_reg_desc *r) =20 static u64 reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) { - u64 mpidr; + u64 mpidr =3D kvm_calculate_mpidr(vcpu); =20 - /* - * Map the vcpu_id into the first three affinity level fields of - * the MPIDR. We limit the number of VCPUs in level 0 due to a - * limitation to 16 CPUs in that level in the ICC_SGIxR registers - * of the GICv3 to be able to address each CPU directly when - * sending IPIs. - */ - mpidr =3D (vcpu->vcpu_id & 0x0f) << MPIDR_LEVEL_SHIFT(0); - mpidr |=3D ((vcpu->vcpu_id >> 4) & 0xff) << MPIDR_LEVEL_SHIFT(1); - mpidr |=3D ((vcpu->vcpu_id >> 12) & 0xff) << MPIDR_LEVEL_SHIFT(2); - mpidr |=3D (1ULL << 31); vcpu_write_sys_reg(vcpu, mpidr, MPIDR_EL1); - return mpidr; } =20 diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h index 2a983664220ce..bd56a45abbf9c 100644 --- a/arch/arm64/kvm/sys_regs.h +++ b/arch/arm64/kvm/sys_regs.h @@ -222,6 +222,25 @@ find_reg(const struct sys_reg_params *params, const st= ruct sys_reg_desc table[], return __inline_bsearch((void *)pval, table, num, sizeof(table[0]), match= _sys_reg); } =20 +static inline u64 kvm_calculate_mpidr(const struct kvm_vcpu *vcpu) +{ + u64 mpidr; + + /* + * Map the vcpu_id into the first three affinity level fields of + * the MPIDR. We limit the number of VCPUs in level 0 due to a + * limitation to 16 CPUs in that level in the ICC_SGIxR registers + * of the GICv3 to be able to address each CPU directly when + * sending IPIs. + */ + mpidr =3D (vcpu->vcpu_id & 0x0f) << MPIDR_LEVEL_SHIFT(0); + mpidr |=3D ((vcpu->vcpu_id >> 4) & 0xff) << MPIDR_LEVEL_SHIFT(1); + mpidr |=3D ((vcpu->vcpu_id >> 12) & 0xff) << MPIDR_LEVEL_SHIFT(2); + mpidr |=3D (1ULL << 31); + + return mpidr; +} + const struct sys_reg_desc *get_reg_by_id(u64 id, const struct sys_reg_desc table[], unsigned int num); --=20 2.39.5 From nobody Sat Jul 25 19:28:29 2026 Received: from out-183.mta0.migadu.com (out-183.mta0.migadu.com [91.218.175.183]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EAF6A36494C for ; Tue, 14 Jul 2026 10:16:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.183 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024205; cv=none; b=SIik06P+tnYFloaFVUm62K9aQarlrOAEM3+FZpmz+wQU7vKruX3YZ3lzbv/zOFFrpzSe2FQip7lQ+xa+UOwDIiWh6wEBXulD7QkdKQLb7jk4hNqe5nNim5wj7dUKjUL0XfLGRrvFqCNCQ6R1wMT2AMG7Oqi5B3EJmM+Qn51ezKA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024205; c=relaxed/simple; bh=RcHFOcd4cUIFDwkePdeyjmialC76ZU3eCKeO6Mgza2w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=n4hS8JhZifhiDmFNVpcXQswcvPon4P1PCPdm0Q3MoMqRM7M/ztkC9eKLVbh1QV+93D1KqX6C69yKMM+NptRVbYlD6RpGxgFH19KS8u4X/EMZNkzQjCFodBIzKdZd5ljq8DEB6uleXf9Ue+9PTmzpLvvueOmc5YhgD9fesVInviI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=siO+AR7a; arc=none smtp.client-ip=91.218.175.183 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="siO+AR7a" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1784024201; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=LV5gap+CzXG51tXDcxBhEo+5k8g/SFFulCMXWXH9VaE=; b=siO+AR7at9a9PQDirwsaaSNwAwkQ2njbiS2cPxBYp5uutGPJLNLjp/3rw/wHaWKgG3if2M daAd6WsZJnCvjuUynbc4rFWrN4hxa4EvoAC9+qJaF9gdSwdkCP99uTm8f2pMxinbJa625V Y8Hv4aPnkiSWOO+MiGLAP3SIlmc0sZY= From: Fuad Tabba To: Marc Zyngier , Oliver Upton , kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Cc: Catalin Marinas , Will Deacon , Joey Gouly , Steffen Eiden , Suzuki K Poulose , Zenghui Yu , Vincent Donnefort , Quentin Perret , Sebastian Ene , Hyunwoo Kim , Fuad Tabba Subject: [PATCH v5 2/8] KVM: arm64: Make vcpu_{read,write}_sys_reg available to HYP code Date: Tue, 14 Jul 2026 11:15:55 +0100 Message-Id: <20260714101601.4142645-3-fuad.tabba@linux.dev> In-Reply-To: <20260714101601.4142645-1-fuad.tabba@linux.dev> References: <20260714101601.4142645-1-fuad.tabba@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" The vcpu_{read,write}_sys_reg() accessors are only valid on a VHE host, so helpers built on them such as kvm_vcpu_set_be()/kvm_vcpu_is_be() cannot be shared with hyp code. exception.c already wraps them in local helpers that pick the host- or hyp-side accessor via has_vhe(). Rename the host-only implementations to __vcpu_{read,write}_sysreg_vhe() and turn vcpu_{read,write}_sys_reg() into the context-dispatching wrappers, so every caller gets the version valid in any context and a follow-up series can share that emulation code at EL2. No functional change intended. Signed-off-by: Fuad Tabba Reviewed-by: Vincent Donnefort --- v5: - Named the wrappers vcpu_{read,write}_sys_reg() and renamed the host-only implementations to __vcpu_{read,write}_sysreg_vhe(), rather than introducing a kvm_vcpu_ prefix. (Oliver) - Dropped Vincent's Reviewed-by since the patch changed materially. arch/arm64/include/asm/kvm_emulate.h | 20 ++++++++++++++++ arch/arm64/include/asm/kvm_host.h | 4 ++-- arch/arm64/kvm/hyp/exception.c | 34 ++++++++-------------------- arch/arm64/kvm/sys_regs.c | 4 ++-- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/= kvm_emulate.h index 5bf3d7e1d92c7..429bda6f48d94 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -506,6 +506,26 @@ static inline unsigned long kvm_vcpu_get_mpidr_aff(str= uct kvm_vcpu *vcpu) return __vcpu_sys_reg(vcpu, MPIDR_EL1) & MPIDR_HWID_BITMASK; } =20 +/* + * __vcpu_*_sysreg_vhe() are only valid on a VHE host; wrap them so the sa= me + * call site also works at EL2 under nVHE. + */ +static inline u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, enum vcpu= _sysreg reg) +{ + if (has_vhe()) + return __vcpu_read_sysreg_vhe(vcpu, reg); + + return __vcpu_sys_reg(vcpu, reg); +} + +static inline void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, enum= vcpu_sysreg reg) +{ + if (has_vhe()) + __vcpu_write_sysreg_vhe(vcpu, val, reg); + else + __vcpu_assign_sys_reg(vcpu, reg, val); +} + static inline void kvm_vcpu_set_be(struct kvm_vcpu *vcpu) { if (vcpu_mode_is_32bit(vcpu)) { diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm= _host.h index bae2c4f92ef5c..f3c3c86b3d7fb 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -1215,8 +1215,8 @@ u64 kvm_vcpu_apply_reg_masks(const struct kvm_vcpu *,= enum vcpu_sysreg, u64); __v; \ }) =20 -u64 vcpu_read_sys_reg(const struct kvm_vcpu *, enum vcpu_sysreg); -void vcpu_write_sys_reg(struct kvm_vcpu *, u64, enum vcpu_sysreg); +u64 __vcpu_read_sysreg_vhe(const struct kvm_vcpu *vcpu, enum vcpu_sysreg r= eg); +void __vcpu_write_sysreg_vhe(struct kvm_vcpu *vcpu, u64 val, enum vcpu_sys= reg reg); =20 struct kvm_vm_stat { struct kvm_vm_stat_generic generic; diff --git a/arch/arm64/kvm/hyp/exception.c b/arch/arm64/kvm/hyp/exception.c index bef40ddb16dbc..754e2dc1df54a 100644 --- a/arch/arm64/kvm/hyp/exception.c +++ b/arch/arm64/kvm/hyp/exception.c @@ -20,22 +20,6 @@ #error Hypervisor code only! #endif =20 -static inline u64 __vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg) -{ - if (has_vhe()) - return vcpu_read_sys_reg(vcpu, reg); - - return __vcpu_sys_reg(vcpu, reg); -} - -static inline void __vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, in= t reg) -{ - if (has_vhe()) - vcpu_write_sys_reg(vcpu, val, reg); - else - __vcpu_assign_sys_reg(vcpu, reg, val); -} - static void __vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long target_= mode, u64 val) { @@ -101,14 +85,14 @@ static void enter_exception64(struct kvm_vcpu *vcpu, u= nsigned long target_mode, =20 switch (target_mode) { case PSR_MODE_EL1h: - vbar =3D __vcpu_read_sys_reg(vcpu, VBAR_EL1); - sctlr =3D __vcpu_read_sys_reg(vcpu, SCTLR_EL1); - __vcpu_write_sys_reg(vcpu, *vcpu_pc(vcpu), ELR_EL1); + vbar =3D vcpu_read_sys_reg(vcpu, VBAR_EL1); + sctlr =3D vcpu_read_sys_reg(vcpu, SCTLR_EL1); + vcpu_write_sys_reg(vcpu, *vcpu_pc(vcpu), ELR_EL1); break; case PSR_MODE_EL2h: - vbar =3D __vcpu_read_sys_reg(vcpu, VBAR_EL2); - sctlr =3D __vcpu_read_sys_reg(vcpu, SCTLR_EL2); - __vcpu_write_sys_reg(vcpu, *vcpu_pc(vcpu), ELR_EL2); + vbar =3D vcpu_read_sys_reg(vcpu, VBAR_EL2); + sctlr =3D vcpu_read_sys_reg(vcpu, SCTLR_EL2); + vcpu_write_sys_reg(vcpu, *vcpu_pc(vcpu), ELR_EL2); break; default: /* Don't do that */ @@ -185,7 +169,7 @@ static void enter_exception64(struct kvm_vcpu *vcpu, un= signed long target_mode, */ static unsigned long get_except32_cpsr(struct kvm_vcpu *vcpu, u32 mode) { - u32 sctlr =3D __vcpu_read_sys_reg(vcpu, SCTLR_EL1); + u32 sctlr =3D vcpu_read_sys_reg(vcpu, SCTLR_EL1); unsigned long old, new; =20 old =3D *vcpu_cpsr(vcpu); @@ -281,7 +265,7 @@ static void enter_exception32(struct kvm_vcpu *vcpu, u3= 2 mode, u32 vect_offset) { unsigned long spsr =3D *vcpu_cpsr(vcpu); bool is_thumb =3D (spsr & PSR_AA32_T_BIT); - u32 sctlr =3D __vcpu_read_sys_reg(vcpu, SCTLR_EL1); + u32 sctlr =3D vcpu_read_sys_reg(vcpu, SCTLR_EL1); u32 return_address; =20 *vcpu_cpsr(vcpu) =3D get_except32_cpsr(vcpu, mode); @@ -305,7 +289,7 @@ static void enter_exception32(struct kvm_vcpu *vcpu, u3= 2 mode, u32 vect_offset) if (sctlr & (1 << 13)) vect_offset +=3D 0xffff0000; else /* always have security exceptions */ - vect_offset +=3D __vcpu_read_sys_reg(vcpu, VBAR_EL1); + vect_offset +=3D vcpu_read_sys_reg(vcpu, VBAR_EL1); =20 *vcpu_pc(vcpu) =3D vect_offset; } diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 08ba882799d48..c6a416974a61f 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -291,7 +291,7 @@ static void write_sr_to_cpu(enum vcpu_sysreg reg, u64 v= al) } } =20 -u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, enum vcpu_sysreg reg) +u64 __vcpu_read_sysreg_vhe(const struct kvm_vcpu *vcpu, enum vcpu_sysreg r= eg) { struct sr_loc loc =3D {}; =20 @@ -338,7 +338,7 @@ u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, enum= vcpu_sysreg reg) return __vcpu_sys_reg(vcpu, reg); } =20 -void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, enum vcpu_sysreg r= eg) +void __vcpu_write_sysreg_vhe(struct kvm_vcpu *vcpu, u64 val, enum vcpu_sys= reg reg) { struct sr_loc loc =3D {}; =20 --=20 2.39.5 From nobody Sat Jul 25 19:28:29 2026 Received: from out-179.mta0.migadu.com (out-179.mta0.migadu.com [91.218.175.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0D621364929 for ; Tue, 14 Jul 2026 10:16:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024207; cv=none; b=FxqdSAIqd4KfnYerzXu+X7YmIai92BeqZxYblhILcOZH5M1bjDYChYn71D3IY1BteFdzAdjbRmtKwbtthR6+blzNiAMpzdCXBX0eMONWwp+0zW4i39fZQtHfLylSvXedhZf6JVgUMUPeCzwJFyB3k90OqcDeqBAS6vsDKJg3iHQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024207; c=relaxed/simple; bh=bitjVCEtfwXWHrzpZEB45QI30SUHoRsBEb5up0pveRA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=WWXZ91bCwcK/6HZR6w4RHegku8a1wCsCzkbVNmvmQbGMiIvZgVjvz1WJuUg15jCt1QwlR+8U1HVSQp5TOjD4XcrKjAO+gswfFp+AZLRpRumugu6pjd5VEninhwhDw7xt2QPJgdMWJcGSW1k5UEy5uIlbE2C5y5NlNMRmIFNNXS0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=pFj7IaTr; arc=none smtp.client-ip=91.218.175.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="pFj7IaTr" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1784024203; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=f2N8bgJhRkFOlZMhlmwhskxYCoKvemQPzz8C1725aUI=; b=pFj7IaTrkF2Q/GH1xVlioHsK84v/GYgtiMP5vno8T/sZL4I5YPxTo2jIm0nAMV8HxlORlg Dt5V4axFaLm758AeCZPsPf0ZyZ7YEtfHgruWX9EvZRzq3LLEspDQYsmAVkmMJnLNnGDPvs FZXXFhlEBdKmzFKtiwUrPzeLZJMPBVg= From: Fuad Tabba To: Marc Zyngier , Oliver Upton , kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Cc: Catalin Marinas , Will Deacon , Joey Gouly , Steffen Eiden , Suzuki K Poulose , Zenghui Yu , Vincent Donnefort , Quentin Perret , Sebastian Ene , Hyunwoo Kim , Fuad Tabba Subject: [PATCH v5 3/8] KVM: arm64: Factor out reusable vCPU reset helpers Date: Tue, 14 Jul 2026 11:15:56 +0100 Message-Id: <20260714101601.4142645-4-fuad.tabba@linux.dev> In-Reply-To: <20260714101601.4142645-1-fuad.tabba@linux.dev> References: <20260714101601.4142645-1-fuad.tabba@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Pull the reusable pieces out of kvm_reset_vcpu(): expose the reset PSTATE values in kvm_arm.h, and split the core register reset and the PSCI-driven reset into kvm_reset_vcpu_core() and kvm_reset_vcpu_psci(). A follow-up series reuses these to reset protected vCPUs at EL2. No functional change intended. Reviewed-by: Vincent Donnefort Signed-off-by: Fuad Tabba --- arch/arm64/include/asm/kvm_arm.h | 12 ++++++ arch/arm64/include/asm/kvm_emulate.h | 57 ++++++++++++++++++++++++++ arch/arm64/kvm/reset.c | 60 ++-------------------------- 3 files changed, 72 insertions(+), 57 deletions(-) diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_= arm.h index 3f9233b5a1308..aba4ec09acd23 100644 --- a/arch/arm64/include/asm/kvm_arm.h +++ b/arch/arm64/include/asm/kvm_arm.h @@ -348,4 +348,16 @@ { PSR_AA32_MODE_UND, "32-bit UND" }, \ { PSR_AA32_MODE_SYS, "32-bit SYS" } =20 +/* + * ARMv8 Reset Values + */ +#define VCPU_RESET_PSTATE_EL1 (PSR_MODE_EL1h | PSR_A_BIT | PSR_I_BIT | \ + PSR_F_BIT | PSR_D_BIT) + +#define VCPU_RESET_PSTATE_EL2 (PSR_MODE_EL2h | PSR_A_BIT | PSR_I_BIT | \ + PSR_F_BIT | PSR_D_BIT) + +#define VCPU_RESET_PSTATE_SVC (PSR_AA32_MODE_SVC | PSR_AA32_A_BIT | \ + PSR_AA32_I_BIT | PSR_AA32_F_BIT) + #endif /* __ARM64_KVM_ARM_H__ */ diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/= kvm_emulate.h index 429bda6f48d94..57e3482ad9a55 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -708,4 +708,61 @@ static inline void vcpu_set_hcrx(struct kvm_vcpu *vcpu) vcpu->arch.hcrx_el2 |=3D HCRX_EL2_EnASR; } } + +/* Reset a vcpu's core registers. */ +static inline void kvm_reset_vcpu_core(struct kvm_vcpu *vcpu) +{ + u32 pstate; + + if (vcpu_el1_is_32bit(vcpu)) + pstate =3D VCPU_RESET_PSTATE_SVC; + else if (vcpu_has_nv(vcpu)) + pstate =3D VCPU_RESET_PSTATE_EL2; + else + pstate =3D VCPU_RESET_PSTATE_EL1; + + /* Reset core registers */ + memset(vcpu_gp_regs(vcpu), 0, sizeof(*vcpu_gp_regs(vcpu))); + memset(&vcpu->arch.ctxt.fp_regs, 0, sizeof(vcpu->arch.ctxt.fp_regs)); + vcpu->arch.ctxt.spsr_abt =3D 0; + vcpu->arch.ctxt.spsr_und =3D 0; + vcpu->arch.ctxt.spsr_irq =3D 0; + vcpu->arch.ctxt.spsr_fiq =3D 0; + vcpu_gp_regs(vcpu)->pstate =3D pstate; +} + +/* PSCI reset handling for a vcpu. */ +static inline void kvm_reset_vcpu_psci(struct kvm_vcpu *vcpu, + struct vcpu_reset_state *reset_state) +{ + unsigned long target_pc =3D reset_state->pc; + + /* Gracefully handle Thumb2 entry point */ + if (vcpu_mode_is_32bit(vcpu) && (target_pc & 1)) { + target_pc &=3D ~1UL; + vcpu_set_thumb(vcpu); + } + + /* Propagate caller endianness */ + if (reset_state->be) + kvm_vcpu_set_be(vcpu); + + *vcpu_pc(vcpu) =3D target_pc; + + /* + * We may come from a state where either a PC update was + * pending (SMC call resulting in PC being increpented to + * skip the SMC) or a pending exception. Make sure we get + * rid of all that, as this cannot be valid out of reset. + * + * Note that clearing the exception mask also clears PC + * updates, but that's an implementation detail, and we + * really want to make it explicit. + */ + vcpu_clear_flag(vcpu, PENDING_EXCEPTION); + vcpu_clear_flag(vcpu, EXCEPT_MASK); + vcpu_clear_flag(vcpu, INCREMENT_PC); + vcpu_set_reg(vcpu, 0, reset_state->r0); +} + #endif /* __ARM64_KVM_EMULATE_H__ */ diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c index b963fd975aaca..10eb7249aa9e8 100644 --- a/arch/arm64/kvm/reset.c +++ b/arch/arm64/kvm/reset.c @@ -34,18 +34,6 @@ static u32 __ro_after_init kvm_ipa_limit; unsigned int __ro_after_init kvm_host_sve_max_vl; =20 -/* - * ARMv8 Reset Values - */ -#define VCPU_RESET_PSTATE_EL1 (PSR_MODE_EL1h | PSR_A_BIT | PSR_I_BIT | \ - PSR_F_BIT | PSR_D_BIT) - -#define VCPU_RESET_PSTATE_EL2 (PSR_MODE_EL2h | PSR_A_BIT | PSR_I_BIT | \ - PSR_F_BIT | PSR_D_BIT) - -#define VCPU_RESET_PSTATE_SVC (PSR_AA32_MODE_SVC | PSR_AA32_A_BIT | \ - PSR_AA32_I_BIT | PSR_AA32_F_BIT) - unsigned int __ro_after_init kvm_sve_max_vl; =20 int __init kvm_arm_init_sve(void) @@ -191,7 +179,6 @@ void kvm_reset_vcpu(struct kvm_vcpu *vcpu) { struct vcpu_reset_state reset_state; bool loaded; - u32 pstate; =20 spin_lock(&vcpu->arch.mp_state_lock); reset_state =3D vcpu->arch.reset_state; @@ -210,21 +197,8 @@ void kvm_reset_vcpu(struct kvm_vcpu *vcpu) kvm_vcpu_reset_sve(vcpu); } =20 - if (vcpu_el1_is_32bit(vcpu)) - pstate =3D VCPU_RESET_PSTATE_SVC; - else if (vcpu_has_nv(vcpu)) - pstate =3D VCPU_RESET_PSTATE_EL2; - else - pstate =3D VCPU_RESET_PSTATE_EL1; - /* Reset core registers */ - memset(vcpu_gp_regs(vcpu), 0, sizeof(*vcpu_gp_regs(vcpu))); - memset(&vcpu->arch.ctxt.fp_regs, 0, sizeof(vcpu->arch.ctxt.fp_regs)); - vcpu->arch.ctxt.spsr_abt =3D 0; - vcpu->arch.ctxt.spsr_und =3D 0; - vcpu->arch.ctxt.spsr_irq =3D 0; - vcpu->arch.ctxt.spsr_fiq =3D 0; - vcpu_gp_regs(vcpu)->pstate =3D pstate; + kvm_reset_vcpu_core(vcpu); =20 /* Reset system registers */ kvm_reset_sys_regs(vcpu); @@ -233,36 +207,8 @@ void kvm_reset_vcpu(struct kvm_vcpu *vcpu) * Additional reset state handling that PSCI may have imposed on us. * Must be done after all the sys_reg reset. */ - if (reset_state.reset) { - unsigned long target_pc =3D reset_state.pc; - - /* Gracefully handle Thumb2 entry point */ - if (vcpu_mode_is_32bit(vcpu) && (target_pc & 1)) { - target_pc &=3D ~1UL; - vcpu_set_thumb(vcpu); - } - - /* Propagate caller endianness */ - if (reset_state.be) - kvm_vcpu_set_be(vcpu); - - *vcpu_pc(vcpu) =3D target_pc; - - /* - * We may come from a state where either a PC update was - * pending (SMC call resulting in PC being increpented to - * skip the SMC) or a pending exception. Make sure we get - * rid of all that, as this cannot be valid out of reset. - * - * Note that clearing the exception mask also clears PC - * updates, but that's an implementation detail, and we - * really want to make it explicit. - */ - vcpu_clear_flag(vcpu, PENDING_EXCEPTION); - vcpu_clear_flag(vcpu, EXCEPT_MASK); - vcpu_clear_flag(vcpu, INCREMENT_PC); - vcpu_set_reg(vcpu, 0, reset_state.r0); - } + if (reset_state.reset) + kvm_reset_vcpu_psci(vcpu, &reset_state); =20 /* Reset timer */ kvm_timer_vcpu_reset(vcpu); --=20 2.39.5 From nobody Sat Jul 25 19:28:29 2026 Received: from out-170.mta0.migadu.com (out-170.mta0.migadu.com [91.218.175.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 49B89328611 for ; Tue, 14 Jul 2026 10:16:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024210; cv=none; b=SpdoupVkMW2saEGpFQDNhqNTGVfAnAwqAqXu88EHYInNjaHRlP4EMmo3SBSt+QmIL7ERqxwotviw8O93nB2/vQw7kBOG8dTlvkWJ9DGeYmgbKGpJx+MjDKRlimSm8kwyRZMK/keL62zZ9GZoVrn47q+q5LqGlDz6KG29P3qgDtU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024210; c=relaxed/simple; bh=seU745NFUCu9ykrsy0Mp34nXJ0y0BN2RmeJoWPkFp7A=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=NDJM6sgr9zIt52Pdl4/svMvq4T2tzesCcW9IWey3Z5fQzAAjFxQ+fdf7ags34flk4M1peWChqVyvJro7Bq7dxmD6bVTBge2hIJgMvHZw6DMOhWFqd0fXthn24bUxGYhRsYmIsOWhaB1mMyqkGfRSwKhowkYxqz6BUUkSEYPPym4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=n9lL7v2m; arc=none smtp.client-ip=91.218.175.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="n9lL7v2m" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1784024205; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=n4y4UR4PmK16GfMJFUFQ14Kb30LFrA+TnM7Dyu73NEs=; b=n9lL7v2mklxI7+c6LYS0lly5GoTMx6UqD/5Nq7OzU26YfM08OHct43hZKqgryO5DNvlsl8 PrtfLQhvrErqk3S3RIBva2w0ZKPIqmLz7b6gYz/p0KaIygZ5NBmb+QfM/k02Rhd6td0vo7 C6ercOSEO5WhSuL6ptYZ87KIal9Sy5Q= From: Fuad Tabba To: Marc Zyngier , Oliver Upton , kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Cc: Catalin Marinas , Will Deacon , Joey Gouly , Steffen Eiden , Suzuki K Poulose , Zenghui Yu , Vincent Donnefort , Quentin Perret , Sebastian Ene , Hyunwoo Kim , Fuad Tabba Subject: [PATCH v5 4/8] KVM: arm64: Move PSCI helper functions to a shared header Date: Tue, 14 Jul 2026 11:15:57 +0100 Message-Id: <20260714101601.4142645-5-fuad.tabba@linux.dev> In-Reply-To: <20260714101601.4142645-1-fuad.tabba@linux.dev> References: <20260714101601.4142645-1-fuad.tabba@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Move kvm_psci_valid_affinity() and kvm_psci_narrow_to_32bit() from psci.c to include/kvm/arm_psci.h, and move psci_affinity_mask() there too, renaming it kvm_psci_affinity_mask() now that it is no longer file-local. A follow-up series handles some protected-guest PSCI calls at EL2 using these helpers. No functional change intended. Reviewed-by: Vincent Donnefort Signed-off-by: Fuad Tabba --- arch/arm64/kvm/psci.c | 30 +----------------------------- include/kvm/arm_psci.h | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c index 3b5dbe9a0a0ea..e3db84400d1f8 100644 --- a/arch/arm64/kvm/psci.c +++ b/arch/arm64/kvm/psci.c @@ -21,16 +21,6 @@ * as described in ARM document number ARM DEN 0022A. */ =20 -#define AFFINITY_MASK(level) ~((0x1UL << ((level) * MPIDR_LEVEL_BITS)) - 1) - -static unsigned long psci_affinity_mask(unsigned long affinity_level) -{ - if (affinity_level <=3D 3) - return MPIDR_HWID_BITMASK & AFFINITY_MASK(affinity_level); - - return 0; -} - static unsigned long kvm_psci_vcpu_suspend(struct kvm_vcpu *vcpu) { /* @@ -51,12 +41,6 @@ static unsigned long kvm_psci_vcpu_suspend(struct kvm_vc= pu *vcpu) return PSCI_RET_SUCCESS; } =20 -static inline bool kvm_psci_valid_affinity(struct kvm_vcpu *vcpu, - unsigned long affinity) -{ - return !(affinity & ~MPIDR_HWID_BITMASK); -} - static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu) { struct vcpu_reset_state *reset_state; @@ -135,7 +119,7 @@ static unsigned long kvm_psci_vcpu_affinity_info(struct= kvm_vcpu *vcpu) return PSCI_RET_INVALID_PARAMS; =20 /* Determine target affinity mask */ - target_affinity_mask =3D psci_affinity_mask(lowest_affinity_level); + target_affinity_mask =3D kvm_psci_affinity_mask(lowest_affinity_level); if (!target_affinity_mask) return PSCI_RET_INVALID_PARAMS; =20 @@ -220,18 +204,6 @@ static void kvm_psci_system_suspend(struct kvm_vcpu *v= cpu) run->exit_reason =3D KVM_EXIT_SYSTEM_EVENT; } =20 -static void kvm_psci_narrow_to_32bit(struct kvm_vcpu *vcpu) -{ - int i; - - /* - * Zero the input registers' upper 32 bits. They will be fully - * zeroed on exit, so we're fine changing them in place. - */ - for (i =3D 1; i < 4; i++) - vcpu_set_reg(vcpu, i, lower_32_bits(vcpu_get_reg(vcpu, i))); -} - static unsigned long kvm_psci_check_allowed_function(struct kvm_vcpu *vcpu= , u32 fn) { /* diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h index cbaec804eb839..f86a006d67136 100644 --- a/include/kvm/arm_psci.h +++ b/include/kvm/arm_psci.h @@ -38,6 +38,33 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu) return KVM_ARM_PSCI_0_1; } =20 +/* Narrow the PSCI register arguments (r1 to r3) to 32 bits. */ +static inline void kvm_psci_narrow_to_32bit(struct kvm_vcpu *vcpu) +{ + int i; + + /* + * Zero the input registers' upper 32 bits. They will be fully + * zeroed on exit, so we're fine changing them in place. + */ + for (i =3D 1; i < 4; i++) + vcpu_set_reg(vcpu, i, lower_32_bits(vcpu_get_reg(vcpu, i))); +} + +static inline bool kvm_psci_valid_affinity(struct kvm_vcpu *vcpu, + unsigned long affinity) +{ + return !(affinity & ~MPIDR_HWID_BITMASK); +} + +static inline unsigned long kvm_psci_affinity_mask(unsigned long affinity_= level) +{ + if (affinity_level <=3D 3) + return MPIDR_HWID_BITMASK & + ~((0x1UL << (affinity_level * MPIDR_LEVEL_BITS)) - 1); + + return 0; +} =20 int kvm_psci_call(struct kvm_vcpu *vcpu); =20 --=20 2.39.5 From nobody Sat Jul 25 19:28:29 2026 Received: from out-178.mta0.migadu.com (out-178.mta0.migadu.com [91.218.175.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EF7DD2FF147 for ; Tue, 14 Jul 2026 10:16:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024216; cv=none; b=RKYCF5efT0RFk20gfv3rCCJx32z+WUwq7IdEcIWFYSYvtyYXNrdHggDEOwu1dBvRlM6nxOSgj2ipUel/G+x6VkNu4XJSSDAQ1RIuqZmzXAeRY1nBIAXjmKwx973/TVtAIQgaqqjJ0SrgswD3aNUD1zJmYPg9e1Yy/GJQQ0O9n6M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024216; c=relaxed/simple; bh=nLy2K7GRel9NEq+9AFAtHubmTI7/yDD8KwlMbT9qc20=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=MfSb5AqCUuXaeGk3m1a5QswyKjQi7tmA+7QaKKYw5+0wLa1qEL7NEd1FDrjWc/GVxSJLwLA9WVp+k6mYkV/XCh2P+/SjX+sfO6ue8CIyiopm1DxfOQ9XDjRmkjbPTGws/+v1kcUYHi2ISRB1MmWW+OlP7zStZICmQDKD244lYwQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=faozMeKv; arc=none smtp.client-ip=91.218.175.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="faozMeKv" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1784024207; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ptobWscVWFitUE7YzTMPHgY1rovNIaVBt3vxnNpD814=; b=faozMeKvmxBXNsF2LuzB8z2hJZhYlvlKeBHS2PALOy9wjZzg643N/DNDb39zKiFvYiucdL YCmhYhjw3WJahJMhCc07j/iS+C/+V53ostEWPhQ80abIy/Kr6LFEiKwsRCzibo0MwiOpNw JGdCh6YZQl7tmZ9m4P4wmZq0kh6TeOs= From: Fuad Tabba To: Marc Zyngier , Oliver Upton , kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Cc: Catalin Marinas , Will Deacon , Joey Gouly , Steffen Eiden , Suzuki K Poulose , Zenghui Yu , Vincent Donnefort , Quentin Perret , Sebastian Ene , Hyunwoo Kim , Fuad Tabba Subject: [PATCH v5 5/8] KVM: arm64: Add host and hypervisor vCPU lookup primitives Date: Tue, 14 Jul 2026 11:15:58 +0100 Message-Id: <20260714101601.4142645-6-fuad.tabba@linux.dev> In-Reply-To: <20260714101601.4142645-1-fuad.tabba@linux.dev> References: <20260714101601.4142645-1-fuad.tabba@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" From: Marc Zyngier The nVHE hypervisor repeatedly resolves a host vCPU into the EL2 address space and validates that the loaded hyp vCPU matches it, with that logic open-coded in each handler. Add __get_host_hyp_vcpus() and the get_host_hyp_vcpus() macro, which translate the host vCPU into the hypervisor's address space and, when pKVM is enabled, also return the loaded hyp vCPU if it matches. If pKVM is enabled but the loaded hyp vCPU does not correspond to the requested host vCPU, both the host and hyp vCPU are returned as NULL. Convert handle___kvm_vcpu_run() to use it. No functional change intended. Reviewed-by: Vincent Donnefort Signed-off-by: Marc Zyngier Co-developed-by: Fuad Tabba Signed-off-by: Fuad Tabba --- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 52 ++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/h= yp-main.c index d3c69de698f48..45d717889f6ea 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -214,14 +214,45 @@ static void handle___pkvm_vcpu_put(struct kvm_cpu_con= text *host_ctxt) pkvm_put_hyp_vcpu(hyp_vcpu); } =20 -static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt) +static struct kvm_vcpu *__get_host_hyp_vcpus(struct kvm_vcpu *arg, + struct pkvm_hyp_vcpu **hyp_vcpup) { - DECLARE_REG(struct kvm_vcpu *, host_vcpu, host_ctxt, 1); - int ret; + struct kvm_vcpu *host_vcpu =3D kern_hyp_va(arg); + struct pkvm_hyp_vcpu *hyp_vcpu =3D NULL; =20 if (unlikely(is_protected_kvm_enabled())) { - struct pkvm_hyp_vcpu *hyp_vcpu =3D pkvm_get_loaded_hyp_vcpu(); + hyp_vcpu =3D pkvm_get_loaded_hyp_vcpu(); =20 + if (!hyp_vcpu || hyp_vcpu->host_vcpu !=3D host_vcpu) { + hyp_vcpu =3D NULL; + host_vcpu =3D NULL; + } + } + + *hyp_vcpup =3D hyp_vcpu; + return host_vcpu; +} + +#define get_host_hyp_vcpus(ctxt, regnr, hyp_vcpup) \ + ({ \ + DECLARE_REG(struct kvm_vcpu *, __vcpu, ctxt, regnr); \ + __get_host_hyp_vcpus(__vcpu, hyp_vcpup); \ + }) + +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; + + host_vcpu =3D get_host_hyp_vcpus(host_ctxt, 1, &hyp_vcpu); + + if (!host_vcpu) { + ret =3D -EINVAL; + goto out; + } + + if (unlikely(hyp_vcpu)) { /* * KVM (and pKVM) doesn't support SME guests for now, and * ensures that SME features aren't enabled in pstate when @@ -233,23 +264,16 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_cont= ext *host_ctxt) goto out; } =20 - if (!hyp_vcpu) { - ret =3D -EINVAL; - goto out; - } - flush_hyp_vcpu(hyp_vcpu); =20 ret =3D __kvm_vcpu_run(&hyp_vcpu->vcpu); =20 sync_hyp_vcpu(hyp_vcpu); } else { - struct kvm_vcpu *vcpu =3D kern_hyp_va(host_vcpu); - /* The host is fully trusted, run its vCPU directly. */ - fpsimd_lazy_switch_to_guest(vcpu); - ret =3D __kvm_vcpu_run(vcpu); - fpsimd_lazy_switch_to_host(vcpu); + fpsimd_lazy_switch_to_guest(host_vcpu); + ret =3D __kvm_vcpu_run(host_vcpu); + fpsimd_lazy_switch_to_host(host_vcpu); } out: cpu_reg(host_ctxt, 1) =3D ret; --=20 2.39.5 From nobody Sat Jul 25 19:28:29 2026 Received: from out-179.mta0.migadu.com (out-179.mta0.migadu.com [91.218.175.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 28569329E6C for ; Tue, 14 Jul 2026 10:16:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024218; cv=none; b=kFU1zlmBf5T+JhgHUgP5pNlTXROU2bsMFzSiUB892xzYdc3Kd7yn/XI8TkivpJpp1EJoY4B0T2H8/Wt0gbqwMKq7hsRxAfyJHxW36TwCupPXfCFVufZWGZ0bEMD6UuUUgkOSJLS2l0m1S9VKng4QHHL2cScjknKkpdzzQgnzFnE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024218; c=relaxed/simple; bh=h8bGkvVSgaFGg0a8ROSm/bYwk4LcvU0kTIHFxDd0kCw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=t373S9cAvr301XMAiPHAylGDfaKkGxAUJhq6+vW9PAkIHrb2bxVF6oX55AC+q8H9lDCReumaFcMxZ4GEbTbCZzmhWI1/1e8QkVKEpDlCR0DXDrwRHo7qviC5oPVgquoJTVQjizz2rZjRHx4w577pr3U6Xu4Gp9Ck3c/4OBsU3Bo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=S34prvk9; arc=none smtp.client-ip=91.218.175.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="S34prvk9" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1784024209; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=0lRjC8sbxSnb8bmyPxIrTX3A1cB87UfAbp+R6QRvhw8=; b=S34prvk9NkJYEqztj9FBQtP9Pzyrf4dWRUm19c0Cj2tzdMHeYKLEYyblT8eFoEJDwm7yqD s84RJuUYoGXzsi0waiHriWX7O1lq3n2Yc/gIydRt3pwJqWm+CgLYtu+8bNC4H1M4bMOOA6 slcuuxsuBNCWEPZQJl6BKi+VI0pDKfc= From: Fuad Tabba To: Marc Zyngier , Oliver Upton , kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Cc: Catalin Marinas , Will Deacon , Joey Gouly , Steffen Eiden , Suzuki K Poulose , Zenghui Yu , Vincent Donnefort , Quentin Perret , Sebastian Ene , Hyunwoo Kim , Fuad Tabba Subject: [PATCH v5 6/8] KVM: arm64: Minimise EL2's exposure of host VGIC state during world switch Date: Tue, 14 Jul 2026 11:15:59 +0100 Message-Id: <20260714101601.4142645-7-fuad.tabba@linux.dev> In-Reply-To: <20260714101601.4142645-1-fuad.tabba@linux.dev> References: <20260714101601.4142645-1-fuad.tabba@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" From: Marc Zyngier The host passes a vgic_v3_cpu_if pointer to the __vgic_v3_save_aprs and __vgic_v3_restore_vmcr_aprs hypercalls, which EL2 dereferences wholesale. That exposes the host's full VGIC emulation state to the hypervisor, against pKVM's isolation goals. Recover the host vCPU from the supplied cpu_if via container_of() and copy only vgic_vmcr and the active priority registers between EL2's hyp-side state and the host vCPU, so EL2 no longer dereferences the host's vgic_v3_cpu_if directly. Reviewed-by: Vincent Donnefort Signed-off-by: Marc Zyngier Co-developed-by: Fuad Tabba Signed-off-by: Fuad Tabba --- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 67 ++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/h= yp-main.c index 45d717889f6ea..f3233ee343a39 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -7,6 +7,8 @@ #include #include =20 +#include + #include #include #include @@ -239,6 +241,16 @@ static struct kvm_vcpu *__get_host_hyp_vcpus(struct kv= m_vcpu *arg, __get_host_hyp_vcpus(__vcpu, hyp_vcpup); \ }) =20 +#define get_host_hyp_vcpus_from_vgic_v3_cpu_if(ctxt, regnr, hyp_vcpup) \ + ({ \ + DECLARE_REG(struct vgic_v3_cpu_if *, cif, ctxt, regnr);\ + struct kvm_vcpu *__vcpu =3D container_of(cif, \ + struct kvm_vcpu, \ + arch.vgic_cpu.vgic_v3); \ + \ + __get_host_hyp_vcpus(__vcpu, hyp_vcpup); \ + }) + static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt) { struct pkvm_hyp_vcpu *hyp_vcpu; @@ -508,16 +520,63 @@ static void handle___vgic_v3_init_lrs(struct kvm_cpu_= context *host_ctxt) =20 static void handle___vgic_v3_save_aprs(struct kvm_cpu_context *host_ctxt) { - DECLARE_REG(struct vgic_v3_cpu_if *, cpu_if, host_ctxt, 1); + struct pkvm_hyp_vcpu *hyp_vcpu; + struct kvm_vcpu *host_vcpu; =20 - __vgic_v3_save_aprs(kern_hyp_va(cpu_if)); + host_vcpu =3D get_host_hyp_vcpus_from_vgic_v3_cpu_if(host_ctxt, 1, + &hyp_vcpu); + if (!host_vcpu) + return; + + if (unlikely(hyp_vcpu)) { + struct vgic_v3_cpu_if *hyp_cpu_if, *host_cpu_if; + int i; + + hyp_cpu_if =3D &hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3; + __vgic_v3_save_aprs(hyp_cpu_if); + + host_cpu_if =3D &host_vcpu->arch.vgic_cpu.vgic_v3; + host_cpu_if->vgic_vmcr =3D hyp_cpu_if->vgic_vmcr; + for (i =3D 0; i < ARRAY_SIZE(host_cpu_if->vgic_ap0r); i++) { + host_cpu_if->vgic_ap0r[i] =3D hyp_cpu_if->vgic_ap0r[i]; + host_cpu_if->vgic_ap1r[i] =3D hyp_cpu_if->vgic_ap1r[i]; + } + } else { + __vgic_v3_save_aprs(&host_vcpu->arch.vgic_cpu.vgic_v3); + } } =20 static void handle___vgic_v3_restore_vmcr_aprs(struct kvm_cpu_context *hos= t_ctxt) { - DECLARE_REG(struct vgic_v3_cpu_if *, cpu_if, host_ctxt, 1); + struct pkvm_hyp_vcpu *hyp_vcpu; + struct kvm_vcpu *host_vcpu; =20 - __vgic_v3_restore_vmcr_aprs(kern_hyp_va(cpu_if)); + host_vcpu =3D get_host_hyp_vcpus_from_vgic_v3_cpu_if(host_ctxt, 1, + &hyp_vcpu); + if (!host_vcpu) + return; + + if (unlikely(hyp_vcpu)) { + struct vgic_v3_cpu_if *hyp_cpu_if, *host_cpu_if; + int i; + + hyp_cpu_if =3D &hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3; + host_cpu_if =3D &host_vcpu->arch.vgic_cpu.vgic_v3; + + hyp_cpu_if->vgic_vmcr =3D host_cpu_if->vgic_vmcr; + /* Should be a one-off */ + hyp_cpu_if->vgic_sre =3D (ICC_SRE_EL1_DIB | + ICC_SRE_EL1_DFB | + ICC_SRE_EL1_SRE); + for (i =3D 0; i < ARRAY_SIZE(host_cpu_if->vgic_ap0r); i++) { + hyp_cpu_if->vgic_ap0r[i] =3D host_cpu_if->vgic_ap0r[i]; + hyp_cpu_if->vgic_ap1r[i] =3D host_cpu_if->vgic_ap1r[i]; + } + + __vgic_v3_restore_vmcr_aprs(hyp_cpu_if); + } else { + __vgic_v3_restore_vmcr_aprs(&host_vcpu->arch.vgic_cpu.vgic_v3); + } } =20 static void handle___pkvm_init(struct kvm_cpu_context *host_ctxt) --=20 2.39.5 From nobody Sat Jul 25 19:28:29 2026 Received: from out-183.mta0.migadu.com (out-183.mta0.migadu.com [91.218.175.183]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0B90D328611 for ; Tue, 14 Jul 2026 10:16:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.183 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024216; cv=none; b=h8fgBlGzQn1jf8hJAuwsLyEFIySlkatqWljL2ne+2R8N/R79qGfPSNuzsE55QUS2m4ACG8CnI/sNh/jMAITpbY9sIa1b2C42fv9WldAEgKq4KmUqeN0gOfZh4Oa3O/v1Kh5mIOWyMFHIfYCW8RWnBtSiccYhTQTi/g6/uN2gH1Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024216; c=relaxed/simple; bh=fZBm9U49NriW6i5YLhn3zhSRCOjztPWLyDa0JgPgYNY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=INlM9sZweTEeMzcRrEwdFYe+/yMYU+sLkKP+onsmo8xjbfdM3P9i+WtOL6CQV7foO8D1ahbHzXjxk9uSthqg5lvJLtK4sqVRRxqly/CpW3RW6064nR3jhBvKtAmM/jZnGWFf3sxkYAzLkH2xGFaWLsOWeynhi8QbRQGbt0yPDZA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=IEREiKQ8; arc=none smtp.client-ip=91.218.175.183 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="IEREiKQ8" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1784024211; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=UPa4aylWgI5AC08JpsSdo1aaI++VFHxt+8yeLWVTzSI=; b=IEREiKQ8vzXfvXvn1ISWOsSmfPoYkjTZuLWUBBVvDcujFYRhJkQRr9jaIIKQysSmqdQ6QU eb2vGML/NTvQcCwmbFRCfAdQsA0MkCZBCF5rx8JLpj9XBUpBBwBHa0UCzDtqsoiRTl1UwI yo10efVG4ML6f6aTnufAni0+Rh92PyI= From: Fuad Tabba To: Marc Zyngier , Oliver Upton , kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Cc: Catalin Marinas , Will Deacon , Joey Gouly , Steffen Eiden , Suzuki K Poulose , Zenghui Yu , Vincent Donnefort , Quentin Perret , Sebastian Ene , Hyunwoo Kim , Fuad Tabba Subject: [PATCH v5 7/8] KVM: arm64: Add primitives to flush/sync the VGIC state at EL2 Date: Tue, 14 Jul 2026 11:16:00 +0100 Message-Id: <20260714101601.4142645-8-fuad.tabba@linux.dev> In-Reply-To: <20260714101601.4142645-1-fuad.tabba@linux.dev> References: <20260714101601.4142645-1-fuad.tabba@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" From: Marc Zyngier pKVM performs its own world switch for protected VMs but has no primitives to move the per-vCPU VGIC state between the host and hypervisor vCPU contexts. Add flush_hyp_vgic_state() and sync_hyp_vgic_state(). Flush copies vgic_hcr, the in-use list registers and used_lrs from the host into the hyp vCPU and pins vgic_sre to a fixed value; sync copies vgic_hcr, vgic_vmcr and the in-use list registers back. The active priority registers are handled separately by the save/restore-aprs path. Bound used_lrs by hyp_gicv3_nr_lr, the cached implemented-LR count, instead of reading ICH_VTR_EL2 on each entry. That clamps the host-supplied value and avoids a per-entry sysreg read that is costly under NV. Reviewed-by: Vincent Donnefort Signed-off-by: Marc Zyngier Co-developed-by: Fuad Tabba Signed-off-by: Fuad Tabba --- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 55 ++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/h= yp-main.c index f3233ee343a39..2db56146ec493 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -104,6 +104,45 @@ static void fpsimd_sve_sync(struct kvm_vcpu *vcpu) *host_data_ptr(fp_owner) =3D FP_STATE_HOST_OWNED; } =20 +static void flush_hyp_vgic_state(struct pkvm_hyp_vcpu *hyp_vcpu) +{ + struct kvm_vcpu *host_vcpu =3D hyp_vcpu->host_vcpu; + struct vgic_v3_cpu_if *host_cpu_if, *hyp_cpu_if; + unsigned int used_lrs, i; + + host_cpu_if =3D &host_vcpu->arch.vgic_cpu.vgic_v3; + hyp_cpu_if =3D &hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3; + + used_lrs =3D host_cpu_if->used_lrs; + used_lrs =3D min(used_lrs, hyp_gicv3_nr_lr); + + hyp_cpu_if->vgic_hcr =3D host_cpu_if->vgic_hcr; + /* Should be a one-off */ + hyp_cpu_if->vgic_sre =3D (ICC_SRE_EL1_DIB | + ICC_SRE_EL1_DFB | + ICC_SRE_EL1_SRE); + hyp_cpu_if->used_lrs =3D used_lrs; + + for (i =3D 0; i < used_lrs; i++) + hyp_cpu_if->vgic_lr[i] =3D host_cpu_if->vgic_lr[i]; +} + +static void sync_hyp_vgic_state(struct pkvm_hyp_vcpu *hyp_vcpu) +{ + struct kvm_vcpu *host_vcpu =3D hyp_vcpu->host_vcpu; + struct vgic_v3_cpu_if *host_cpu_if, *hyp_cpu_if; + unsigned int i; + + host_cpu_if =3D &host_vcpu->arch.vgic_cpu.vgic_v3; + hyp_cpu_if =3D &hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3; + + host_cpu_if->vgic_hcr =3D hyp_cpu_if->vgic_hcr; + host_cpu_if->vgic_vmcr =3D hyp_cpu_if->vgic_vmcr; + + for (i =3D 0; i < hyp_cpu_if->used_lrs; i++) + host_cpu_if->vgic_lr[i] =3D hyp_cpu_if->vgic_lr[i]; +} + static void flush_debug_state(struct pkvm_hyp_vcpu *hyp_vcpu) { struct kvm_vcpu *host_vcpu =3D hyp_vcpu->host_vcpu; @@ -152,13 +191,7 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_v= cpu) =20 hyp_vcpu->vcpu.arch.vsesr_el2 =3D host_vcpu->arch.vsesr_el2; =20 - hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 =3D 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 =3D - min_t(unsigned int, - hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs, - hyp_gicv3_nr_lr); + flush_hyp_vgic_state(hyp_vcpu); =20 hyp_vcpu->vcpu.arch.pid =3D host_vcpu->arch.pid; } @@ -166,9 +199,6 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vc= pu) static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) { struct kvm_vcpu *host_vcpu =3D hyp_vcpu->host_vcpu; - struct vgic_v3_cpu_if *hyp_cpu_if =3D &hyp_vcpu->vcpu.arch.vgic_cpu.vgic_= v3; - struct vgic_v3_cpu_if *host_cpu_if =3D &host_vcpu->arch.vgic_cpu.vgic_v3; - unsigned int i; =20 fpsimd_sve_sync(&hyp_vcpu->vcpu); sync_debug_state(hyp_vcpu); @@ -181,10 +211,7 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vc= pu) =20 host_vcpu->arch.iflags =3D hyp_vcpu->vcpu.arch.iflags; =20 - host_cpu_if->vgic_hcr =3D hyp_cpu_if->vgic_hcr; - host_cpu_if->vgic_vmcr =3D hyp_cpu_if->vgic_vmcr; - for (i =3D 0; i < hyp_cpu_if->used_lrs; ++i) - host_cpu_if->vgic_lr[i] =3D hyp_cpu_if->vgic_lr[i]; + sync_hyp_vgic_state(hyp_vcpu); } =20 static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt) --=20 2.39.5 From nobody Sat Jul 25 19:28:29 2026 Received: from out-180.mta0.migadu.com (out-180.mta0.migadu.com [91.218.175.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 395613655FD for ; Tue, 14 Jul 2026 10:16:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024218; cv=none; b=iPEVgugoUFksa8ATBrGhQaH7SkFDDrcF8gSZx7nCzYUfvChYi61x3nbEKp/gmcqDb8edicMLKrMiAXLtKoZLdnSyfwff8VVCUsx1D5ThZNo2qfdJem8vrcDa7rwk3680A3BS6qIb/XjQtiWrFh/ln2V0SjpPHBwN8JZLGi104pU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784024218; c=relaxed/simple; bh=KHoRdv3Hg+pvnrdZze9XTBUzwaBdZCrXDxKzVYjDt60=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=H6cv2x/7vZxUKkLj2SVyUBHEyzfdti4JrX8lwNvklWtgrAxO1LTA26cpBuUV9BwdDd7xUH8Ubnj6g86lpVur1fjEROrVlqgUa5F3PXr54LZqs8qf+38jhbbEJW+jjkMj3lBCQOd9AyVteSg+TPc63m23n2NZpir/1aAMdya+6cg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=ipjjPeGi; arc=none smtp.client-ip=91.218.175.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="ipjjPeGi" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1784024214; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=UU+Pmv5r0WbDeQoGhRdEXjtelRASRBLHbQYoRLyEwZg=; b=ipjjPeGiI/F7LWJSlGP1mx4eXs2w7Lt7OzAGzJnb+bI1sY+vdkFXPADnJcLUisWBykoLi5 YEElDSqqUVmkx6Nh5Md45dLZ0H9/d8f5R9/wGlUVHzPNe6IMal7Ck+7v3o6u2i/lt73/r6 x5idZRU17xOAoE/ZrQ4JQS6nB4+tk4c= From: Fuad Tabba To: Marc Zyngier , Oliver Upton , kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Cc: Catalin Marinas , Will Deacon , Joey Gouly , Steffen Eiden , Suzuki K Poulose , Zenghui Yu , Vincent Donnefort , Quentin Perret , Sebastian Ene , Hyunwoo Kim , Fuad Tabba Subject: [PATCH v5 8/8] KVM: arm64: Implement lazy vCPU state sync for non-protected guests Date: Tue, 14 Jul 2026 11:16:01 +0100 Message-Id: <20260714101601.4142645-9-fuad.tabba@linux.dev> In-Reply-To: <20260714101601.4142645-1-fuad.tabba@linux.dev> References: <20260714101601.4142645-1-fuad.tabba@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" pKVM copies a non-protected guest's register context between the host and the hypervisor on every world switch, even when the host never inspects it. Defer the copy: on entry, flush the host context into the hyp vCPU only when the host marked it dirty (PKVM_HOST_STATE_DIRTY); on exit, leave it in the hyp vCPU and copy it back only when the host needs it, via a __pkvm_vcpu_sync_state hypercall or at vcpu put. A protected guest's context is copied as before, since lazy sync only helps where the host is trusted to see the guest's registers. PC and PSTATE are the exception: they are copied back on every exit so the kvm_exit tracepoint reports the guest's real exit PC, and the run loop's vcpu_mode_is_bad_32bit() and SError-masking checks evaluate the guest's current PSTATE rather than the value left by the previous sync. The host needs the full context when it is about to read it (trap handling) or write it (the SError injection that writes ESR_EL1). Sync both from handle_exit_early(), which runs non-preemptible so the loaded hyp vCPU is stable without a preempt guard. Reviewed-by: Vincent Donnefort Signed-off-by: Fuad Tabba --- arch/arm64/include/asm/kvm_asm.h | 1 + arch/arm64/include/asm/kvm_host.h | 2 + arch/arm64/kvm/arm.c | 7 +++ arch/arm64/kvm/handle_exit.c | 23 ++++++++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 86 ++++++++++++++++++++++++++++-- 5 files changed, 114 insertions(+), 5 deletions(-) diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_= asm.h index 043495f7fc78b..6e1135b3ded44 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -113,6 +113,7 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___pkvm_finalize_teardown_vm, __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_load, __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_put, + __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_sync_state, __KVM_HOST_SMCCC_FUNC___pkvm_tlb_flush_vmid, =20 MARKER(__KVM_HOST_SMCCC_FUNC_MAX) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm= _host.h index f3c3c86b3d7fb..1b81bc0a6b374 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -1051,6 +1051,8 @@ struct kvm_vcpu_arch { #define INCREMENT_PC __vcpu_single_flag(iflags, BIT(1)) /* Target EL/MODE (not a single flag, but let's abuse the macro) */ #define EXCEPT_MASK __vcpu_single_flag(iflags, GENMASK(3, 1)) +/* Host-set: the hyp flushes the non-protected vCPU state in on entry */ +#define PKVM_HOST_STATE_DIRTY __vcpu_single_flag(iflags, BIT(4)) =20 /* Helpers to encode exceptions with minimum fuss */ #define __EXCEPT_MASK_VAL unpack_vcpu_flag(EXCEPT_MASK) diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 50adfff75be82..ace7b67fe1cc8 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -735,6 +735,10 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) if (is_protected_kvm_enabled()) { kvm_call_hyp(__vgic_v3_save_aprs, &vcpu->arch.vgic_cpu.vgic_v3); kvm_call_hyp_nvhe(__pkvm_vcpu_put); + + /* __pkvm_vcpu_put implies a sync of the state */ + if (!kvm_vm_is_protected(vcpu->kvm)) + vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY); } =20 kvm_vcpu_put_debug(vcpu); @@ -966,6 +970,9 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu) return ret; =20 if (is_protected_kvm_enabled()) { + /* Start with the vcpu in a dirty state */ + if (!kvm_vm_is_protected(vcpu->kvm)) + vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY); ret =3D pkvm_create_hyp_vm(kvm); if (ret) return ret; diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c index 54aedf93c78b6..29108e5c0206e 100644 --- a/arch/arm64/kvm/handle_exit.c +++ b/arch/arm64/kvm/handle_exit.c @@ -486,9 +486,32 @@ int handle_exit(struct kvm_vcpu *vcpu, int exception_i= ndex) } } =20 +static void handle_exit_pkvm_state(struct kvm_vcpu *vcpu, int exception_in= dex) +{ + int exception_code =3D ARM_EXCEPTION_CODE(exception_index); + + if (!is_protected_kvm_enabled() || kvm_vm_is_protected(vcpu->kvm)) + return; + + /* + * Sync the context back when the host will read (trap) or write + * (SError) it. Preempt-off here, so the loaded hyp vCPU is stable. + */ + if (exception_code =3D=3D ARM_EXCEPTION_TRAP || + exception_code =3D=3D ARM_EXCEPTION_EL1_SERROR || + ARM_SERROR_PENDING(exception_index)) { + kvm_call_hyp_nvhe(__pkvm_vcpu_sync_state); + vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY); + } else { + vcpu_clear_flag(vcpu, PKVM_HOST_STATE_DIRTY); + } +} + /* For exit types that need handling before we can be preempted */ void handle_exit_early(struct kvm_vcpu *vcpu, int exception_index) { + handle_exit_pkvm_state(vcpu, exception_index); + if (ARM_SERROR_PENDING(exception_index)) { if (this_cpu_has_cap(ARM64_HAS_RAS_EXTN)) { u64 disr =3D kvm_vcpu_get_disr(vcpu); diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/h= yp-main.c index 2db56146ec493..4a8445b636e60 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -143,6 +143,48 @@ static void sync_hyp_vgic_state(struct pkvm_hyp_vcpu *= hyp_vcpu) host_cpu_if->vgic_lr[i] =3D hyp_cpu_if->vgic_lr[i]; } =20 +static void __copy_vcpu_state(const struct kvm_vcpu *from_vcpu, + struct kvm_vcpu *to_vcpu) +{ + int i; + + to_vcpu->arch.ctxt.regs =3D from_vcpu->arch.ctxt.regs; + to_vcpu->arch.ctxt.spsr_abt =3D from_vcpu->arch.ctxt.spsr_abt; + to_vcpu->arch.ctxt.spsr_und =3D from_vcpu->arch.ctxt.spsr_und; + to_vcpu->arch.ctxt.spsr_irq =3D from_vcpu->arch.ctxt.spsr_irq; + to_vcpu->arch.ctxt.spsr_fiq =3D from_vcpu->arch.ctxt.spsr_fiq; + to_vcpu->arch.ctxt.fp_regs =3D from_vcpu->arch.ctxt.fp_regs; + + /* + * Copy the sysregs, but don't mess with the timer state which + * is directly handled by EL1 and is expected to be preserved. + * enum vcpu_sysreg is sparse: VNCR-mapped registers take values + * derived from their VNCR page offset, so the timer registers do + * not form a contiguous numeric range and must be skipped by name. + */ + for (i =3D 1; i < NR_SYS_REGS; i++) { + switch (i) { + case CNTVOFF_EL2: + case CNTV_CVAL_EL0: + case CNTV_CTL_EL0: + case CNTP_CVAL_EL0: + case CNTP_CTL_EL0: + continue; + } + to_vcpu->arch.ctxt.sys_regs[i] =3D from_vcpu->arch.ctxt.sys_regs[i]; + } +} + +static void sync_hyp_vcpu_state(struct pkvm_hyp_vcpu *hyp_vcpu) +{ + __copy_vcpu_state(&hyp_vcpu->vcpu, hyp_vcpu->host_vcpu); +} + +static void flush_hyp_vcpu_state(struct pkvm_hyp_vcpu *hyp_vcpu) +{ + __copy_vcpu_state(hyp_vcpu->host_vcpu, &hyp_vcpu->vcpu); +} + static void flush_debug_state(struct pkvm_hyp_vcpu *hyp_vcpu) { struct kvm_vcpu *host_vcpu =3D hyp_vcpu->host_vcpu; @@ -172,7 +214,17 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_v= cpu) fpsimd_sve_flush(); flush_debug_state(hyp_vcpu); =20 - hyp_vcpu->vcpu.arch.ctxt =3D host_vcpu->arch.ctxt; + /* + * If we deal with a non-protected guest and the state is potentially + * dirty (from a host perspective), copy the state back into the hyp + * vcpu. + */ + if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu)) { + if (vcpu_get_flag(host_vcpu, PKVM_HOST_STATE_DIRTY)) + flush_hyp_vcpu_state(hyp_vcpu); + } else { + hyp_vcpu->vcpu.arch.ctxt =3D host_vcpu->arch.ctxt; + } =20 /* __hyp_running_vcpu must be NULL in a guest context. */ hyp_vcpu->vcpu.arch.ctxt.__hyp_running_vcpu =3D NULL; @@ -203,9 +255,13 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vc= pu) fpsimd_sve_sync(&hyp_vcpu->vcpu); sync_debug_state(hyp_vcpu); =20 - host_vcpu->arch.ctxt =3D hyp_vcpu->vcpu.arch.ctxt; - - host_vcpu->arch.hcr_el2 =3D hyp_vcpu->vcpu.arch.hcr_el2; + if (pkvm_hyp_vcpu_is_protected(hyp_vcpu)) { + host_vcpu->arch.ctxt =3D hyp_vcpu->vcpu.arch.ctxt; + } else { + /* Keep PC (tracepoint) and PSTATE (vcpu_mode_is_bad_32bit) current. */ + host_vcpu->arch.ctxt.regs.pc =3D hyp_vcpu->vcpu.arch.ctxt.regs.pc; + host_vcpu->arch.ctxt.regs.pstate =3D hyp_vcpu->vcpu.arch.ctxt.regs.pstat= e; + } =20 host_vcpu->arch.fault =3D hyp_vcpu->vcpu.arch.fault; =20 @@ -239,8 +295,27 @@ static void handle___pkvm_vcpu_put(struct kvm_cpu_cont= ext *host_ctxt) { struct pkvm_hyp_vcpu *hyp_vcpu =3D pkvm_get_loaded_hyp_vcpu(); =20 - if (hyp_vcpu) + if (hyp_vcpu) { + struct kvm_vcpu *host_vcpu =3D hyp_vcpu->host_vcpu; + + if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu) && + !vcpu_get_flag(host_vcpu, PKVM_HOST_STATE_DIRTY)) { + sync_hyp_vcpu_state(hyp_vcpu); + } + pkvm_put_hyp_vcpu(hyp_vcpu); + } +} + +static void handle___pkvm_vcpu_sync_state(struct kvm_cpu_context *host_ctx= t) +{ + struct pkvm_hyp_vcpu *hyp_vcpu; + + hyp_vcpu =3D pkvm_get_loaded_hyp_vcpu(); + if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu)) + return; + + sync_hyp_vcpu_state(hyp_vcpu); } =20 static struct kvm_vcpu *__get_host_hyp_vcpus(struct kvm_vcpu *arg, @@ -871,6 +946,7 @@ static const hcall_t host_hcall[] =3D { HANDLE_FUNC(__pkvm_finalize_teardown_vm), HANDLE_FUNC(__pkvm_vcpu_load), HANDLE_FUNC(__pkvm_vcpu_put), + HANDLE_FUNC(__pkvm_vcpu_sync_state), HANDLE_FUNC(__pkvm_tlb_flush_vmid), }; =20 --=20 2.39.5