|
@@ -1466,6 +1466,16 @@ static void vgic_mmio_write_its_ctlr(struct kvm *kvm, struct vgic_its *its,
|
|
|
{
|
|
|
mutex_lock(&its->cmd_lock);
|
|
|
|
|
|
+ /*
|
|
|
+ * It is UNPREDICTABLE to enable the ITS if any of the CBASER or
|
|
|
+ * device/collection BASER are invalid
|
|
|
+ */
|
|
|
+ if (!its->enabled && (val & GITS_CTLR_ENABLE) &&
|
|
|
+ (!(its->baser_device_table & GITS_BASER_VALID) ||
|
|
|
+ !(its->baser_coll_table & GITS_BASER_VALID) ||
|
|
|
+ !(its->cbaser & GITS_CBASER_VALID)))
|
|
|
+ goto out;
|
|
|
+
|
|
|
its->enabled = !!(val & GITS_CTLR_ENABLE);
|
|
|
|
|
|
/*
|
|
@@ -1474,6 +1484,7 @@ static void vgic_mmio_write_its_ctlr(struct kvm *kvm, struct vgic_its *its,
|
|
|
*/
|
|
|
vgic_its_process_commands(kvm, its);
|
|
|
|
|
|
+out:
|
|
|
mutex_unlock(&its->cmd_lock);
|
|
|
}
|
|
|
|
|
@@ -1801,37 +1812,33 @@ typedef int (*entry_fn_t)(struct vgic_its *its, u32 id, void *entry,
|
|
|
static int scan_its_table(struct vgic_its *its, gpa_t base, int size, int esz,
|
|
|
int start_id, entry_fn_t fn, void *opaque)
|
|
|
{
|
|
|
- void *entry = kzalloc(esz, GFP_KERNEL);
|
|
|
struct kvm *kvm = its->dev->kvm;
|
|
|
unsigned long len = size;
|
|
|
int id = start_id;
|
|
|
gpa_t gpa = base;
|
|
|
+ char entry[esz];
|
|
|
int ret;
|
|
|
|
|
|
+ memset(entry, 0, esz);
|
|
|
+
|
|
|
while (len > 0) {
|
|
|
int next_offset;
|
|
|
size_t byte_offset;
|
|
|
|
|
|
ret = kvm_read_guest(kvm, gpa, entry, esz);
|
|
|
if (ret)
|
|
|
- goto out;
|
|
|
+ return ret;
|
|
|
|
|
|
next_offset = fn(its, id, entry, opaque);
|
|
|
- if (next_offset <= 0) {
|
|
|
- ret = next_offset;
|
|
|
- goto out;
|
|
|
- }
|
|
|
+ if (next_offset <= 0)
|
|
|
+ return next_offset;
|
|
|
|
|
|
byte_offset = next_offset * esz;
|
|
|
id += next_offset;
|
|
|
gpa += byte_offset;
|
|
|
len -= byte_offset;
|
|
|
}
|
|
|
- ret = 1;
|
|
|
-
|
|
|
-out:
|
|
|
- kfree(entry);
|
|
|
- return ret;
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1940,6 +1947,14 @@ static int vgic_its_save_itt(struct vgic_its *its, struct its_device *device)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * vgic_its_restore_itt - restore the ITT of a device
|
|
|
+ *
|
|
|
+ * @its: its handle
|
|
|
+ * @dev: device handle
|
|
|
+ *
|
|
|
+ * Return 0 on success, < 0 on error
|
|
|
+ */
|
|
|
static int vgic_its_restore_itt(struct vgic_its *its, struct its_device *dev)
|
|
|
{
|
|
|
const struct vgic_its_abi *abi = vgic_its_get_abi(its);
|
|
@@ -1951,6 +1966,10 @@ static int vgic_its_restore_itt(struct vgic_its *its, struct its_device *dev)
|
|
|
ret = scan_its_table(its, base, max_size, ite_esz, 0,
|
|
|
vgic_its_restore_ite, dev);
|
|
|
|
|
|
+ /* scan_its_table returns +1 if all ITEs are invalid */
|
|
|
+ if (ret > 0)
|
|
|
+ ret = 0;
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -2048,11 +2067,12 @@ static int vgic_its_device_cmp(void *priv, struct list_head *a,
|
|
|
static int vgic_its_save_device_tables(struct vgic_its *its)
|
|
|
{
|
|
|
const struct vgic_its_abi *abi = vgic_its_get_abi(its);
|
|
|
+ u64 baser = its->baser_device_table;
|
|
|
struct its_device *dev;
|
|
|
int dte_esz = abi->dte_esz;
|
|
|
- u64 baser;
|
|
|
|
|
|
- baser = its->baser_device_table;
|
|
|
+ if (!(baser & GITS_BASER_VALID))
|
|
|
+ return 0;
|
|
|
|
|
|
list_sort(NULL, &its->device_list, vgic_its_device_cmp);
|
|
|
|
|
@@ -2107,10 +2127,7 @@ static int handle_l1_dte(struct vgic_its *its, u32 id, void *addr,
|
|
|
ret = scan_its_table(its, gpa, SZ_64K, dte_esz,
|
|
|
l2_start_id, vgic_its_restore_dte, NULL);
|
|
|
|
|
|
- if (ret <= 0)
|
|
|
- return ret;
|
|
|
-
|
|
|
- return 1;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -2140,8 +2157,9 @@ static int vgic_its_restore_device_tables(struct vgic_its *its)
|
|
|
vgic_its_restore_dte, NULL);
|
|
|
}
|
|
|
|
|
|
+ /* scan_its_table returns +1 if all entries are invalid */
|
|
|
if (ret > 0)
|
|
|
- ret = -EINVAL;
|
|
|
+ ret = 0;
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
@@ -2198,17 +2216,17 @@ static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz)
|
|
|
static int vgic_its_save_collection_table(struct vgic_its *its)
|
|
|
{
|
|
|
const struct vgic_its_abi *abi = vgic_its_get_abi(its);
|
|
|
+ u64 baser = its->baser_coll_table;
|
|
|
+ gpa_t gpa = BASER_ADDRESS(baser);
|
|
|
struct its_collection *collection;
|
|
|
u64 val;
|
|
|
- gpa_t gpa;
|
|
|
size_t max_size, filled = 0;
|
|
|
int ret, cte_esz = abi->cte_esz;
|
|
|
|
|
|
- gpa = BASER_ADDRESS(its->baser_coll_table);
|
|
|
- if (!gpa)
|
|
|
+ if (!(baser & GITS_BASER_VALID))
|
|
|
return 0;
|
|
|
|
|
|
- max_size = GITS_BASER_NR_PAGES(its->baser_coll_table) * SZ_64K;
|
|
|
+ max_size = GITS_BASER_NR_PAGES(baser) * SZ_64K;
|
|
|
|
|
|
list_for_each_entry(collection, &its->collection_list, coll_list) {
|
|
|
ret = vgic_its_save_cte(its, collection, gpa, cte_esz);
|
|
@@ -2239,17 +2257,18 @@ static int vgic_its_save_collection_table(struct vgic_its *its)
|
|
|
static int vgic_its_restore_collection_table(struct vgic_its *its)
|
|
|
{
|
|
|
const struct vgic_its_abi *abi = vgic_its_get_abi(its);
|
|
|
+ u64 baser = its->baser_coll_table;
|
|
|
int cte_esz = abi->cte_esz;
|
|
|
size_t max_size, read = 0;
|
|
|
gpa_t gpa;
|
|
|
int ret;
|
|
|
|
|
|
- if (!(its->baser_coll_table & GITS_BASER_VALID))
|
|
|
+ if (!(baser & GITS_BASER_VALID))
|
|
|
return 0;
|
|
|
|
|
|
- gpa = BASER_ADDRESS(its->baser_coll_table);
|
|
|
+ gpa = BASER_ADDRESS(baser);
|
|
|
|
|
|
- max_size = GITS_BASER_NR_PAGES(its->baser_coll_table) * SZ_64K;
|
|
|
+ max_size = GITS_BASER_NR_PAGES(baser) * SZ_64K;
|
|
|
|
|
|
while (read < max_size) {
|
|
|
ret = vgic_its_restore_cte(its, gpa, cte_esz);
|
|
@@ -2258,6 +2277,10 @@ static int vgic_its_restore_collection_table(struct vgic_its *its)
|
|
|
gpa += cte_esz;
|
|
|
read += cte_esz;
|
|
|
}
|
|
|
+
|
|
|
+ if (ret > 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
|