summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/lib
diff options
context:
space:
mode:
authorAnju T2017-02-08 09:57:30 +0100
committerMichael Ellerman2017-02-10 03:28:03 +0100
commitebfa50df435eed18e1389a43e0596246228e7298 (patch)
treea901e3284d4bd0e26e541f39f873c6292f7862ba /arch/powerpc/lib
parentpowerpc/bpf: Introduce __PPC_SH64() (diff)
downloadkernel-qcow2-linux-ebfa50df435eed18e1389a43e0596246228e7298.tar.gz
kernel-qcow2-linux-ebfa50df435eed18e1389a43e0596246228e7298.tar.xz
kernel-qcow2-linux-ebfa50df435eed18e1389a43e0596246228e7298.zip
powerpc: Add helper to check if offset is within relative branch range
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>
Diffstat (limited to 'arch/powerpc/lib')
-rw-r--r--arch/powerpc/lib/code-patching.c24
1 files changed, 23 insertions, 1 deletions
diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c
index c1746df0f88e..4ccf16a822cc 100644
--- a/arch/powerpc/lib/code-patching.c
+++ b/arch/powerpc/lib/code-patching.c
@@ -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. */