summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Ellerman <michael@ellerman.id.au>2005-11-03 09:57:53 +0300
committerMichael Ellerman <michael@ellerman.id.au>2005-11-03 09:57:53 +0300
commitc87ef1171db207d9d19f87ad12db92974d95c466 (patch)
tree18f229ae7cd22fd7cb2c9ab54a6b5632a08d45c2
parente19e4ab4154a831788365c07e7a10d9114bba46c (diff)
downloadlinux-c87ef1171db207d9d19f87ad12db92974d95c466.tar.xz
powerpc: Add helper functions for synthesising instructions at runtime
There's a few places already, and soon will be more, where we synthesise branch instructions at runtime. Rather than doing it by hand in each case, it would make sense to have one implementation. Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
-rw-r--r--include/asm-powerpc/system.h48
1 files changed, 48 insertions, 0 deletions
diff --git a/include/asm-powerpc/system.h b/include/asm-powerpc/system.h
index 5b2ecbc47907..b5da0b851e02 100644
--- a/include/asm-powerpc/system.h
+++ b/include/asm-powerpc/system.h
@@ -359,5 +359,53 @@ extern void reloc_got2(unsigned long);
#define PTRRELOC(x) ((typeof(x)) add_reloc_offset((unsigned long)(x)))
+static inline void create_instruction(unsigned long addr, unsigned int instr)
+{
+ unsigned int *p;
+ p = (unsigned int *)addr;
+ *p = instr;
+ asm ("dcbst 0, %0; sync; icbi 0,%0; sync; isync" : : "r" (p));
+}
+
+/* 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
+
+static inline void create_branch(unsigned long addr,
+ unsigned long target, int flags)
+{
+ unsigned int instruction;
+
+ if (! (flags & BRANCH_ABSOLUTE))
+ target = target - addr;
+
+ /* Mask out the flags and target, so they don't step on each other. */
+ instruction = 0x48000000 | (flags & 0x3) | (target & 0x03FFFFFC);
+
+ create_instruction(addr, instruction);
+}
+
+static inline void create_function_call(unsigned long addr, void * func)
+{
+ unsigned long func_addr;
+
+#ifdef CONFIG_PPC64
+ /*
+ * On PPC64 the function pointer actually points to the function's
+ * descriptor. The first entry in the descriptor is the address
+ * of the function text.
+ */
+ func_addr = *(unsigned long *)func;
+#else
+ func_addr = (unsigned long)func;
+#endif
+ create_branch(addr, func_addr, BRANCH_SET_LINK);
+}
+
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_SYSTEM_H */