From nobody Fri Oct 2 12:20:17 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 86B283431E6; Fri, 31 Jul 2026 20:46:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785530796; cv=none; b=AeHG4B4IiikMwNoIFm3WtPhfLxuH5aZrSozvSGnc302OJloRIoPzZX5Wc5WZnpt9MKcjpDEbPzf1Wv/0F+FsU5q/xX7nb0O1/d6V39io7pOsVar0qytbr7EJAr40VSpmlJ2NCkUpVI3CM/XWAd4RW+L4nQt2/8UyaA6//txXwfU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785530796; c=relaxed/simple; bh=sAoUaDyMQALfN+ONy0lnwC2Oo/ld3+Elyc6NDljIQe8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=KGAn3uT207uvHNnyYBrJ6f4ByIzopi5doQkN5Stl7JRrZzmt2jfGOI+oUZhzzIk00AKyKE7RfDhZpNae6ugRUZmCNujnBtrYO2K6AwiPkQWZZGmo8n3wWeg68cuYpHSdCSR/mZDSpYsKMAgGsBz7slJv8ViXTLg75wbi5VlN2dY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=D9UAAfF7; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="D9UAAfF7" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9B9F91F00ACA; Fri, 31 Jul 2026 20:46:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785530795; bh=N7URY0/P+czEc9oAhoLdople5LjdnyrNihUG3Il3dJU=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=D9UAAfF7luiusONfjIjp5ztWVObeyNL9uioaS3MQGp4R8xHXkHY3xitNXurTZ3Hzt cGnFgHa5Z1qciKhJnG09nh23s3Y3f2yQ7YvxbvXhrpqkMbx3m0m7sQCHsp8AzMiu+n BkAZ7wfFduWlUbTd+owNyPFLT1BYsw0MwZJ04IEOpnApGPobHIpN1snLeC891hpOxQ pSlFUqRiQjf5YMcWhdfmtGTDpNqSUtQh1LDovPwab6PkQkdu9cknEEerXjdAdTV4T2 rtt8GYibZM/QoDbdoZRbBRuN2LsYFzY2grn4soTc8KL0HApxDcMfGX9jnu3ZtEeIs0 SwdYNWGJDkvUg== From: Mark Brown Date: Fri, 31 Jul 2026 21:44:45 +0100 Subject: [PATCH 1/2] KVM: arm64: Finalize guest-wide sysregs prior to per-vCPU sysregs Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260731-kvm-arm64-idreg-final-v1-1-3de2a5616dc6@kernel.org> References: <20260731-kvm-arm64-idreg-final-v1-0-3de2a5616dc6@kernel.org> In-Reply-To: <20260731-kvm-arm64-idreg-final-v1-0-3de2a5616dc6@kernel.org> To: Marc Zyngier , Oliver Upton , Fuad Tabba , Joey Gouly , Steffen Eiden , Suzuki K Poulose , Zenghui Yu , Catalin Marinas , Will Deacon Cc: Peter Maydell , linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-kernel@vger.kernel.org, Mark Brown X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=3763; i=broonie@kernel.org; h=from:subject:message-id; bh=sAoUaDyMQALfN+ONy0lnwC2Oo/ld3+Elyc6NDljIQe8=; b=owEBbQGS/pANAwAKASTWi3JdVIfQAcsmYgBqbQmjVfAtLU1PVQEawt81+Ms/TGa8+XaODI4a2 G2AkmizqkeJATMEAAEKAB0WIQSt5miqZ1cYtZ/in+ok1otyXVSH0AUCam0JowAKCRAk1otyXVSH 0Md5B/wJbunxy6e8xmvSR3LbfR+dQxZaiqJOtOpg6p3QiPEofddW2Ub1ZQ76Z9hBJPcOXwi+Bwr NgBtQtXlOZ77TME3oNb8ysDKB+AtcoIiCCOhq719E5KdzXiE1GDbHp32Cv6BNBTDKPDtx22e1ZA 8mtrINZFc1b9jpXH8tfuGmw3FUBpfcmfIyEs5pRk4sIB03qTkJxH5d37woTzyVY3BeV5ppxle+9 tzKDSEjdBkxG5hsqQaOZrbLZra/1EL9L7UUCeNPw2/06V8+GIsjacPlQoixJvI7PrzcNOQF39I6 TqtoDEuAwZKdaHJjEcLtJH2FD0bB0r/Pep0guznGosfZ3SRn X-Developer-Key: i=broonie@kernel.org; a=openpgp; fpr=3F2568AAC26998F9E813A1C5C3F436CA30F5D8EB In commit d82d09d5ba4b ("KVM: arm64: Don't skip per-vcpu NV initialisation") the NV register sanitisation was moved earlier in kvm_finalize_sys_regs() so that it runs for each vCPU rather than only once per guest. This means that for the first vCPU it runs prior to vGIC finalization, but the vGIC finalization updates the ID registers which the NV initialization uses so we may end up with a mismatch. For example, HFGRTR_EL2.ICC_IGRPENn_EL1 depends on GICv3 being enabled in ID_AA64PFR0_EL1.GIC so may be mistakenly marked or not marked as RES0. Split the initialization which runs once per guest into a separate function and run that before the per-vCPU initialisation for NV, renaming the per-vCPU function to make it clear that it does per-vCPU setup. Fixes: d82d09d5ba4b ("KVM: arm64: Don't skip per-vcpu NV initialisation") Signed-off-by: Mark Brown --- arch/arm64/kvm/arm.c | 2 +- arch/arm64/kvm/sys_regs.c | 40 ++++++++++++++++++++++++++-------------- arch/arm64/kvm/sys_regs.h | 2 +- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 50adfff75be8..2b75e1d5ca8d 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -931,7 +931,7 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu) return ret; } =20 - ret =3D kvm_finalize_sys_regs(vcpu); + ret =3D kvm_vcpu_finalize_sys_regs(vcpu); if (ret) return ret; =20 diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 5d5c579d4579..958d7ef78785 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -5755,25 +5755,14 @@ void kvm_calculate_traps(struct kvm_vcpu *vcpu) } =20 /* - * Perform last adjustments to the ID registers that are implied by the + * Do system register finalization that is shared by the whole guest. This + * includes last adjustments to the ID registers that are implied by the * configuration outside of the ID regs themselves, as well as any * initialisation that directly depend on these ID registers (such as * RES0/RES1 behaviours). This is not the place to configure traps though. - * - * Because this can be called once per CPU, changes must be idempotent. */ -int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu) +static int kvm_vm_finalize_sys_regs(struct kvm *kvm) { - struct kvm *kvm =3D vcpu->kvm; - - guard(mutex)(&kvm->arch.config_lock); - - if (vcpu_has_nv(vcpu)) { - int ret =3D kvm_init_nv_sysregs(vcpu); - if (ret) - return ret; - } - if (kvm_vm_has_ran_once(kvm)) return 0; =20 @@ -5825,6 +5814,29 @@ int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu) return 0; } =20 +/* + * Because this can be called once per CPU, changes must be idempotent. + */ +int kvm_vcpu_finalize_sys_regs(struct kvm_vcpu *vcpu) +{ + struct kvm *kvm =3D vcpu->kvm; + int ret; + + guard(mutex)(&kvm->arch.config_lock); + + ret =3D kvm_vm_finalize_sys_regs(kvm); + if (ret) + return ret; + + if (vcpu_has_nv(vcpu)) { + ret =3D kvm_init_nv_sysregs(vcpu); + if (ret) + return ret; + } + + return 0; +} + int __init kvm_sys_reg_table_init(void) { const struct sys_reg_desc *gicv3_regs; diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h index 2a983664220c..402c5774d916 100644 --- a/arch/arm64/kvm/sys_regs.h +++ b/arch/arm64/kvm/sys_regs.h @@ -235,7 +235,7 @@ int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const s= truct kvm_one_reg *reg, =20 bool triage_sysreg_trap(struct kvm_vcpu *vcpu, int *sr_index); =20 -int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu); +int kvm_vcpu_finalize_sys_regs(struct kvm_vcpu *vcpu); =20 #define AA32(_x) .aarch32_map =3D AA32_##_x #define Op0(_x) .Op0 =3D _x --=20 2.47.3 From nobody Fri Oct 2 12:20:17 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 504313033E1; Fri, 31 Jul 2026 20:46:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785530799; cv=none; b=WqgydxG4VhsSvBQvw5BtRv6YaIRY3W9qTSn0DbutJIAtbVlClMYRn7E0rFLQw9xjbpEURyPrKWmOpKQ2yoHBzWMQWQQOwdCAQpdqRG8WyekAikU4f8mlmVxDS+f77lpKXmCFAUtZi7pbme40uAQ2645puQ+O976Ji/mteJM94VI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785530799; c=relaxed/simple; bh=ZYIdTyS6+gTxOz7kuPB5KP6ZwC/NiAAsdSBfmdRN8oc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=J6F3zjjjJMzaPHU1zdEcNCoqk5B6w8u8lTzIpgNM2BzQCtdf19SOdfFAN7oy7Yfk0LCGl5zzkUqLTyylpTeRQYPrZw3CvqphYnF+vmJAujjQxiWaeBdwXSAJ2UiTx7GG1qAdeOLej9oliF90/+q4+2enNr0oGg7P5RoTiJKheyg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WS+nR94d; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="WS+nR94d" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9EE221F00AC4; Fri, 31 Jul 2026 20:46:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785530798; bh=1788FpKq2MQsvrC2BttQ63hnkLjcQiCKybYoWgRjOgQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=WS+nR94dCib0xZGZnyZdvSxQnjqD+bKYY5jlAmNsfLWUGachUjSoPgyycuvKhCv+1 p99jXfr320l9VxbC3bYp55zHdDc0lWDsrs3vXz0YPmRNXqwdtuyjyNFJsgrPog5gNo TBMtb4OG5enIxZAbxHuRw644sMAWd+4kgmMtAT+6w8aGWGBtpzgPqahHGi3fp062Sy CSAIBeUkVBdG1OTlkyB1AwtHN/RYxpOHn2Jzh/EOYD494qEUbS9pcP5P2hPsMPwOUt euYI+hsptfx7QNhutWauFB9XpvpKmRxhTqvkk2+I5KKautd4aQMqshGJNLW6Ami95Y C2re7gOH0TfFg== From: Mark Brown Date: Fri, 31 Jul 2026 21:44:46 +0100 Subject: [PATCH 2/2] KVM: arm64: Block ID register changes after we rely on the values Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260731-kvm-arm64-idreg-final-v1-2-3de2a5616dc6@kernel.org> References: <20260731-kvm-arm64-idreg-final-v1-0-3de2a5616dc6@kernel.org> In-Reply-To: <20260731-kvm-arm64-idreg-final-v1-0-3de2a5616dc6@kernel.org> To: Marc Zyngier , Oliver Upton , Fuad Tabba , Joey Gouly , Steffen Eiden , Suzuki K Poulose , Zenghui Yu , Catalin Marinas , Will Deacon Cc: Peter Maydell , linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-kernel@vger.kernel.org, Mark Brown X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=5514; i=broonie@kernel.org; h=from:subject:message-id; bh=ZYIdTyS6+gTxOz7kuPB5KP6ZwC/NiAAsdSBfmdRN8oc=; b=owEBbQGS/pANAwAKASTWi3JdVIfQAcsmYgBqbQmkQTFNHE/X7jyM1RgY6o2RlLj5bNDIwZVxk gYIxajU7HiJATMEAAEKAB0WIQSt5miqZ1cYtZ/in+ok1otyXVSH0AUCam0JpAAKCRAk1otyXVSH 0N76B/kBW8hT2mUncxHGhc3FwXoG/eT1fkFUlw/OZJ9q1wCEuJKuGwkrarYovvT4bOCCpurY3L2 TWSR9PWfZwYC46nCJvj5FIYymhLSZS2HbBblqZf+MWY3HQwWti9s9HGJ0DJ7F+XCle9iwQMuXKA 810bC9rqO5559zQIMB5C4KiDMb6ycE/5lz1iY/BhScMqn93zdPiHfFwkOSyGIekWdoxNYKxer+Z TBJ3xIHv7oJD3tDhe0PeufT0P2v7HBMKyCtQIJKlpFD4vSvmlD0QI+IbN6kfYuqJO8Tp/IGiI7O 9XlQKDVzfyn+mVWNPqCnSKoAdSF0LQox7ZUZCIoOGVYL7o0I X-Developer-Key: i=broonie@kernel.org; a=openpgp; fpr=3F2568AAC26998F9E813A1C5C3F436CA30F5D8EB In commit c5bac1ef7df6b ("KVM: arm64: Move existing feature disabling over to FGU infrastructure") a check was added to suppress duplicate recalculation of FGUs based on a flag KVM_ARCH_FLAG_FGU_INITIALIZED. This flag is set when we complete kvm_calculate_traps(), which is called from kvm_arch_vcpu_run_pid_change(). There are several points where that function could fail after we have calculated FGUs (eg, due to an invalid timer configuration). If this happens then userspace will still be able to write to the ID registers, writes to which are gated on KVM_ARCH_FLAG_HAS_RAN_ONCE being set. This in turn means that the FGU configuration for a running guest may not match the ID register configuration. This will result in issues based on the hypervisor assuming a consistent configuration, for example it allows the creation of guests which have untrapped access to system registers which are not context switched for the guest. A similar issue exists in kvm_init_nv_sysregs() where once sysreg_masks is allocated the RES0/RES1 masks for registers are fixed based on the ID register values at the time the function ran, and also for copying the implementation ID registers to the hypervisor for pKVM. There is a further issue with vGIC setup, creating a vGIC includes updating the ID registers to reflect the GIC configuration. We refuse to create a vGIC after the first vCPU has run but if a vCPU fails its first run we may already have finalized the ID register values. Avoid these issues by adding a new flag that we set when we finalize the system registers, blocking ID register changes after that has been set even if something fails later on. Do this in kvm_vm_finalize_sys_regs(), this is where we finalize the GIC fields in the ID registers and happens before we do the FGU and RES0/1 setup. A VMM which tries to create an irqchip after failing to run a vCPU will now get -EBUSY rather than a likely misconfigured guest. Userspace is not expected to try to run a guest that fails to start, never mind try to repair the guest configuration after doing so, so this is not expected to have any impact on practical users. Fixes: c5bac1ef7df6b ("KVM: arm64: Move existing feature disabling over to = FGU infrastructure") Fixes: 888f088070229 ("KVM: arm64: nv: Add sanitising to VNCR-backed sysreg= s") Fixes: 03e1b89d051f ("KVM: arm64: Copy MIDR_EL1 into hyp VM when it is writ= able") Signed-off-by: Mark Brown --- arch/arm64/include/asm/kvm_host.h | 8 ++++++++ arch/arm64/kvm/sys_regs.c | 13 ++++++++----- arch/arm64/kvm/vgic/vgic-init.c | 6 ++---- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm= _host.h index bae2c4f92ef5..8c8f7d83b6ff 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -367,6 +367,8 @@ struct kvm_arch { #define KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS 10 /* Unhandled SEAs are taken to userspace */ #define KVM_ARCH_FLAG_EXIT_SEA 11 + /* No further ID register changes possible */ +#define KVM_ARCH_FLAG_ID_REGS_FINAL 12 unsigned long flags; =20 /* VM-wide vCPU feature set */ @@ -1143,6 +1145,12 @@ struct kvm_vcpu_arch { #define vcpu_has_ptrauth(vcpu) false #endif =20 +#define kvm_id_regs_final(kvm) \ + test_bit(KVM_ARCH_FLAG_ID_REGS_FINAL, &(kvm)->arch.flags) + +#define vcpu_id_regs_final(vcpu) \ + kvm_id_regs_final((vcpu)->kvm) + #define vcpu_on_unsupported_cpu(vcpu) \ vcpu_get_flag(vcpu, ON_UNSUPPORTED_CPU) =20 diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 958d7ef78785..3b423db56d3c 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -2427,9 +2427,10 @@ static int set_id_reg(struct kvm_vcpu *vcpu, const s= truct sys_reg_desc *rd, =20 /* * Once the VM has started the ID registers are immutable. Reject any - * write that does not match the final register value. + * write that does not match the final register value once we have + * got far enough into first running the VM to use the values. */ - if (kvm_vm_has_ran_once(vcpu->kvm)) { + if (vcpu_id_regs_final(vcpu)) { if (val !=3D read_id_reg(vcpu, rd)) ret =3D -EBUSY; else @@ -3149,10 +3150,10 @@ static int set_imp_id_reg(struct kvm_vcpu *vcpu, co= nst struct sys_reg_desc *r, return -EINVAL; =20 /* - * Once the VM has started the ID registers are immutable. Reject the - * write if userspace tries to change it. + * Once we have been far enough into starting the VM the ID registers + * are immutable. Reject the write if userspace tries to change it. */ - if (kvm_vm_has_ran_once(kvm)) + if (kvm_id_regs_final(kvm)) return -EBUSY; =20 /* @@ -5811,6 +5812,8 @@ static int kvm_vm_finalize_sys_regs(struct kvm *kvm) kvm_vgic_finalize_idregs(kvm); } =20 + set_bit(KVM_ARCH_FLAG_ID_REGS_FINAL, &kvm->arch.flags); + return 0; } =20 diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-ini= t.c index 907057881b26..4ffe0b7c3407 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -123,10 +123,8 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) goto out_unlock; } =20 - kvm_for_each_vcpu(i, vcpu, kvm) { - if (vcpu_has_run_once(vcpu)) - goto out_unlock; - } + if (kvm_id_regs_final(kvm)) + goto out_unlock; ret =3D 0; =20 if (type =3D=3D KVM_DEV_TYPE_ARM_VGIC_V2) --=20 2.47.3