diff options
author | Michael Ellerman <michael@ellerman.id.au> | 2005-11-03 17:57:53 +1100 |
---|---|---|
committer | Michael Ellerman <michael@ellerman.id.au> | 2005-11-03 17:57:53 +1100 |
commit | c87ef1171db207d9d19f87ad12db92974d95c466 (patch) | |
tree | 18f229ae7cd22fd7cb2c9ab54a6b5632a08d45c2 /include | |
parent | e19e4ab4154a831788365c07e7a10d9114bba46c (diff) |
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>
Diffstat (limited to 'include')
-rw-r--r-- | include/asm-powerpc/system.h | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/include/asm-powerpc/system.h b/include/asm-powerpc/system.h index 5b2ecbc4790..b5da0b851e0 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 */ |