arch/powerpc/lib/test-code-patching.c

Source file repositories/reference/linux-study-clean/arch/powerpc/lib/test-code-patching.c

File Facts

System
Linux kernel
Corpus path
arch/powerpc/lib/test-code-patching.c
Extension
.c
Size
14419 bytes
Lines
496
Domain
Architecture Layer
Bucket
arch/powerpc
Inferred role
Architecture Layer: implementation source
Status
source implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Copyright 2008 Michael Ellerman, IBM Corporation.
 */

#include <linux/vmalloc.h>
#include <linux/init.h>

#include <asm/text-patching.h>

static int __init instr_is_branch_to_addr(const u32 *instr, unsigned long addr)
{
	if (instr_is_branch_iform(ppc_inst_read(instr)) ||
	    instr_is_branch_bform(ppc_inst_read(instr)))
		return branch_target(instr) == addr;

	return 0;
}

static void __init test_trampoline(void)
{
	asm ("nop;nop;\n");
}

#define check(x)	do {	\
	if (!(x))		\
		pr_err("code-patching: test failed at line %d\n", __LINE__); \
} while (0)

static void __init test_branch_iform(void)
{
	int err;
	ppc_inst_t instr;
	u32 tmp[2];
	u32 *iptr = tmp;
	unsigned long addr = (unsigned long)tmp;

	/* The simplest case, branch to self, no flags */
	check(instr_is_branch_iform(ppc_inst(0x48000000)));
	/* All bits of target set, and flags */
	check(instr_is_branch_iform(ppc_inst(0x4bffffff)));
	/* High bit of opcode set, which is wrong */
	check(!instr_is_branch_iform(ppc_inst(0xcbffffff)));
	/* Middle bits of opcode set, which is wrong */
	check(!instr_is_branch_iform(ppc_inst(0x7bffffff)));

	/* Simplest case, branch to self with link */
	check(instr_is_branch_iform(ppc_inst(0x48000001)));
	/* All bits of targets set */
	check(instr_is_branch_iform(ppc_inst(0x4bfffffd)));
	/* Some bits of targets set */
	check(instr_is_branch_iform(ppc_inst(0x4bff00fd)));
	/* Must be a valid branch to start with */
	check(!instr_is_branch_iform(ppc_inst(0x7bfffffd)));

	/* Absolute branch to 0x100 */
	ppc_inst_write(iptr, ppc_inst(0x48000103));
	check(instr_is_branch_to_addr(iptr, 0x100));
	/* Absolute branch to 0x420fc */
	ppc_inst_write(iptr, ppc_inst(0x480420ff));
	check(instr_is_branch_to_addr(iptr, 0x420fc));
	/* Maximum positive relative branch, + 20MB - 4B */
	ppc_inst_write(iptr, ppc_inst(0x49fffffc));
	check(instr_is_branch_to_addr(iptr, addr + 0x1FFFFFC));
	/* Smallest negative relative branch, - 4B */
	ppc_inst_write(iptr, ppc_inst(0x4bfffffc));
	check(instr_is_branch_to_addr(iptr, addr - 4));
	/* Largest negative relative branch, - 32 MB */
	ppc_inst_write(iptr, ppc_inst(0x4a000000));
	check(instr_is_branch_to_addr(iptr, addr - 0x2000000));

	/* Branch to self, with link */
	err = create_branch(&instr, iptr, addr, BRANCH_SET_LINK);
	ppc_inst_write(iptr, instr);
	check(instr_is_branch_to_addr(iptr, addr));

	/* Branch to self - 0x100, with link */
	err = create_branch(&instr, iptr, addr - 0x100, BRANCH_SET_LINK);
	ppc_inst_write(iptr, instr);
	check(instr_is_branch_to_addr(iptr, addr - 0x100));

	/* Branch to self + 0x100, no link */
	err = create_branch(&instr, iptr, addr + 0x100, 0);
	ppc_inst_write(iptr, instr);
	check(instr_is_branch_to_addr(iptr, addr + 0x100));

	/* Maximum relative negative offset, - 32 MB */
	err = create_branch(&instr, iptr, addr - 0x2000000, BRANCH_SET_LINK);
	ppc_inst_write(iptr, instr);
	check(instr_is_branch_to_addr(iptr, addr - 0x2000000));

Annotation

Implementation Notes