Commit 028bf278d31a0d04b096ebb5340949e9fc9e2010

Authored by Eric Auger
Committed by Marc Zyngier
1 parent ba7b3f1275

KVM: arm/arm64: Adapt vgic_v3_check_base to multiple rdist regions

vgic_v3_check_base() currently only handles the case of a unique
legacy redistributor region whose size is not explicitly set but
inferred, instead, from the number of online vcpus.

We adapt it to handle the case of multiple redistributor regions
with explicitly defined size. We rely on two new helpers:
- vgic_v3_rdist_overlap() is used to detect overlap with the dist
  region if defined
- vgic_v3_rd_region_size computes the size of the redist region,
  would it be a legacy unique region or a new explicitly sized
  region.

Signed-off-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Christoffer Dall <christoffer.dall@arm.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>

Showing 2 changed files with 42 additions and 17 deletions Side-by-side Diff

virt/kvm/arm/vgic/vgic-v3.c
... ... @@ -419,6 +419,29 @@
419 419 return 0;
420 420 }
421 421  
  422 +/**
  423 + * vgic_v3_rdist_overlap - check if a region overlaps with any
  424 + * existing redistributor region
  425 + *
  426 + * @kvm: kvm handle
  427 + * @base: base of the region
  428 + * @size: size of region
  429 + *
  430 + * Return: true if there is an overlap
  431 + */
  432 +bool vgic_v3_rdist_overlap(struct kvm *kvm, gpa_t base, size_t size)
  433 +{
  434 + struct vgic_dist *d = &kvm->arch.vgic;
  435 + struct vgic_redist_region *rdreg;
  436 +
  437 + list_for_each_entry(rdreg, &d->rd_regions, list) {
  438 + if ((base + size > rdreg->base) &&
  439 + (base < rdreg->base + vgic_v3_rd_region_size(kvm, rdreg)))
  440 + return true;
  441 + }
  442 + return false;
  443 +}
  444 +
422 445 /*
423 446 * Check for overlapping regions and for regions crossing the end of memory
424 447 * for base addresses which have already been set.
425 448  
426 449  
427 450  
428 451  
... ... @@ -426,31 +449,23 @@
426 449 bool vgic_v3_check_base(struct kvm *kvm)
427 450 {
428 451 struct vgic_dist *d = &kvm->arch.vgic;
429   - gpa_t redist_size = KVM_VGIC_V3_REDIST_SIZE;
430   - struct vgic_redist_region *rdreg =
431   - list_first_entry(&d->rd_regions,
432   - struct vgic_redist_region, list);
  452 + struct vgic_redist_region *rdreg;
433 453  
434   - redist_size *= atomic_read(&kvm->online_vcpus);
435   -
436 454 if (!IS_VGIC_ADDR_UNDEF(d->vgic_dist_base) &&
437 455 d->vgic_dist_base + KVM_VGIC_V3_DIST_SIZE < d->vgic_dist_base)
438 456 return false;
439 457  
440   - if (rdreg && (rdreg->base + redist_size < rdreg->base))
441   - return false;
  458 + list_for_each_entry(rdreg, &d->rd_regions, list) {
  459 + if (rdreg->base + vgic_v3_rd_region_size(kvm, rdreg) <
  460 + rdreg->base)
  461 + return false;
  462 + }
442 463  
443   - /* Both base addresses must be set to check if they overlap */
444   - if (IS_VGIC_ADDR_UNDEF(d->vgic_dist_base) || !rdreg)
  464 + if (IS_VGIC_ADDR_UNDEF(d->vgic_dist_base))
445 465 return true;
446 466  
447   - if (d->vgic_dist_base + KVM_VGIC_V3_DIST_SIZE <= rdreg->base)
448   - return true;
449   -
450   - if (rdreg->base + redist_size <= d->vgic_dist_base)
451   - return true;
452   -
453   - return false;
  467 + return !vgic_v3_rdist_overlap(kvm, d->vgic_dist_base,
  468 + KVM_VGIC_V3_DIST_SIZE);
454 469 }
455 470  
456 471 /**
virt/kvm/arm/vgic/vgic.h
... ... @@ -276,6 +276,16 @@
276 276  
277 277 struct vgic_redist_region *vgic_v3_rdist_free_slot(struct list_head *rdregs);
278 278  
  279 +static inline size_t
  280 +vgic_v3_rd_region_size(struct kvm *kvm, struct vgic_redist_region *rdreg)
  281 +{
  282 + if (!rdreg->count)
  283 + return atomic_read(&kvm->online_vcpus) * KVM_VGIC_V3_REDIST_SIZE;
  284 + else
  285 + return rdreg->count * KVM_VGIC_V3_REDIST_SIZE;
  286 +}
  287 +bool vgic_v3_rdist_overlap(struct kvm *kvm, gpa_t base, size_t size);
  288 +
279 289 int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its,
280 290 u32 devid, u32 eventid, struct vgic_irq **irq);
281 291 struct vgic_its *vgic_msi_to_its(struct kvm *kvm, struct kvm_msi *msi);