diff options
| author | Sascha Bischoff <sascha.bischoff@arm.com> | 2026-10-20 17:39:22 +0100 |
|---|---|---|
| committer | Marc Zyngier <maz@kernel.org> | 2026-10-26 10:57:19 +0000 |
| commit | 05620a31421935f737d16a060d2dfe731f7b2f74 (patch) | |
| tree | 5f8ebc2e24962f2b9b6c3752248e7cffeca9fbbd /arch | |
| parent | 576a1b5ab4b8be03ab5cf7b41b475850795e3fe2 (diff) | |
KVM: arm64: vgic: Require matching VGIC and ITS models
On a GICv5 host with legacy support, KVM registers both VGICv3 and
VGICv5 devices. Without a model check, a VM can create a GICv3 ITS
alongside an incompatible VGIC, regardless of creation order.
The same problem exists for GICv2 on hosts that support a GICv3 ITS.
KVM used to allow userspace to create both devices in either order,
then ignored the ITS.
Record the VGIC model required by an ITS. Reject an ITS if the VM
already has an incompatible VGIC, and reject a VGIC if an existing
ITS requires a different model. These checks apply on every host:
a GICv2 VGIC and a GICv3 ITS now fail with -ENODEV in either order.
Allow either creation order when the models match.
Have vgic_has_its() require matching VGIC and ITS models, and drop
the separate has_its flag.
As part of this change, swap the type of vgic_model from u32 to enum
kvm_device_type. This requires updating the switch cases in
kvm_vgic_vcpu_init() and kvm_vgic_dist_destroy() to include default
cases, which we mark as unreachable().
Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
Link: https://patch.msgid.link/20261020163923.1407856-2-sascha.bischoff@arm.com
Signed-off-by: Marc Zyngier <maz@kernel.org>
Diffstat (limited to 'arch')
| -rw-r--r-- | arch/arm64/kvm/vgic/vgic-init.c | 17 | ||||
| -rw-r--r-- | arch/arm64/kvm/vgic/vgic-its.c | 15 | ||||
| -rw-r--r-- | arch/arm64/kvm/vgic/vgic-mmio-v3.c | 5 |
3 files changed, 32 insertions, 5 deletions
diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 979301c3dbd8..c9991e24cb6b 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -121,6 +121,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) goto out_unlock; } + if (kvm->arch.vgic.its_model && kvm->arch.vgic.its_model != type) { + ret = -ENODEV; + goto out_unlock; + } + if (kvm_id_regs_final(kvm)) goto out_unlock; ret = 0; @@ -433,6 +438,8 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) return ret; switch (dist->vgic_model) { + case KVM_DEV_TYPE_ARM_VGIC_V2: + break; case KVM_DEV_TYPE_ARM_VGIC_V3: /* * If we are creating a VCPU with a GICv3 we must also register @@ -451,6 +458,9 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) */ if (vcpu->vcpu_id >= kvm_vgic_global_state.max_gicv5_vcpus) return -EINVAL; + break; + default: + unreachable(); } return ret; @@ -550,6 +560,10 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm) dist->nr_spis = 0; dist->vgic_dist_base = VGIC_ADDR_UNDEF; + /* A VM may be destroyed without ever creating a VGIC. */ + if (!dist->vgic_model) + goto out; + switch (dist->vgic_model) { case KVM_DEV_TYPE_ARM_VGIC_V2: dist->vgic_cpu_base = VGIC_ADDR_UNDEF; @@ -567,8 +581,11 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm) kfree(dist->vgic_v5_irs_data); dist->vgic_v5_irs_data = NULL; break; + default: + unreachable(); } +out: xa_destroy(&dist->lpi_xa); } diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index e5657cb60356..7df5869cff99 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -1875,6 +1875,19 @@ static int vgic_its_create(struct kvm_device *dev, u32 type) mutex_lock(&dev->kvm->arch.config_lock); + /* + * Reject creation of a GICv3 ITS if: + * - The VM already has a VGIC other than GICv3. + * - An existing ITS requires a VGIC model other than GICv3. + */ + if ((irqchip_in_kernel(dev->kvm) && !vgic_is_v3(dev->kvm)) || + (dev->kvm->arch.vgic.its_model && + dev->kvm->arch.vgic.its_model != KVM_DEV_TYPE_ARM_VGIC_V3)) { + mutex_unlock(&dev->kvm->arch.config_lock); + kfree(its); + return -ENODEV; + } + if (vgic_initialized(dev->kvm)) { ret = vgic_v4_init(dev->kvm); if (ret < 0) { @@ -1902,7 +1915,7 @@ static int vgic_its_create(struct kvm_device *dev, u32 type) xa_init(&its->translation_cache); dev->kvm->arch.vgic.msis_require_devid = true; - dev->kvm->arch.vgic.has_its = true; + dev->kvm->arch.vgic.its_model = KVM_DEV_TYPE_ARM_VGIC_V3; its->enabled = false; its->dev = dev; diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c index 5ab789accc9d..08f867ebc0df 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c +++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c @@ -42,10 +42,7 @@ bool vgic_has_its(struct kvm *kvm) { struct vgic_dist *dist = &kvm->arch.vgic; - if (dist->vgic_model != KVM_DEV_TYPE_ARM_VGIC_V3) - return false; - - return dist->has_its; + return dist->its_model && dist->its_model == dist->vgic_model; } bool vgic_supports_direct_msis(struct kvm *kvm) |
