arch/powerpc/include/asm/text-patching.h

Source file repositories/reference/linux-study-clean/arch/powerpc/include/asm/text-patching.h

File Facts

System
Linux kernel
Corpus path
arch/powerpc/include/asm/text-patching.h
Extension
.h
Size
7542 bytes
Lines
276
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

#ifndef _ASM_POWERPC_CODE_PATCHING_H
#define _ASM_POWERPC_CODE_PATCHING_H

/*
 * Copyright 2008, Michael Ellerman, IBM Corporation.
 */

#include <asm/types.h>
#include <asm/ppc-opcode.h>
#include <linux/string.h>
#include <linux/kallsyms.h>
#include <asm/asm-compat.h>
#include <asm/inst.h>

/* Flags for create_branch:
 * "b"   == create_branch(addr, target, 0);
 * "ba"  == create_branch(addr, target, BRANCH_ABSOLUTE);
 * "bl"  == create_branch(addr, target, BRANCH_SET_LINK);
 * "bla" == create_branch(addr, target, BRANCH_ABSOLUTE | BRANCH_SET_LINK);
 */
#define BRANCH_SET_LINK	0x1
#define BRANCH_ABSOLUTE	0x2

/*
 * Powerpc branch instruction is :
 *
 *  0         6                 30   31
 *  +---------+----------------+---+---+
 *  | opcode  |     LI         |AA |LK |
 *  +---------+----------------+---+---+
 *  Where AA = 0 and LK = 0
 *
 * LI is a signed 24 bits integer. The real branch offset is computed
 * by: imm32 = SignExtend(LI:'0b00', 32);
 *
 * So the maximum forward branch should be:
 *   (0x007fffff << 2) = 0x01fffffc =  0x1fffffc
 * The maximum backward branch should be:
 *   (0xff800000 << 2) = 0xfe000000 = -0x2000000
 */
static inline bool is_offset_in_branch_range(long offset)
{
	return (offset >= -0x2000000 && offset <= 0x1fffffc && !(offset & 0x3));
}

static inline bool is_offset_in_cond_branch_range(long offset)
{
	return offset >= -0x8000 && offset <= 0x7fff && !(offset & 0x3);
}

static inline int create_branch(ppc_inst_t *instr, const u32 *addr,
				unsigned long target, int flags)
{
	long offset;

	*instr = ppc_inst(0);
	offset = target;
	if (! (flags & BRANCH_ABSOLUTE))
		offset = offset - (unsigned long)addr;

	/* Check we can represent the target in the instruction format */
	if (!is_offset_in_branch_range(offset))
		return 1;

	/* Mask out the flags and target, so they don't step on each other. */
	*instr = ppc_inst(0x48000000 | (flags & 0x3) | (offset & 0x03FFFFFC));

	return 0;
}

int create_cond_branch(ppc_inst_t *instr, const u32 *addr,
		       unsigned long target, int flags);
int patch_branch(u32 *addr, unsigned long target, int flags);
int patch_instruction(u32 *addr, ppc_inst_t instr);
int raw_patch_instruction(u32 *addr, ppc_inst_t instr);
int patch_instructions(u32 *addr, u32 *code, size_t len, bool repeat_instr);

/*
 * The data patching functions patch_uint() and patch_ulong(), etc., must be
 * called on aligned addresses.
 *
 * The instruction patching functions patch_instruction() and similar must be
 * called on addresses satisfying instruction alignment requirements.
 */

#ifdef CONFIG_PPC64

int patch_uint(void *addr, unsigned int val);
int patch_ulong(void *addr, unsigned long val);

Annotation

Implementation Notes