powerpc: Add helper to check if offset is within relative branch range
authorAnju T <anju@linux.vnet.ibm.com>
Wed, 8 Feb 2017 08:57:30 +0000 (14:27 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Fri, 10 Feb 2017 02:28:03 +0000 (13:28 +1100)
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>
arch/powerpc/include/asm/code-patching.h
arch/powerpc/lib/code-patching.c

index 2015b072422cb1d7c63e04f22631ebc48578179f..75ee4f4ac840fff77997a3ef653d51a02834b799 100644 (file)
@@ -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,
index c1746df0f88e953c7b404ba0f55f2179882d48b3..4ccf16a822cc05db0d311d6748b70853bb0d8edb 100644 (file)
@@ -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)
 {
@@ -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. */