Commit 5c0ad551 authored by Marc Zyngier's avatar Marc Zyngier

Merge branch kvm-arm64/its-save-restore-fixes-5.19 into kvmarm-master/next

* kvm-arm64/its-save-restore-fixes-5.19:
  : .
  : Tighten the ITS save/restore infrastructure to fail early rather
  : than late. Patches courtesy of Rocardo Koller.
  : .
  KVM: arm64: vgic: Undo work in failed ITS restores
  KVM: arm64: vgic: Do not ignore vgic_its_restore_cte failures
  KVM: arm64: vgic: Add more checks when restoring ITS tables
  KVM: arm64: vgic: Check that new ITEs could be saved in guest memory
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parents 822ca7f8 8c5e74c9
...@@ -894,6 +894,18 @@ static int vgic_its_cmd_handle_movi(struct kvm *kvm, struct vgic_its *its, ...@@ -894,6 +894,18 @@ static int vgic_its_cmd_handle_movi(struct kvm *kvm, struct vgic_its *its,
return update_affinity(ite->irq, vcpu); return update_affinity(ite->irq, vcpu);
} }
static bool __is_visible_gfn_locked(struct vgic_its *its, gpa_t gpa)
{
gfn_t gfn = gpa >> PAGE_SHIFT;
int idx;
bool ret;
idx = srcu_read_lock(&its->dev->kvm->srcu);
ret = kvm_is_visible_gfn(its->dev->kvm, gfn);
srcu_read_unlock(&its->dev->kvm->srcu, idx);
return ret;
}
/* /*
* Check whether an ID can be stored into the corresponding guest table. * Check whether an ID can be stored into the corresponding guest table.
* For a direct table this is pretty easy, but gets a bit nasty for * For a direct table this is pretty easy, but gets a bit nasty for
...@@ -908,9 +920,7 @@ static bool vgic_its_check_id(struct vgic_its *its, u64 baser, u32 id, ...@@ -908,9 +920,7 @@ static bool vgic_its_check_id(struct vgic_its *its, u64 baser, u32 id,
u64 indirect_ptr, type = GITS_BASER_TYPE(baser); u64 indirect_ptr, type = GITS_BASER_TYPE(baser);
phys_addr_t base = GITS_BASER_ADDR_48_to_52(baser); phys_addr_t base = GITS_BASER_ADDR_48_to_52(baser);
int esz = GITS_BASER_ENTRY_SIZE(baser); int esz = GITS_BASER_ENTRY_SIZE(baser);
int index, idx; int index;
gfn_t gfn;
bool ret;
switch (type) { switch (type) {
case GITS_BASER_TYPE_DEVICE: case GITS_BASER_TYPE_DEVICE:
...@@ -933,12 +943,11 @@ static bool vgic_its_check_id(struct vgic_its *its, u64 baser, u32 id, ...@@ -933,12 +943,11 @@ static bool vgic_its_check_id(struct vgic_its *its, u64 baser, u32 id,
return false; return false;
addr = base + id * esz; addr = base + id * esz;
gfn = addr >> PAGE_SHIFT;
if (eaddr) if (eaddr)
*eaddr = addr; *eaddr = addr;
goto out; return __is_visible_gfn_locked(its, addr);
} }
/* calculate and check the index into the 1st level */ /* calculate and check the index into the 1st level */
...@@ -964,27 +973,42 @@ static bool vgic_its_check_id(struct vgic_its *its, u64 baser, u32 id, ...@@ -964,27 +973,42 @@ static bool vgic_its_check_id(struct vgic_its *its, u64 baser, u32 id,
/* Find the address of the actual entry */ /* Find the address of the actual entry */
index = id % (SZ_64K / esz); index = id % (SZ_64K / esz);
indirect_ptr += index * esz; indirect_ptr += index * esz;
gfn = indirect_ptr >> PAGE_SHIFT;
if (eaddr) if (eaddr)
*eaddr = indirect_ptr; *eaddr = indirect_ptr;
out: return __is_visible_gfn_locked(its, indirect_ptr);
idx = srcu_read_lock(&its->dev->kvm->srcu); }
ret = kvm_is_visible_gfn(its->dev->kvm, gfn);
srcu_read_unlock(&its->dev->kvm->srcu, idx); /*
return ret; * Check whether an event ID can be stored in the corresponding Interrupt
* Translation Table, which starts at device->itt_addr.
*/
static bool vgic_its_check_event_id(struct vgic_its *its, struct its_device *device,
u32 event_id)
{
const struct vgic_its_abi *abi = vgic_its_get_abi(its);
int ite_esz = abi->ite_esz;
gpa_t gpa;
/* max table size is: BIT_ULL(device->num_eventid_bits) * ite_esz */
if (event_id >= BIT_ULL(device->num_eventid_bits))
return false;
gpa = device->itt_addr + event_id * ite_esz;
return __is_visible_gfn_locked(its, gpa);
} }
/*
* Add a new collection into the ITS collection table.
* Returns 0 on success, and a negative error value for generic errors.
*/
static int vgic_its_alloc_collection(struct vgic_its *its, static int vgic_its_alloc_collection(struct vgic_its *its,
struct its_collection **colp, struct its_collection **colp,
u32 coll_id) u32 coll_id)
{ {
struct its_collection *collection; struct its_collection *collection;
if (!vgic_its_check_id(its, its->baser_coll_table, coll_id, NULL))
return E_ITS_MAPC_COLLECTION_OOR;
collection = kzalloc(sizeof(*collection), GFP_KERNEL_ACCOUNT); collection = kzalloc(sizeof(*collection), GFP_KERNEL_ACCOUNT);
if (!collection) if (!collection)
return -ENOMEM; return -ENOMEM;
...@@ -1061,7 +1085,7 @@ static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its, ...@@ -1061,7 +1085,7 @@ static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its,
if (!device) if (!device)
return E_ITS_MAPTI_UNMAPPED_DEVICE; return E_ITS_MAPTI_UNMAPPED_DEVICE;
if (event_id >= BIT_ULL(device->num_eventid_bits)) if (!vgic_its_check_event_id(its, device, event_id))
return E_ITS_MAPTI_ID_OOR; return E_ITS_MAPTI_ID_OOR;
if (its_cmd_get_command(its_cmd) == GITS_CMD_MAPTI) if (its_cmd_get_command(its_cmd) == GITS_CMD_MAPTI)
...@@ -1078,7 +1102,12 @@ static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its, ...@@ -1078,7 +1102,12 @@ static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its,
collection = find_collection(its, coll_id); collection = find_collection(its, coll_id);
if (!collection) { if (!collection) {
int ret = vgic_its_alloc_collection(its, &collection, coll_id); int ret;
if (!vgic_its_check_id(its, its->baser_coll_table, coll_id, NULL))
return E_ITS_MAPC_COLLECTION_OOR;
ret = vgic_its_alloc_collection(its, &collection, coll_id);
if (ret) if (ret)
return ret; return ret;
new_coll = collection; new_coll = collection;
...@@ -1233,6 +1262,10 @@ static int vgic_its_cmd_handle_mapc(struct kvm *kvm, struct vgic_its *its, ...@@ -1233,6 +1262,10 @@ static int vgic_its_cmd_handle_mapc(struct kvm *kvm, struct vgic_its *its,
if (!collection) { if (!collection) {
int ret; int ret;
if (!vgic_its_check_id(its, its->baser_coll_table,
coll_id, NULL))
return E_ITS_MAPC_COLLECTION_OOR;
ret = vgic_its_alloc_collection(its, &collection, ret = vgic_its_alloc_collection(its, &collection,
coll_id); coll_id);
if (ret) if (ret)
...@@ -2195,6 +2228,9 @@ static int vgic_its_restore_ite(struct vgic_its *its, u32 event_id, ...@@ -2195,6 +2228,9 @@ static int vgic_its_restore_ite(struct vgic_its *its, u32 event_id,
if (!collection) if (!collection)
return -EINVAL; return -EINVAL;
if (!vgic_its_check_event_id(its, dev, event_id))
return -EINVAL;
ite = vgic_its_alloc_ite(dev, collection, event_id); ite = vgic_its_alloc_ite(dev, collection, event_id);
if (IS_ERR(ite)) if (IS_ERR(ite))
return PTR_ERR(ite); return PTR_ERR(ite);
...@@ -2203,8 +2239,10 @@ static int vgic_its_restore_ite(struct vgic_its *its, u32 event_id, ...@@ -2203,8 +2239,10 @@ static int vgic_its_restore_ite(struct vgic_its *its, u32 event_id,
vcpu = kvm_get_vcpu(kvm, collection->target_addr); vcpu = kvm_get_vcpu(kvm, collection->target_addr);
irq = vgic_add_lpi(kvm, lpi_id, vcpu); irq = vgic_add_lpi(kvm, lpi_id, vcpu);
if (IS_ERR(irq)) if (IS_ERR(irq)) {
its_free_ite(kvm, ite);
return PTR_ERR(irq); return PTR_ERR(irq);
}
ite->irq = irq; ite->irq = irq;
return offset; return offset;
...@@ -2316,6 +2354,7 @@ static int vgic_its_restore_dte(struct vgic_its *its, u32 id, ...@@ -2316,6 +2354,7 @@ static int vgic_its_restore_dte(struct vgic_its *its, u32 id,
void *ptr, void *opaque) void *ptr, void *opaque)
{ {
struct its_device *dev; struct its_device *dev;
u64 baser = its->baser_device_table;
gpa_t itt_addr; gpa_t itt_addr;
u8 num_eventid_bits; u8 num_eventid_bits;
u64 entry = *(u64 *)ptr; u64 entry = *(u64 *)ptr;
...@@ -2336,6 +2375,9 @@ static int vgic_its_restore_dte(struct vgic_its *its, u32 id, ...@@ -2336,6 +2375,9 @@ static int vgic_its_restore_dte(struct vgic_its *its, u32 id,
/* dte entry is valid */ /* dte entry is valid */
offset = (entry & KVM_ITS_DTE_NEXT_MASK) >> KVM_ITS_DTE_NEXT_SHIFT; offset = (entry & KVM_ITS_DTE_NEXT_MASK) >> KVM_ITS_DTE_NEXT_SHIFT;
if (!vgic_its_check_id(its, baser, id, NULL))
return -EINVAL;
dev = vgic_its_alloc_device(its, id, itt_addr, num_eventid_bits); dev = vgic_its_alloc_device(its, id, itt_addr, num_eventid_bits);
if (IS_ERR(dev)) if (IS_ERR(dev))
return PTR_ERR(dev); return PTR_ERR(dev);
...@@ -2465,6 +2507,9 @@ static int vgic_its_restore_device_tables(struct vgic_its *its) ...@@ -2465,6 +2507,9 @@ static int vgic_its_restore_device_tables(struct vgic_its *its)
if (ret > 0) if (ret > 0)
ret = 0; ret = 0;
if (ret < 0)
vgic_its_free_device_list(its->dev->kvm, its);
return ret; return ret;
} }
...@@ -2481,6 +2526,11 @@ static int vgic_its_save_cte(struct vgic_its *its, ...@@ -2481,6 +2526,11 @@ static int vgic_its_save_cte(struct vgic_its *its,
return kvm_write_guest_lock(its->dev->kvm, gpa, &val, esz); return kvm_write_guest_lock(its->dev->kvm, gpa, &val, esz);
} }
/*
* Restore a collection entry into the ITS collection table.
* Return +1 on success, 0 if the entry was invalid (which should be
* interpreted as end-of-table), and a negative error value for generic errors.
*/
static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz) static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz)
{ {
struct its_collection *collection; struct its_collection *collection;
...@@ -2507,6 +2557,10 @@ static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz) ...@@ -2507,6 +2557,10 @@ static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz)
collection = find_collection(its, coll_id); collection = find_collection(its, coll_id);
if (collection) if (collection)
return -EEXIST; return -EEXIST;
if (!vgic_its_check_id(its, its->baser_coll_table, coll_id, NULL))
return -EINVAL;
ret = vgic_its_alloc_collection(its, &collection, coll_id); ret = vgic_its_alloc_collection(its, &collection, coll_id);
if (ret) if (ret)
return ret; return ret;
...@@ -2586,6 +2640,9 @@ static int vgic_its_restore_collection_table(struct vgic_its *its) ...@@ -2586,6 +2640,9 @@ static int vgic_its_restore_collection_table(struct vgic_its *its)
if (ret > 0) if (ret > 0)
return 0; return 0;
if (ret < 0)
vgic_its_free_collection_list(its->dev->kvm, its);
return ret; return ret;
} }
...@@ -2617,7 +2674,10 @@ static int vgic_its_restore_tables_v0(struct vgic_its *its) ...@@ -2617,7 +2674,10 @@ static int vgic_its_restore_tables_v0(struct vgic_its *its)
if (ret) if (ret)
return ret; return ret;
return vgic_its_restore_device_tables(its); ret = vgic_its_restore_device_tables(its);
if (ret)
vgic_its_free_collection_list(its->dev->kvm, its);
return ret;
} }
static int vgic_its_commit_v0(struct vgic_its *its) static int vgic_its_commit_v0(struct vgic_its *its)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment