Skip to content

Commit

Permalink
powerpc: Add helper to check if offset is within relative branch range
Browse files Browse the repository at this point in the history
To permit the use of relative branch instruction in powerpc, the target
address has to be relatively nearby, since the address is specified in an
immediate field (24 bit filed) in the instruction opcode itself. Here
nearby refers to 32MB on either side of the current instruction.

This patch verifies whether the target address is within +/- 32MB
range or not.

Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Signed-off-by: Anju T Sudhakar <anju@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
  • Loading branch information
Anju T authored and Michael Ellerman committed Feb 10, 2017
1 parent c233f59 commit ebfa50d
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 1 deletion.
1 change: 1 addition & 0 deletions arch/powerpc/include/asm/code-patching.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#define BRANCH_SET_LINK 0x1
#define BRANCH_ABSOLUTE 0x2

bool is_offset_in_branch_range(long offset);
unsigned int create_branch(const unsigned int *addr,
unsigned long target, int flags);
unsigned int create_cond_branch(const unsigned int *addr,
Expand Down
24 changes: 23 additions & 1 deletion arch/powerpc/lib/code-patching.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,28 @@ int patch_branch(unsigned int *addr, unsigned long target, int flags)
return patch_instruction(addr, create_branch(addr, target, flags));
}

bool is_offset_in_branch_range(long offset)
{
/*
* 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
*/
return (offset >= -0x2000000 && offset <= 0x1fffffc && !(offset & 0x3));
}

unsigned int create_branch(const unsigned int *addr,
unsigned long target, int flags)
{
Expand All @@ -43,7 +65,7 @@ unsigned int create_branch(const unsigned int *addr,
offset = offset - (unsigned long)addr;

/* Check we can represent the target in the instruction format */
if (offset < -0x2000000 || offset > 0x1fffffc || offset & 0x3)
if (!is_offset_in_branch_range(offset))
return 0;

/* Mask out the flags and target, so they don't step on each other. */
Expand Down

0 comments on commit ebfa50d

Please sign in to comment.