int kvm_vgic_addr()

in kvm/vgic/vgic-kvm-device.c [60:160]


int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write)
{
	int r = 0;
	struct vgic_dist *vgic = &kvm->arch.vgic;
	phys_addr_t *addr_ptr, alignment, size;
	u64 undef_value = VGIC_ADDR_UNDEF;

	mutex_lock(&kvm->lock);
	switch (type) {
	case KVM_VGIC_V2_ADDR_TYPE_DIST:
		r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2);
		addr_ptr = &vgic->vgic_dist_base;
		alignment = SZ_4K;
		size = KVM_VGIC_V2_DIST_SIZE;
		break;
	case KVM_VGIC_V2_ADDR_TYPE_CPU:
		r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2);
		addr_ptr = &vgic->vgic_cpu_base;
		alignment = SZ_4K;
		size = KVM_VGIC_V2_CPU_SIZE;
		break;
	case KVM_VGIC_V3_ADDR_TYPE_DIST:
		r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3);
		addr_ptr = &vgic->vgic_dist_base;
		alignment = SZ_64K;
		size = KVM_VGIC_V3_DIST_SIZE;
		break;
	case KVM_VGIC_V3_ADDR_TYPE_REDIST: {
		struct vgic_redist_region *rdreg;

		r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3);
		if (r)
			break;
		if (write) {
			r = vgic_v3_set_redist_base(kvm, 0, *addr, 0);
			goto out;
		}
		rdreg = list_first_entry_or_null(&vgic->rd_regions,
						 struct vgic_redist_region, list);
		if (!rdreg)
			addr_ptr = &undef_value;
		else
			addr_ptr = &rdreg->base;
		break;
	}
	case KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION:
	{
		struct vgic_redist_region *rdreg;
		u8 index;

		r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3);
		if (r)
			break;

		index = *addr & KVM_VGIC_V3_RDIST_INDEX_MASK;

		if (write) {
			gpa_t base = *addr & KVM_VGIC_V3_RDIST_BASE_MASK;
			u32 count = (*addr & KVM_VGIC_V3_RDIST_COUNT_MASK)
					>> KVM_VGIC_V3_RDIST_COUNT_SHIFT;
			u8 flags = (*addr & KVM_VGIC_V3_RDIST_FLAGS_MASK)
					>> KVM_VGIC_V3_RDIST_FLAGS_SHIFT;

			if (!count || flags)
				r = -EINVAL;
			else
				r = vgic_v3_set_redist_base(kvm, index,
							    base, count);
			goto out;
		}

		rdreg = vgic_v3_rdist_region_from_index(kvm, index);
		if (!rdreg) {
			r = -ENOENT;
			goto out;
		}

		*addr = index;
		*addr |= rdreg->base;
		*addr |= (u64)rdreg->count << KVM_VGIC_V3_RDIST_COUNT_SHIFT;
		goto out;
	}
	default:
		r = -ENODEV;
	}

	if (r)
		goto out;

	if (write) {
		r = vgic_check_iorange(kvm, *addr_ptr, *addr, alignment, size);
		if (!r)
			*addr_ptr = *addr;
	} else {
		*addr = *addr_ptr;
	}

out:
	mutex_unlock(&kvm->lock);
	return r;
}