Commit 9d0bee66 authored by Oliver Upton's avatar Oliver Upton
Browse files

Merge branch kvm-arm64/vgic-its-fixes into kvmarm/next



* kvm-arm64/vgic-its-fixes:
  : Fixes for vgic-its save/restore, courtesy of Kunkun Jiang and Jing Zhang
  :
  : Address bugs where restoring an ITS consumes a stale DTE/ITE, which
  : may lead to either garbage mappings in the ITS or the overall restore
  : ioctl failing. The fix in both cases is to zero a DTE/ITE when its
  : translation has been invalidated by the guest.
  KVM: arm64: vgic-its: Clear ITE when DISCARD frees an ITE
  KVM: arm64: vgic-its: Clear DTE when MAPD unmaps a device
  KVM: arm64: vgic-its: Add a data length check in vgic_its_save_*

Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
parents 6d4b81e2 7602ffd1
Loading
Loading
Loading
Loading
+17 −15
Original line number Diff line number Diff line
@@ -782,6 +782,9 @@ static int vgic_its_cmd_handle_discard(struct kvm *kvm, struct vgic_its *its,

	ite = find_ite(its, device_id, event_id);
	if (ite && its_is_collection_mapped(ite->collection)) {
		struct its_device *device = find_its_device(its, device_id);
		int ite_esz = vgic_its_get_abi(its)->ite_esz;
		gpa_t gpa = device->itt_addr + ite->event_id * ite_esz;
		/*
		 * Though the spec talks about removing the pending state, we
		 * don't bother here since we clear the ITTE anyway and the
@@ -790,7 +793,8 @@ static int vgic_its_cmd_handle_discard(struct kvm *kvm, struct vgic_its *its,
		vgic_its_invalidate_cache(its);

		its_free_ite(kvm, ite);
		return 0;

		return vgic_its_write_entry_lock(its, gpa, 0, ite_esz);
	}

	return E_ITS_DISCARD_UNMAPPED_INTERRUPT;
@@ -1139,9 +1143,11 @@ static int vgic_its_cmd_handle_mapd(struct kvm *kvm, struct vgic_its *its,
	bool valid = its_cmd_get_validbit(its_cmd);
	u8 num_eventid_bits = its_cmd_get_size(its_cmd);
	gpa_t itt_addr = its_cmd_get_ittaddr(its_cmd);
	int dte_esz = vgic_its_get_abi(its)->dte_esz;
	struct its_device *device;
	gpa_t gpa;

	if (!vgic_its_check_id(its, its->baser_device_table, device_id, NULL))
	if (!vgic_its_check_id(its, its->baser_device_table, device_id, &gpa))
		return E_ITS_MAPD_DEVICE_OOR;

	if (valid && num_eventid_bits > VITS_TYPER_IDBITS)
@@ -1162,7 +1168,7 @@ static int vgic_its_cmd_handle_mapd(struct kvm *kvm, struct vgic_its *its,
	 * is an error, so we are done in any case.
	 */
	if (!valid)
		return 0;
		return vgic_its_write_entry_lock(its, gpa, 0, dte_esz);

	device = vgic_its_alloc_device(its, device_id, itt_addr,
				       num_eventid_bits);
@@ -2086,7 +2092,6 @@ static int scan_its_table(struct vgic_its *its, gpa_t base, int size, u32 esz,
static int vgic_its_save_ite(struct vgic_its *its, struct its_device *dev,
			      struct its_ite *ite, gpa_t gpa, int ite_esz)
{
	struct kvm *kvm = its->dev->kvm;
	u32 next_offset;
	u64 val;

@@ -2095,7 +2100,8 @@ static int vgic_its_save_ite(struct vgic_its *its, struct its_device *dev,
	       ((u64)ite->irq->intid << KVM_ITS_ITE_PINTID_SHIFT) |
		ite->collection->collection_id;
	val = cpu_to_le64(val);
	return vgic_write_guest_lock(kvm, gpa, &val, ite_esz);

	return vgic_its_write_entry_lock(its, gpa, val, ite_esz);
}

/**
@@ -2239,7 +2245,6 @@ static int vgic_its_restore_itt(struct vgic_its *its, struct its_device *dev)
static int vgic_its_save_dte(struct vgic_its *its, struct its_device *dev,
			     gpa_t ptr, int dte_esz)
{
	struct kvm *kvm = its->dev->kvm;
	u64 val, itt_addr_field;
	u32 next_offset;

@@ -2250,7 +2255,8 @@ static int vgic_its_save_dte(struct vgic_its *its, struct its_device *dev,
	       (itt_addr_field << KVM_ITS_DTE_ITTADDR_SHIFT) |
		(dev->num_eventid_bits - 1));
	val = cpu_to_le64(val);
	return vgic_write_guest_lock(kvm, ptr, &val, dte_esz);

	return vgic_its_write_entry_lock(its, ptr, val, dte_esz);
}

/**
@@ -2437,7 +2443,8 @@ static int vgic_its_save_cte(struct vgic_its *its,
	       ((u64)collection->target_addr << KVM_ITS_CTE_RDBASE_SHIFT) |
	       collection->collection_id);
	val = cpu_to_le64(val);
	return vgic_write_guest_lock(its->dev->kvm, gpa, &val, esz);

	return vgic_its_write_entry_lock(its, gpa, val, esz);
}

/*
@@ -2453,8 +2460,7 @@ static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz)
	u64 val;
	int ret;

	BUG_ON(esz > sizeof(val));
	ret = kvm_read_guest_lock(kvm, gpa, &val, esz);
	ret = vgic_its_read_entry_lock(its, gpa, &val, esz);
	if (ret)
		return ret;
	val = le64_to_cpu(val);
@@ -2492,7 +2498,6 @@ static int vgic_its_save_collection_table(struct vgic_its *its)
	u64 baser = its->baser_coll_table;
	gpa_t gpa = GITS_BASER_ADDR_48_to_52(baser);
	struct its_collection *collection;
	u64 val;
	size_t max_size, filled = 0;
	int ret, cte_esz = abi->cte_esz;

@@ -2516,10 +2521,7 @@ static int vgic_its_save_collection_table(struct vgic_its *its)
	 * table is not fully filled, add a last dummy element
	 * with valid bit unset
	 */
	val = 0;
	BUG_ON(cte_esz > sizeof(val));
	ret = vgic_write_guest_lock(its->dev->kvm, gpa, &val, cte_esz);
	return ret;
	return vgic_its_write_entry_lock(its, gpa, 0, cte_esz);
}

/*
+23 −0
Original line number Diff line number Diff line
@@ -146,6 +146,29 @@ static inline int vgic_write_guest_lock(struct kvm *kvm, gpa_t gpa,
	return ret;
}

static inline int vgic_its_read_entry_lock(struct vgic_its *its, gpa_t eaddr,
					   u64 *eval, unsigned long esize)
{
	struct kvm *kvm = its->dev->kvm;

	if (KVM_BUG_ON(esize != sizeof(*eval), kvm))
		return -EINVAL;

	return kvm_read_guest_lock(kvm, eaddr, eval, esize);

}

static inline int vgic_its_write_entry_lock(struct vgic_its *its, gpa_t eaddr,
					    u64 eval, unsigned long esize)
{
	struct kvm *kvm = its->dev->kvm;

	if (KVM_BUG_ON(esize != sizeof(eval), kvm))
		return -EINVAL;

	return vgic_write_guest_lock(kvm, eaddr, &eval, esize);
}

/*
 * This struct provides an intermediate representation of the fields contained
 * in the GICH_VMCR and ICH_VMCR registers, such that code exporting the GIC