diff options
author | Michael Ellerman <michael@ellerman.id.au> | 2008-06-23 21:32:29 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2008-06-30 21:28:21 -0400 |
commit | 411781a290b0d0a31fd73826b3ee110f1e3cc3b6 (patch) | |
tree | 4a5eb482d99916c17f19a013b52cf808161f32ea | |
parent | 07630a37beefe8e4401c602f04e3e5bcbba50b31 (diff) |
powerpc: Add new code patching routines
This commit adds some new routines for patching code, which will be used
in a following commit.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r-- | arch/powerpc/lib/code-patching.c | 107 | ||||
-rw-r--r-- | include/asm-powerpc/code-patching.h | 8 |
2 files changed, 115 insertions, 0 deletions
diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c index 430f4c15d786..27957c4ea9e0 100644 --- a/arch/powerpc/lib/code-patching.c +++ b/arch/powerpc/lib/code-patching.c | |||
@@ -41,3 +41,110 @@ unsigned int create_branch(const unsigned int *addr, | |||
41 | 41 | ||
42 | return instruction; | 42 | return instruction; |
43 | } | 43 | } |
44 | |||
45 | unsigned int create_cond_branch(const unsigned int *addr, | ||
46 | unsigned long target, int flags) | ||
47 | { | ||
48 | unsigned int instruction; | ||
49 | long offset; | ||
50 | |||
51 | offset = target; | ||
52 | if (! (flags & BRANCH_ABSOLUTE)) | ||
53 | offset = offset - (unsigned long)addr; | ||
54 | |||
55 | /* Check we can represent the target in the instruction format */ | ||
56 | if (offset < -0x8000 || offset > 0x7FFF || offset & 0x3) | ||
57 | return 0; | ||
58 | |||
59 | /* Mask out the flags and target, so they don't step on each other. */ | ||
60 | instruction = 0x40000000 | (flags & 0x3FF0003) | (offset & 0xFFFC); | ||
61 | |||
62 | return instruction; | ||
63 | } | ||
64 | |||
65 | static unsigned int branch_opcode(unsigned int instr) | ||
66 | { | ||
67 | return (instr >> 26) & 0x3F; | ||
68 | } | ||
69 | |||
70 | static int instr_is_branch_iform(unsigned int instr) | ||
71 | { | ||
72 | return branch_opcode(instr) == 18; | ||
73 | } | ||
74 | |||
75 | static int instr_is_branch_bform(unsigned int instr) | ||
76 | { | ||
77 | return branch_opcode(instr) == 16; | ||
78 | } | ||
79 | |||
80 | int instr_is_relative_branch(unsigned int instr) | ||
81 | { | ||
82 | if (instr & BRANCH_ABSOLUTE) | ||
83 | return 0; | ||
84 | |||
85 | return instr_is_branch_iform(instr) || instr_is_branch_bform(instr); | ||
86 | } | ||
87 | |||
88 | static unsigned long branch_iform_target(const unsigned int *instr) | ||
89 | { | ||
90 | signed long imm; | ||
91 | |||
92 | imm = *instr & 0x3FFFFFC; | ||
93 | |||
94 | /* If the top bit of the immediate value is set this is negative */ | ||
95 | if (imm & 0x2000000) | ||
96 | imm -= 0x4000000; | ||
97 | |||
98 | if ((*instr & BRANCH_ABSOLUTE) == 0) | ||
99 | imm += (unsigned long)instr; | ||
100 | |||
101 | return (unsigned long)imm; | ||
102 | } | ||
103 | |||
104 | static unsigned long branch_bform_target(const unsigned int *instr) | ||
105 | { | ||
106 | signed long imm; | ||
107 | |||
108 | imm = *instr & 0xFFFC; | ||
109 | |||
110 | /* If the top bit of the immediate value is set this is negative */ | ||
111 | if (imm & 0x8000) | ||
112 | imm -= 0x10000; | ||
113 | |||
114 | if ((*instr & BRANCH_ABSOLUTE) == 0) | ||
115 | imm += (unsigned long)instr; | ||
116 | |||
117 | return (unsigned long)imm; | ||
118 | } | ||
119 | |||
120 | unsigned long branch_target(const unsigned int *instr) | ||
121 | { | ||
122 | if (instr_is_branch_iform(*instr)) | ||
123 | return branch_iform_target(instr); | ||
124 | else if (instr_is_branch_bform(*instr)) | ||
125 | return branch_bform_target(instr); | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | int instr_is_branch_to_addr(const unsigned int *instr, unsigned long addr) | ||
131 | { | ||
132 | if (instr_is_branch_iform(*instr) || instr_is_branch_bform(*instr)) | ||
133 | return branch_target(instr) == addr; | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | unsigned int translate_branch(const unsigned int *dest, const unsigned int *src) | ||
139 | { | ||
140 | unsigned long target; | ||
141 | |||
142 | target = branch_target(src); | ||
143 | |||
144 | if (instr_is_branch_iform(*src)) | ||
145 | return create_branch(dest, target, *src); | ||
146 | else if (instr_is_branch_bform(*src)) | ||
147 | return create_cond_branch(dest, target, *src); | ||
148 | |||
149 | return 0; | ||
150 | } | ||
diff --git a/include/asm-powerpc/code-patching.h b/include/asm-powerpc/code-patching.h index a45a7ff78725..40ad46b1dd9d 100644 --- a/include/asm-powerpc/code-patching.h +++ b/include/asm-powerpc/code-patching.h | |||
@@ -23,9 +23,17 @@ | |||
23 | 23 | ||
24 | unsigned int create_branch(const unsigned int *addr, | 24 | unsigned int create_branch(const unsigned int *addr, |
25 | unsigned long target, int flags); | 25 | unsigned long target, int flags); |
26 | unsigned int create_cond_branch(const unsigned int *addr, | ||
27 | unsigned long target, int flags); | ||
26 | void patch_branch(unsigned int *addr, unsigned long target, int flags); | 28 | void patch_branch(unsigned int *addr, unsigned long target, int flags); |
27 | void patch_instruction(unsigned int *addr, unsigned int instr); | 29 | void patch_instruction(unsigned int *addr, unsigned int instr); |
28 | 30 | ||
31 | int instr_is_relative_branch(unsigned int instr); | ||
32 | int instr_is_branch_to_addr(const unsigned int *instr, unsigned long addr); | ||
33 | unsigned long branch_target(const unsigned int *instr); | ||
34 | unsigned int translate_branch(const unsigned int *dest, | ||
35 | const unsigned int *src); | ||
36 | |||
29 | static inline unsigned long ppc_function_entry(void *func) | 37 | static inline unsigned long ppc_function_entry(void *func) |
30 | { | 38 | { |
31 | #ifdef CONFIG_PPC64 | 39 | #ifdef CONFIG_PPC64 |