162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright 2008 Michael Ellerman, IBM Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/vmalloc.h>
762306a36Sopenharmony_ci#include <linux/init.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <asm/code-patching.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic int __init instr_is_branch_to_addr(const u32 *instr, unsigned long addr)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	if (instr_is_branch_iform(ppc_inst_read(instr)) ||
1462306a36Sopenharmony_ci	    instr_is_branch_bform(ppc_inst_read(instr)))
1562306a36Sopenharmony_ci		return branch_target(instr) == addr;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci	return 0;
1862306a36Sopenharmony_ci}
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic void __init test_trampoline(void)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	asm ("nop;nop;\n");
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define check(x)	do {	\
2662306a36Sopenharmony_ci	if (!(x))		\
2762306a36Sopenharmony_ci		pr_err("code-patching: test failed at line %d\n", __LINE__); \
2862306a36Sopenharmony_ci} while (0)
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic void __init test_branch_iform(void)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	int err;
3362306a36Sopenharmony_ci	ppc_inst_t instr;
3462306a36Sopenharmony_ci	u32 tmp[2];
3562306a36Sopenharmony_ci	u32 *iptr = tmp;
3662306a36Sopenharmony_ci	unsigned long addr = (unsigned long)tmp;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* The simplest case, branch to self, no flags */
3962306a36Sopenharmony_ci	check(instr_is_branch_iform(ppc_inst(0x48000000)));
4062306a36Sopenharmony_ci	/* All bits of target set, and flags */
4162306a36Sopenharmony_ci	check(instr_is_branch_iform(ppc_inst(0x4bffffff)));
4262306a36Sopenharmony_ci	/* High bit of opcode set, which is wrong */
4362306a36Sopenharmony_ci	check(!instr_is_branch_iform(ppc_inst(0xcbffffff)));
4462306a36Sopenharmony_ci	/* Middle bits of opcode set, which is wrong */
4562306a36Sopenharmony_ci	check(!instr_is_branch_iform(ppc_inst(0x7bffffff)));
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/* Simplest case, branch to self with link */
4862306a36Sopenharmony_ci	check(instr_is_branch_iform(ppc_inst(0x48000001)));
4962306a36Sopenharmony_ci	/* All bits of targets set */
5062306a36Sopenharmony_ci	check(instr_is_branch_iform(ppc_inst(0x4bfffffd)));
5162306a36Sopenharmony_ci	/* Some bits of targets set */
5262306a36Sopenharmony_ci	check(instr_is_branch_iform(ppc_inst(0x4bff00fd)));
5362306a36Sopenharmony_ci	/* Must be a valid branch to start with */
5462306a36Sopenharmony_ci	check(!instr_is_branch_iform(ppc_inst(0x7bfffffd)));
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* Absolute branch to 0x100 */
5762306a36Sopenharmony_ci	ppc_inst_write(iptr, ppc_inst(0x48000103));
5862306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, 0x100));
5962306a36Sopenharmony_ci	/* Absolute branch to 0x420fc */
6062306a36Sopenharmony_ci	ppc_inst_write(iptr, ppc_inst(0x480420ff));
6162306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, 0x420fc));
6262306a36Sopenharmony_ci	/* Maximum positive relative branch, + 20MB - 4B */
6362306a36Sopenharmony_ci	ppc_inst_write(iptr, ppc_inst(0x49fffffc));
6462306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr + 0x1FFFFFC));
6562306a36Sopenharmony_ci	/* Smallest negative relative branch, - 4B */
6662306a36Sopenharmony_ci	ppc_inst_write(iptr, ppc_inst(0x4bfffffc));
6762306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr - 4));
6862306a36Sopenharmony_ci	/* Largest negative relative branch, - 32 MB */
6962306a36Sopenharmony_ci	ppc_inst_write(iptr, ppc_inst(0x4a000000));
7062306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr - 0x2000000));
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* Branch to self, with link */
7362306a36Sopenharmony_ci	err = create_branch(&instr, iptr, addr, BRANCH_SET_LINK);
7462306a36Sopenharmony_ci	ppc_inst_write(iptr, instr);
7562306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr));
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Branch to self - 0x100, with link */
7862306a36Sopenharmony_ci	err = create_branch(&instr, iptr, addr - 0x100, BRANCH_SET_LINK);
7962306a36Sopenharmony_ci	ppc_inst_write(iptr, instr);
8062306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr - 0x100));
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* Branch to self + 0x100, no link */
8362306a36Sopenharmony_ci	err = create_branch(&instr, iptr, addr + 0x100, 0);
8462306a36Sopenharmony_ci	ppc_inst_write(iptr, instr);
8562306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr + 0x100));
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* Maximum relative negative offset, - 32 MB */
8862306a36Sopenharmony_ci	err = create_branch(&instr, iptr, addr - 0x2000000, BRANCH_SET_LINK);
8962306a36Sopenharmony_ci	ppc_inst_write(iptr, instr);
9062306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr - 0x2000000));
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* Out of range relative negative offset, - 32 MB + 4*/
9362306a36Sopenharmony_ci	err = create_branch(&instr, iptr, addr - 0x2000004, BRANCH_SET_LINK);
9462306a36Sopenharmony_ci	check(err);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* Out of range relative positive offset, + 32 MB */
9762306a36Sopenharmony_ci	err = create_branch(&instr, iptr, addr + 0x2000000, BRANCH_SET_LINK);
9862306a36Sopenharmony_ci	check(err);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* Unaligned target */
10162306a36Sopenharmony_ci	err = create_branch(&instr, iptr, addr + 3, BRANCH_SET_LINK);
10262306a36Sopenharmony_ci	check(err);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* Check flags are masked correctly */
10562306a36Sopenharmony_ci	err = create_branch(&instr, iptr, addr, 0xFFFFFFFC);
10662306a36Sopenharmony_ci	ppc_inst_write(iptr, instr);
10762306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr));
10862306a36Sopenharmony_ci	check(ppc_inst_equal(instr, ppc_inst(0x48000000)));
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void __init test_create_function_call(void)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	u32 *iptr;
11462306a36Sopenharmony_ci	unsigned long dest;
11562306a36Sopenharmony_ci	ppc_inst_t instr;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* Check we can create a function call */
11862306a36Sopenharmony_ci	iptr = (u32 *)ppc_function_entry(test_trampoline);
11962306a36Sopenharmony_ci	dest = ppc_function_entry(test_create_function_call);
12062306a36Sopenharmony_ci	create_branch(&instr, iptr, dest, BRANCH_SET_LINK);
12162306a36Sopenharmony_ci	patch_instruction(iptr, instr);
12262306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, dest));
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic void __init test_branch_bform(void)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	int err;
12862306a36Sopenharmony_ci	unsigned long addr;
12962306a36Sopenharmony_ci	ppc_inst_t instr;
13062306a36Sopenharmony_ci	u32 tmp[2];
13162306a36Sopenharmony_ci	u32 *iptr = tmp;
13262306a36Sopenharmony_ci	unsigned int flags;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	addr = (unsigned long)iptr;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* The simplest case, branch to self, no flags */
13762306a36Sopenharmony_ci	check(instr_is_branch_bform(ppc_inst(0x40000000)));
13862306a36Sopenharmony_ci	/* All bits of target set, and flags */
13962306a36Sopenharmony_ci	check(instr_is_branch_bform(ppc_inst(0x43ffffff)));
14062306a36Sopenharmony_ci	/* High bit of opcode set, which is wrong */
14162306a36Sopenharmony_ci	check(!instr_is_branch_bform(ppc_inst(0xc3ffffff)));
14262306a36Sopenharmony_ci	/* Middle bits of opcode set, which is wrong */
14362306a36Sopenharmony_ci	check(!instr_is_branch_bform(ppc_inst(0x7bffffff)));
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* Absolute conditional branch to 0x100 */
14662306a36Sopenharmony_ci	ppc_inst_write(iptr, ppc_inst(0x43ff0103));
14762306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, 0x100));
14862306a36Sopenharmony_ci	/* Absolute conditional branch to 0x20fc */
14962306a36Sopenharmony_ci	ppc_inst_write(iptr, ppc_inst(0x43ff20ff));
15062306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, 0x20fc));
15162306a36Sopenharmony_ci	/* Maximum positive relative conditional branch, + 32 KB - 4B */
15262306a36Sopenharmony_ci	ppc_inst_write(iptr, ppc_inst(0x43ff7ffc));
15362306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr + 0x7FFC));
15462306a36Sopenharmony_ci	/* Smallest negative relative conditional branch, - 4B */
15562306a36Sopenharmony_ci	ppc_inst_write(iptr, ppc_inst(0x43fffffc));
15662306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr - 4));
15762306a36Sopenharmony_ci	/* Largest negative relative conditional branch, - 32 KB */
15862306a36Sopenharmony_ci	ppc_inst_write(iptr, ppc_inst(0x43ff8000));
15962306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr - 0x8000));
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* All condition code bits set & link */
16262306a36Sopenharmony_ci	flags = 0x3ff000 | BRANCH_SET_LINK;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Branch to self */
16562306a36Sopenharmony_ci	err = create_cond_branch(&instr, iptr, addr, flags);
16662306a36Sopenharmony_ci	ppc_inst_write(iptr, instr);
16762306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr));
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/* Branch to self - 0x100 */
17062306a36Sopenharmony_ci	err = create_cond_branch(&instr, iptr, addr - 0x100, flags);
17162306a36Sopenharmony_ci	ppc_inst_write(iptr, instr);
17262306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr - 0x100));
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* Branch to self + 0x100 */
17562306a36Sopenharmony_ci	err = create_cond_branch(&instr, iptr, addr + 0x100, flags);
17662306a36Sopenharmony_ci	ppc_inst_write(iptr, instr);
17762306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr + 0x100));
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* Maximum relative negative offset, - 32 KB */
18062306a36Sopenharmony_ci	err = create_cond_branch(&instr, iptr, addr - 0x8000, flags);
18162306a36Sopenharmony_ci	ppc_inst_write(iptr, instr);
18262306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr - 0x8000));
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* Out of range relative negative offset, - 32 KB + 4*/
18562306a36Sopenharmony_ci	err = create_cond_branch(&instr, iptr, addr - 0x8004, flags);
18662306a36Sopenharmony_ci	check(err);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* Out of range relative positive offset, + 32 KB */
18962306a36Sopenharmony_ci	err = create_cond_branch(&instr, iptr, addr + 0x8000, flags);
19062306a36Sopenharmony_ci	check(err);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* Unaligned target */
19362306a36Sopenharmony_ci	err = create_cond_branch(&instr, iptr, addr + 3, flags);
19462306a36Sopenharmony_ci	check(err);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Check flags are masked correctly */
19762306a36Sopenharmony_ci	err = create_cond_branch(&instr, iptr, addr, 0xFFFFFFFC);
19862306a36Sopenharmony_ci	ppc_inst_write(iptr, instr);
19962306a36Sopenharmony_ci	check(instr_is_branch_to_addr(iptr, addr));
20062306a36Sopenharmony_ci	check(ppc_inst_equal(instr, ppc_inst(0x43FF0000)));
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic void __init test_translate_branch(void)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	unsigned long addr;
20662306a36Sopenharmony_ci	void *p, *q;
20762306a36Sopenharmony_ci	ppc_inst_t instr;
20862306a36Sopenharmony_ci	void *buf;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	buf = vmalloc(PAGE_ALIGN(0x2000000 + 1));
21162306a36Sopenharmony_ci	check(buf);
21262306a36Sopenharmony_ci	if (!buf)
21362306a36Sopenharmony_ci		return;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Simple case, branch to self moved a little */
21662306a36Sopenharmony_ci	p = buf;
21762306a36Sopenharmony_ci	addr = (unsigned long)p;
21862306a36Sopenharmony_ci	create_branch(&instr, p, addr, 0);
21962306a36Sopenharmony_ci	ppc_inst_write(p, instr);
22062306a36Sopenharmony_ci	check(instr_is_branch_to_addr(p, addr));
22162306a36Sopenharmony_ci	q = p + 4;
22262306a36Sopenharmony_ci	translate_branch(&instr, q, p);
22362306a36Sopenharmony_ci	ppc_inst_write(q, instr);
22462306a36Sopenharmony_ci	check(instr_is_branch_to_addr(q, addr));
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Maximum negative case, move b . to addr + 32 MB */
22762306a36Sopenharmony_ci	p = buf;
22862306a36Sopenharmony_ci	addr = (unsigned long)p;
22962306a36Sopenharmony_ci	create_branch(&instr, p, addr, 0);
23062306a36Sopenharmony_ci	ppc_inst_write(p, instr);
23162306a36Sopenharmony_ci	q = buf + 0x2000000;
23262306a36Sopenharmony_ci	translate_branch(&instr, q, p);
23362306a36Sopenharmony_ci	ppc_inst_write(q, instr);
23462306a36Sopenharmony_ci	check(instr_is_branch_to_addr(p, addr));
23562306a36Sopenharmony_ci	check(instr_is_branch_to_addr(q, addr));
23662306a36Sopenharmony_ci	check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x4a000000)));
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* Maximum positive case, move x to x - 32 MB + 4 */
23962306a36Sopenharmony_ci	p = buf + 0x2000000;
24062306a36Sopenharmony_ci	addr = (unsigned long)p;
24162306a36Sopenharmony_ci	create_branch(&instr, p, addr, 0);
24262306a36Sopenharmony_ci	ppc_inst_write(p, instr);
24362306a36Sopenharmony_ci	q = buf + 4;
24462306a36Sopenharmony_ci	translate_branch(&instr, q, p);
24562306a36Sopenharmony_ci	ppc_inst_write(q, instr);
24662306a36Sopenharmony_ci	check(instr_is_branch_to_addr(p, addr));
24762306a36Sopenharmony_ci	check(instr_is_branch_to_addr(q, addr));
24862306a36Sopenharmony_ci	check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x49fffffc)));
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* Jump to x + 16 MB moved to x + 20 MB */
25162306a36Sopenharmony_ci	p = buf;
25262306a36Sopenharmony_ci	addr = 0x1000000 + (unsigned long)buf;
25362306a36Sopenharmony_ci	create_branch(&instr, p, addr, BRANCH_SET_LINK);
25462306a36Sopenharmony_ci	ppc_inst_write(p, instr);
25562306a36Sopenharmony_ci	q = buf + 0x1400000;
25662306a36Sopenharmony_ci	translate_branch(&instr, q, p);
25762306a36Sopenharmony_ci	ppc_inst_write(q, instr);
25862306a36Sopenharmony_ci	check(instr_is_branch_to_addr(p, addr));
25962306a36Sopenharmony_ci	check(instr_is_branch_to_addr(q, addr));
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/* Jump to x + 16 MB moved to x - 16 MB + 4 */
26262306a36Sopenharmony_ci	p = buf + 0x1000000;
26362306a36Sopenharmony_ci	addr = 0x2000000 + (unsigned long)buf;
26462306a36Sopenharmony_ci	create_branch(&instr, p, addr, 0);
26562306a36Sopenharmony_ci	ppc_inst_write(p, instr);
26662306a36Sopenharmony_ci	q = buf + 4;
26762306a36Sopenharmony_ci	translate_branch(&instr, q, p);
26862306a36Sopenharmony_ci	ppc_inst_write(q, instr);
26962306a36Sopenharmony_ci	check(instr_is_branch_to_addr(p, addr));
27062306a36Sopenharmony_ci	check(instr_is_branch_to_addr(q, addr));
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Conditional branch tests */
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* Simple case, branch to self moved a little */
27662306a36Sopenharmony_ci	p = buf;
27762306a36Sopenharmony_ci	addr = (unsigned long)p;
27862306a36Sopenharmony_ci	create_cond_branch(&instr, p, addr, 0);
27962306a36Sopenharmony_ci	ppc_inst_write(p, instr);
28062306a36Sopenharmony_ci	check(instr_is_branch_to_addr(p, addr));
28162306a36Sopenharmony_ci	q = buf + 4;
28262306a36Sopenharmony_ci	translate_branch(&instr, q, p);
28362306a36Sopenharmony_ci	ppc_inst_write(q, instr);
28462306a36Sopenharmony_ci	check(instr_is_branch_to_addr(q, addr));
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* Maximum negative case, move b . to addr + 32 KB */
28762306a36Sopenharmony_ci	p = buf;
28862306a36Sopenharmony_ci	addr = (unsigned long)p;
28962306a36Sopenharmony_ci	create_cond_branch(&instr, p, addr, 0xFFFFFFFC);
29062306a36Sopenharmony_ci	ppc_inst_write(p, instr);
29162306a36Sopenharmony_ci	q = buf + 0x8000;
29262306a36Sopenharmony_ci	translate_branch(&instr, q, p);
29362306a36Sopenharmony_ci	ppc_inst_write(q, instr);
29462306a36Sopenharmony_ci	check(instr_is_branch_to_addr(p, addr));
29562306a36Sopenharmony_ci	check(instr_is_branch_to_addr(q, addr));
29662306a36Sopenharmony_ci	check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x43ff8000)));
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	/* Maximum positive case, move x to x - 32 KB + 4 */
29962306a36Sopenharmony_ci	p = buf + 0x8000;
30062306a36Sopenharmony_ci	addr = (unsigned long)p;
30162306a36Sopenharmony_ci	create_cond_branch(&instr, p, addr, 0xFFFFFFFC);
30262306a36Sopenharmony_ci	ppc_inst_write(p, instr);
30362306a36Sopenharmony_ci	q = buf + 4;
30462306a36Sopenharmony_ci	translate_branch(&instr, q, p);
30562306a36Sopenharmony_ci	ppc_inst_write(q, instr);
30662306a36Sopenharmony_ci	check(instr_is_branch_to_addr(p, addr));
30762306a36Sopenharmony_ci	check(instr_is_branch_to_addr(q, addr));
30862306a36Sopenharmony_ci	check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x43ff7ffc)));
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* Jump to x + 12 KB moved to x + 20 KB */
31162306a36Sopenharmony_ci	p = buf;
31262306a36Sopenharmony_ci	addr = 0x3000 + (unsigned long)buf;
31362306a36Sopenharmony_ci	create_cond_branch(&instr, p, addr, BRANCH_SET_LINK);
31462306a36Sopenharmony_ci	ppc_inst_write(p, instr);
31562306a36Sopenharmony_ci	q = buf + 0x5000;
31662306a36Sopenharmony_ci	translate_branch(&instr, q, p);
31762306a36Sopenharmony_ci	ppc_inst_write(q, instr);
31862306a36Sopenharmony_ci	check(instr_is_branch_to_addr(p, addr));
31962306a36Sopenharmony_ci	check(instr_is_branch_to_addr(q, addr));
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/* Jump to x + 8 KB moved to x - 8 KB + 4 */
32262306a36Sopenharmony_ci	p = buf + 0x2000;
32362306a36Sopenharmony_ci	addr = 0x4000 + (unsigned long)buf;
32462306a36Sopenharmony_ci	create_cond_branch(&instr, p, addr, 0);
32562306a36Sopenharmony_ci	ppc_inst_write(p, instr);
32662306a36Sopenharmony_ci	q = buf + 4;
32762306a36Sopenharmony_ci	translate_branch(&instr, q, p);
32862306a36Sopenharmony_ci	ppc_inst_write(q, instr);
32962306a36Sopenharmony_ci	check(instr_is_branch_to_addr(p, addr));
33062306a36Sopenharmony_ci	check(instr_is_branch_to_addr(q, addr));
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* Free the buffer we were using */
33362306a36Sopenharmony_ci	vfree(buf);
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic void __init test_prefixed_patching(void)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	u32 *iptr = (u32 *)ppc_function_entry(test_trampoline);
33962306a36Sopenharmony_ci	u32 expected[2] = {OP_PREFIX << 26, 0};
34062306a36Sopenharmony_ci	ppc_inst_t inst = ppc_inst_prefix(OP_PREFIX << 26, 0);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_PPC64))
34362306a36Sopenharmony_ci		return;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	patch_instruction(iptr, inst);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	check(!memcmp(iptr, expected, sizeof(expected)));
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int __init test_code_patching(void)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	pr_info("Running code patching self-tests ...\n");
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	test_branch_iform();
35562306a36Sopenharmony_ci	test_branch_bform();
35662306a36Sopenharmony_ci	test_create_function_call();
35762306a36Sopenharmony_ci	test_translate_branch();
35862306a36Sopenharmony_ci	test_prefixed_patching();
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	return 0;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_cilate_initcall(test_code_patching);
363