in kvm/vgic/vgic-mmio-v3.c [796:862]
static int vgic_v3_alloc_redist_region(struct kvm *kvm, uint32_t index,
gpa_t base, uint32_t count)
{
struct vgic_dist *d = &kvm->arch.vgic;
struct vgic_redist_region *rdreg;
struct list_head *rd_regions = &d->rd_regions;
int nr_vcpus = atomic_read(&kvm->online_vcpus);
size_t size = count ? count * KVM_VGIC_V3_REDIST_SIZE
: nr_vcpus * KVM_VGIC_V3_REDIST_SIZE;
int ret;
/* cross the end of memory ? */
if (base + size < base)
return -EINVAL;
if (list_empty(rd_regions)) {
if (index != 0)
return -EINVAL;
} else {
rdreg = list_last_entry(rd_regions,
struct vgic_redist_region, list);
/* Don't mix single region and discrete redist regions */
if (!count && rdreg->count)
return -EINVAL;
if (!count)
return -EEXIST;
if (index != rdreg->index + 1)
return -EINVAL;
}
/*
* For legacy single-region redistributor regions (!count),
* check that the redistributor region does not overlap with the
* distributor's address space.
*/
if (!count && !IS_VGIC_ADDR_UNDEF(d->vgic_dist_base) &&
vgic_dist_overlap(kvm, base, size))
return -EINVAL;
/* collision with any other rdist region? */
if (vgic_v3_rdist_overlap(kvm, base, size))
return -EINVAL;
rdreg = kzalloc(sizeof(*rdreg), GFP_KERNEL_ACCOUNT);
if (!rdreg)
return -ENOMEM;
rdreg->base = VGIC_ADDR_UNDEF;
ret = vgic_check_iorange(kvm, rdreg->base, base, SZ_64K, size);
if (ret)
goto free;
rdreg->base = base;
rdreg->count = count;
rdreg->free_index = 0;
rdreg->index = index;
list_add_tail(&rdreg->list, rd_regions);
return 0;
free:
kfree(rdreg);
return ret;
}