diff options
Diffstat (limited to 'arch/powerpc/lib')
-rw-r--r-- | arch/powerpc/lib/code-patching.c | 107 |
1 files changed, 107 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 | } | ||