|
@@ -442,6 +442,92 @@ static int run_api_tests(long (*func)(long, long))
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Decoding table self-consistency tests
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
|
|
|
|
+ [DECODE_TYPE_TABLE] = sizeof(struct decode_table),
|
|
|
|
+ [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom),
|
|
|
|
+ [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate),
|
|
|
|
+ [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate),
|
|
|
|
+ [DECODE_TYPE_OR] = sizeof(struct decode_or),
|
|
|
|
+ [DECODE_TYPE_REJECT] = sizeof(struct decode_reject)
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int table_iter(const union decode_item *table,
|
|
|
|
+ int (*fn)(const struct decode_header *, void *),
|
|
|
|
+ void *args)
|
|
|
|
+{
|
|
|
|
+ const struct decode_header *h = (struct decode_header *)table;
|
|
|
|
+ int result;
|
|
|
|
+
|
|
|
|
+ for (;;) {
|
|
|
|
+ enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
|
|
|
|
+
|
|
|
|
+ if (type == DECODE_TYPE_END)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ result = fn(h, args);
|
|
|
|
+ if (result)
|
|
|
|
+ return result;
|
|
|
|
+
|
|
|
|
+ h = (struct decode_header *)
|
|
|
|
+ ((uintptr_t)h + decode_struct_sizes[type]);
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int table_test_fail(const struct decode_header *h, const char* message)
|
|
|
|
+{
|
|
|
|
+
|
|
|
|
+ pr_err("FAIL: kprobes test failure \"%s\" (mask %08x, value %08x)\n",
|
|
|
|
+ message, h->mask.bits, h->value.bits);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+struct table_test_args {
|
|
|
|
+ const union decode_item *root_table;
|
|
|
|
+ u32 parent_mask;
|
|
|
|
+ u32 parent_value;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int table_test_fn(const struct decode_header *h, void *args)
|
|
|
|
+{
|
|
|
|
+ struct table_test_args *a = (struct table_test_args *)args;
|
|
|
|
+ enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
|
|
|
|
+
|
|
|
|
+ if (h->value.bits & ~h->mask.bits)
|
|
|
|
+ return table_test_fail(h, "Match value has bits not in mask");
|
|
|
|
+
|
|
|
|
+ if ((h->mask.bits & a->parent_mask) != a->parent_mask)
|
|
|
|
+ return table_test_fail(h, "Mask has bits not in parent mask");
|
|
|
|
+
|
|
|
|
+ if ((h->value.bits ^ a->parent_value) & a->parent_mask)
|
|
|
|
+ return table_test_fail(h, "Value is inconsistent with parent");
|
|
|
|
+
|
|
|
|
+ if (type == DECODE_TYPE_TABLE) {
|
|
|
|
+ struct decode_table *d = (struct decode_table *)h;
|
|
|
|
+ struct table_test_args args2 = *a;
|
|
|
|
+ args2.parent_mask = h->mask.bits;
|
|
|
|
+ args2.parent_value = h->value.bits;
|
|
|
|
+ return table_iter(d->table.table, table_test_fn, &args2);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int table_test(const union decode_item *table)
|
|
|
|
+{
|
|
|
|
+ struct table_test_args args = {
|
|
|
|
+ .root_table = table,
|
|
|
|
+ .parent_mask = 0,
|
|
|
|
+ .parent_value = 0
|
|
|
|
+ };
|
|
|
|
+ return table_iter(args.root_table, table_test_fn, &args);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Framework for instruction set test cases
|
|
* Framework for instruction set test cases
|
|
*/
|
|
*/
|
|
@@ -1117,8 +1203,15 @@ end:
|
|
* Top level test functions
|
|
* Top level test functions
|
|
*/
|
|
*/
|
|
|
|
|
|
-static int run_test_cases(void (*tests)(void))
|
|
|
|
|
|
+static int run_test_cases(void (*tests)(void), const union decode_item *table)
|
|
{
|
|
{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ pr_info(" Check decoding tables\n");
|
|
|
|
+ ret = table_test(table);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
pr_info(" Run test cases\n");
|
|
pr_info(" Run test cases\n");
|
|
tests();
|
|
tests();
|
|
|
|
|
|
@@ -1140,7 +1233,7 @@ static int __init run_all_tests(void)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
pr_info("ARM instruction simulation\n");
|
|
pr_info("ARM instruction simulation\n");
|
|
- ret = run_test_cases(kprobe_arm_test_cases);
|
|
|
|
|
|
+ ret = run_test_cases(kprobe_arm_test_cases, kprobe_decode_arm_table);
|
|
if (ret)
|
|
if (ret)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
@@ -1162,12 +1255,14 @@ static int __init run_all_tests(void)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
pr_info("16-bit Thumb instruction simulation\n");
|
|
pr_info("16-bit Thumb instruction simulation\n");
|
|
- ret = run_test_cases(kprobe_thumb16_test_cases);
|
|
|
|
|
|
+ ret = run_test_cases(kprobe_thumb16_test_cases,
|
|
|
|
+ kprobe_decode_thumb16_table);
|
|
if (ret)
|
|
if (ret)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
pr_info("32-bit Thumb instruction simulation\n");
|
|
pr_info("32-bit Thumb instruction simulation\n");
|
|
- ret = run_test_cases(kprobe_thumb32_test_cases);
|
|
|
|
|
|
+ ret = run_test_cases(kprobe_thumb32_test_cases,
|
|
|
|
+ kprobe_decode_thumb32_table);
|
|
if (ret)
|
|
if (ret)
|
|
goto out;
|
|
goto out;
|
|
#endif
|
|
#endif
|