Commit c5ef5e35 authored by Benjamin Gray's avatar Benjamin Gray Committed by Michael Ellerman
Browse files

powerpc/code-patching: Test patch_instructions() during boot



patch_instructions() introduces new behaviour with a couple of
variations. Test each case of

  * a repeated 32-bit instruction,
  * a repeated 64-bit instruction (ppc64), and
  * a copied sequence of instructions

for both on a single page and when it crosses a page boundary.

Signed-off-by: default avatarBenjamin Gray <bgray@linux.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20240325052815.854044-1-bgray@linux.ibm.com
parent 295454ed
Loading
Loading
Loading
Loading
+92 −0
Original line number Diff line number Diff line
@@ -347,6 +347,97 @@ static void __init test_prefixed_patching(void)
	check(!memcmp(iptr, expected, sizeof(expected)));
}

static void __init test_multi_instruction_patching(void)
{
	u32 code[32];
	void *buf;
	u32 *addr32;
	u64 *addr64;
	ppc_inst_t inst64 = ppc_inst_prefix(OP_PREFIX << 26 | 3UL << 24, PPC_RAW_TRAP());
	u32 inst32 = PPC_RAW_NOP();

	buf = vzalloc(PAGE_SIZE * 8);
	check(buf);
	if (!buf)
		return;

	/* Test single page 32-bit repeated instruction */
	addr32 = buf + PAGE_SIZE;
	check(!patch_instructions(addr32 + 1, &inst32, 12, true));

	check(addr32[0] == 0);
	check(addr32[1] == inst32);
	check(addr32[2] == inst32);
	check(addr32[3] == inst32);
	check(addr32[4] == 0);

	/* Test single page 64-bit repeated instruction */
	if (IS_ENABLED(CONFIG_PPC64)) {
		check(ppc_inst_prefixed(inst64));

		addr64 = buf + PAGE_SIZE * 2;
		ppc_inst_write(code, inst64);
		check(!patch_instructions((u32 *)(addr64 + 1), code, 24, true));

		check(addr64[0] == 0);
		check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[1]), inst64));
		check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[2]), inst64));
		check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[3]), inst64));
		check(addr64[4] == 0);
	}

	/* Test single page memcpy */
	addr32 = buf + PAGE_SIZE * 3;

	for (int i = 0; i < ARRAY_SIZE(code); i++)
		code[i] = i + 1;

	check(!patch_instructions(addr32 + 1, code, sizeof(code), false));

	check(addr32[0] == 0);
	check(!memcmp(&addr32[1], code, sizeof(code)));
	check(addr32[ARRAY_SIZE(code) + 1] == 0);

	/* Test multipage 32-bit repeated instruction */
	addr32 = buf + PAGE_SIZE * 4 - 8;
	check(!patch_instructions(addr32 + 1, &inst32, 12, true));

	check(addr32[0] == 0);
	check(addr32[1] == inst32);
	check(addr32[2] == inst32);
	check(addr32[3] == inst32);
	check(addr32[4] == 0);

	/* Test multipage 64-bit repeated instruction */
	if (IS_ENABLED(CONFIG_PPC64)) {
		check(ppc_inst_prefixed(inst64));

		addr64 = buf + PAGE_SIZE * 5 - 8;
		ppc_inst_write(code, inst64);
		check(!patch_instructions((u32 *)(addr64 + 1), code, 24, true));

		check(addr64[0] == 0);
		check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[1]), inst64));
		check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[2]), inst64));
		check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[3]), inst64));
		check(addr64[4] == 0);
	}

	/* Test multipage memcpy */
	addr32 = buf + PAGE_SIZE * 6 - 12;

	for (int i = 0; i < ARRAY_SIZE(code); i++)
		code[i] = i + 1;

	check(!patch_instructions(addr32 + 1, code, sizeof(code), false));

	check(addr32[0] == 0);
	check(!memcmp(&addr32[1], code, sizeof(code)));
	check(addr32[ARRAY_SIZE(code) + 1] == 0);

	vfree(buf);
}

static int __init test_code_patching(void)
{
	pr_info("Running code patching self-tests ...\n");
@@ -356,6 +447,7 @@ static int __init test_code_patching(void)
	test_create_function_call();
	test_translate_branch();
	test_prefixed_patching();
	test_multi_instruction_patching();

	return 0;
}