Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 68f360e7 authored by Jon Medhurst's avatar Jon Medhurst
Browse files

ARM: kprobes: Add decoding table self-consistency tests



These check that the bitmask and match value used in the decoding tables
are self consistent.

Signed-off-by: default avatarJon Medhurst <tixy@yxit.co.uk>
Acked-by: default avatarNicolas Pitre <nicolas.pitre@linaro.org>
parent 2c89240b
Loading
Loading
Loading
Loading
+99 −4
Original line number Diff line number Diff line
@@ -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
 */
@@ -1117,8 +1203,15 @@ static uintptr_t __used kprobes_test_case_end(void)
 * 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");
	tests();

@@ -1140,7 +1233,7 @@ static int __init run_all_tests(void)
		goto out;

	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)
		goto out;

@@ -1162,12 +1255,14 @@ static int __init run_all_tests(void)
		goto out;

	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)
		goto out;

	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)
		goto out;
#endif