aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Auger <eric.auger@redhat.com>2018-05-22 03:55:12 -0400
committerMarc Zyngier <marc.zyngier@arm.com>2018-05-25 07:29:26 -0400
commitccc27bf5be7b78f64b67902bf27f6ef484bedc2c (patch)
tree08ea46b5b09d5a5e074ab162fe1b1fdf7a063a6f
parent028bf278d31a0d04b096ebb5340949e9fc9e2010 (diff)
KVM: arm/arm64: Helper to register a new redistributor region
We introduce a new helper that creates and inserts a new redistributor region into the rdist region list. This helper both handles the case where the redistributor region size is known at registration time and the legacy case where it is not (eventually depending on the number of online vcpus). Depending on pfns, we perform all the possible checks that we can do: - end of memory crossing - incorrect alignment of the base address - collision with distributor region if already defined - collision with already registered rdist regions - check of the new index Rdist regions must be inserted by increasing order of indices. Indices must be contiguous. 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>
-rw-r--r--virt/kvm/arm/vgic/vgic-mmio-v3.c89
-rw-r--r--virt/kvm/arm/vgic/vgic.h8
2 files changed, 81 insertions, 16 deletions
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index ce5c927fad06..3dbc057f861b 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v3.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c
@@ -680,14 +680,63 @@ static int vgic_register_all_redist_iodevs(struct kvm *kvm)
680 return ret; 680 return ret;
681} 681}
682 682
683int vgic_v3_set_redist_base(struct kvm *kvm, u64 addr) 683/**
684 * vgic_v3_insert_redist_region - Insert a new redistributor region
685 *
686 * Performs various checks before inserting the rdist region in the list.
687 * Those tests depend on whether the size of the rdist region is known
688 * (ie. count != 0). The list is sorted by rdist region index.
689 *
690 * @kvm: kvm handle
691 * @index: redist region index
692 * @base: base of the new rdist region
693 * @count: number of redistributors the region is made of (0 in the old style
694 * single region, whose size is induced from the number of vcpus)
695 *
696 * Return 0 on success, < 0 otherwise
697 */
698static int vgic_v3_insert_redist_region(struct kvm *kvm, uint32_t index,
699 gpa_t base, uint32_t count)
684{ 700{
685 struct vgic_dist *vgic = &kvm->arch.vgic; 701 struct vgic_dist *d = &kvm->arch.vgic;
686 struct vgic_redist_region *rdreg; 702 struct vgic_redist_region *rdreg;
703 struct list_head *rd_regions = &d->rd_regions;
704 size_t size = count * KVM_VGIC_V3_REDIST_SIZE;
687 int ret; 705 int ret;
688 706
689 /* vgic_check_ioaddr makes sure we don't do this twice */ 707 /* single rdist region already set ?*/
690 if (!list_empty(&vgic->rd_regions)) 708 if (!count && !list_empty(rd_regions))
709 return -EINVAL;
710
711 /* cross the end of memory ? */
712 if (base + size < base)
713 return -EINVAL;
714
715 if (list_empty(rd_regions)) {
716 if (index != 0)
717 return -EINVAL;
718 } else {
719 rdreg = list_last_entry(rd_regions,
720 struct vgic_redist_region, list);
721 if (index != rdreg->index + 1)
722 return -EINVAL;
723
724 /* Cannot add an explicitly sized regions after legacy region */
725 if (!rdreg->count)
726 return -EINVAL;
727 }
728
729 /*
730 * For legacy single-region redistributor regions (!count),
731 * check that the redistributor region does not overlap with the
732 * distributor's address space.
733 */
734 if (!count && !IS_VGIC_ADDR_UNDEF(d->vgic_dist_base) &&
735 vgic_dist_overlap(kvm, base, size))
736 return -EINVAL;
737
738 /* collision with any other rdist region? */
739 if (vgic_v3_rdist_overlap(kvm, base, size))
691 return -EINVAL; 740 return -EINVAL;
692 741
693 rdreg = kzalloc(sizeof(*rdreg), GFP_KERNEL); 742 rdreg = kzalloc(sizeof(*rdreg), GFP_KERNEL);
@@ -696,17 +745,29 @@ int vgic_v3_set_redist_base(struct kvm *kvm, u64 addr)
696 745
697 rdreg->base = VGIC_ADDR_UNDEF; 746 rdreg->base = VGIC_ADDR_UNDEF;
698 747
699 ret = vgic_check_ioaddr(kvm, &rdreg->base, addr, SZ_64K); 748 ret = vgic_check_ioaddr(kvm, &rdreg->base, base, SZ_64K);
700 if (ret) 749 if (ret)
701 goto out; 750 goto free;
702 751
703 rdreg->base = addr; 752 rdreg->base = base;
704 if (!vgic_v3_check_base(kvm)) { 753 rdreg->count = count;
705 ret = -EINVAL; 754 rdreg->free_index = 0;
706 goto out; 755 rdreg->index = index;
707 }
708 756
709 list_add(&rdreg->list, &vgic->rd_regions); 757 list_add_tail(&rdreg->list, rd_regions);
758 return 0;
759free:
760 kfree(rdreg);
761 return ret;
762}
763
764int vgic_v3_set_redist_base(struct kvm *kvm, u64 addr)
765{
766 int ret;
767
768 ret = vgic_v3_insert_redist_region(kvm, 0, addr, 0);
769 if (ret)
770 return ret;
710 771
711 /* 772 /*
712 * Register iodevs for each existing VCPU. Adding more VCPUs 773 * Register iodevs for each existing VCPU. Adding more VCPUs
@@ -717,10 +778,6 @@ int vgic_v3_set_redist_base(struct kvm *kvm, u64 addr)
717 return ret; 778 return ret;
718 779
719 return 0; 780 return 0;
720
721out:
722 kfree(rdreg);
723 return ret;
724} 781}
725 782
726int vgic_v3_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr) 783int vgic_v3_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr)
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index e9f192660097..1c8af4e4131c 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -286,6 +286,14 @@ vgic_v3_rd_region_size(struct kvm *kvm, struct vgic_redist_region *rdreg)
286} 286}
287bool vgic_v3_rdist_overlap(struct kvm *kvm, gpa_t base, size_t size); 287bool vgic_v3_rdist_overlap(struct kvm *kvm, gpa_t base, size_t size);
288 288
289static inline bool vgic_dist_overlap(struct kvm *kvm, gpa_t base, size_t size)
290{
291 struct vgic_dist *d = &kvm->arch.vgic;
292
293 return (base + size > d->vgic_dist_base) &&
294 (base < d->vgic_dist_base + KVM_VGIC_V3_DIST_SIZE);
295}
296
289int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its, 297int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its,
290 u32 devid, u32 eventid, struct vgic_irq **irq); 298 u32 devid, u32 eventid, struct vgic_irq **irq);
291struct vgic_its *vgic_msi_to_its(struct kvm *kvm, struct kvm_msi *msi); 299struct vgic_its *vgic_msi_to_its(struct kvm *kvm, struct kvm_msi *msi);