1
2
3
4
5
6#include <linux/kprobes.h>
7#include <linux/vmalloc.h>
8#include <linux/init.h>
9#include <linux/cpuhotplug.h>
10#include <linux/uaccess.h>
11
12#include <asm/tlbflush.h>
13#include <asm/page.h>
14#include <asm/code-patching.h>
15#include <asm/inst.h>
16
17static int __patch_instruction(u32 *exec_addr, ppc_inst_t instr, u32 *patch_addr)
18{
19 if (!ppc_inst_prefixed(instr)) {
20 u32 val = ppc_inst_val(instr);
21
22 __put_kernel_nofault(patch_addr, &val, u32, failed);
23 } else {
24 u64 val = ppc_inst_as_ulong(instr);
25
26 __put_kernel_nofault(patch_addr, &val, u64, failed);
27 }
28
29 asm ("dcbst 0, %0; sync; icbi 0,%1; sync; isync" :: "r" (patch_addr),
30 "r" (exec_addr));
31
32 return 0;
33
34failed:
35 return -EFAULT;
36}
37
38int raw_patch_instruction(u32 *addr, ppc_inst_t instr)
39{
40 return __patch_instruction(addr, instr, addr);
41}
42
43#ifdef CONFIG_STRICT_KERNEL_RWX
44static DEFINE_PER_CPU(struct vm_struct *, text_poke_area);
45
46static int map_patch_area(void *addr, unsigned long text_poke_addr);
47static void unmap_patch_area(unsigned long addr);
48
49static int text_area_cpu_up(unsigned int cpu)
50{
51 struct vm_struct *area;
52 unsigned long addr;
53 int err;
54
55 area = get_vm_area(PAGE_SIZE, VM_ALLOC);
56 if (!area) {
57 WARN_ONCE(1, "Failed to create text area for cpu %d\n",
58 cpu);
59 return -1;
60 }
61
62
63 addr = (unsigned long)area->addr;
64 err = map_patch_area(empty_zero_page, addr);
65 if (err)
66 return err;
67
68 unmap_patch_area(addr);
69
70 this_cpu_write(text_poke_area, area);
71
72 return 0;
73}
74
75static int text_area_cpu_down(unsigned int cpu)
76{
77 free_vm_area(this_cpu_read(text_poke_area));
78 return 0;
79}
80
81
82
83
84
85
86void __init poking_init(void)
87{
88 BUG_ON(!cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
89 "powerpc/text_poke:online", text_area_cpu_up,
90 text_area_cpu_down));
91}
92
93
94
95
96static int map_patch_area(void *addr, unsigned long text_poke_addr)
97{
98 unsigned long pfn;
99
100 if (is_vmalloc_or_module_addr(addr))
101 pfn = vmalloc_to_pfn(addr);
102 else
103 pfn = __pa_symbol(addr) >> PAGE_SHIFT;
104
105 return map_kernel_page(text_poke_addr, (pfn << PAGE_SHIFT), PAGE_KERNEL);
106}
107
108static void unmap_patch_area(unsigned long addr)
109{
110 pte_t *ptep;
111 pmd_t *pmdp;
112 pud_t *pudp;
113 p4d_t *p4dp;
114 pgd_t *pgdp;
115
116 pgdp = pgd_offset_k(addr);
117 if (WARN_ON(pgd_none(*pgdp)))
118 return;
119
120 p4dp = p4d_offset(pgdp, addr);
121 if (WARN_ON(p4d_none(*p4dp)))
122 return;
123
124 pudp = pud_offset(p4dp, addr);
125 if (WARN_ON(pud_none(*pudp)))
126 return;
127
128 pmdp = pmd_offset(pudp, addr);
129 if (WARN_ON(pmd_none(*pmdp)))
130 return;
131
132 ptep = pte_offset_kernel(pmdp, addr);
133 if (WARN_ON(pte_none(*ptep)))
134 return;
135
136
137
138
139 pte_clear(&init_mm, addr, ptep);
140 flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
141}
142
143static int __do_patch_instruction(u32 *addr, ppc_inst_t instr)
144{
145 int err;
146 u32 *patch_addr;
147 unsigned long text_poke_addr;
148
149 text_poke_addr = (unsigned long)__this_cpu_read(text_poke_area)->addr;
150 patch_addr = (u32 *)(text_poke_addr + offset_in_page(addr));
151
152 err = map_patch_area(addr, text_poke_addr);
153 if (err)
154 return err;
155
156 err = __patch_instruction(addr, instr, patch_addr);
157
158 unmap_patch_area(text_poke_addr);
159
160 return err;
161}
162
163static int do_patch_instruction(u32 *addr, ppc_inst_t instr)
164{
165 int err;
166 unsigned long flags;
167
168
169
170
171
172
173 if (!this_cpu_read(text_poke_area))
174 return raw_patch_instruction(addr, instr);
175
176 local_irq_save(flags);
177 err = __do_patch_instruction(addr, instr);
178 local_irq_restore(flags);
179
180 return err;
181}
182#else
183
184static int do_patch_instruction(u32 *addr, ppc_inst_t instr)
185{
186 return raw_patch_instruction(addr, instr);
187}
188
189#endif
190
191int patch_instruction(u32 *addr, ppc_inst_t instr)
192{
193
194 if (system_state >= SYSTEM_FREEING_INITMEM && init_section_contains(addr, 4))
195 return 0;
196
197 return do_patch_instruction(addr, instr);
198}
199NOKPROBE_SYMBOL(patch_instruction);
200
201int patch_branch(u32 *addr, unsigned long target, int flags)
202{
203 ppc_inst_t instr;
204
205 if (create_branch(&instr, addr, target, flags))
206 return -ERANGE;
207
208 return patch_instruction(addr, instr);
209}
210
211bool is_offset_in_branch_range(long offset)
212{
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230 return (offset >= -0x2000000 && offset <= 0x1fffffc && !(offset & 0x3));
231}
232
233bool is_offset_in_cond_branch_range(long offset)
234{
235 return offset >= -0x8000 && offset <= 0x7fff && !(offset & 0x3);
236}
237
238
239
240
241
242bool is_conditional_branch(ppc_inst_t instr)
243{
244 unsigned int opcode = ppc_inst_primary_opcode(instr);
245
246 if (opcode == 16)
247 return true;
248 if (opcode == 19) {
249 switch ((ppc_inst_val(instr) >> 1) & 0x3ff) {
250 case 16:
251 case 528:
252 case 560:
253 return true;
254 }
255 }
256 return false;
257}
258NOKPROBE_SYMBOL(is_conditional_branch);
259
260int create_branch(ppc_inst_t *instr, const u32 *addr,
261 unsigned long target, int flags)
262{
263 long offset;
264
265 *instr = ppc_inst(0);
266 offset = target;
267 if (! (flags & BRANCH_ABSOLUTE))
268 offset = offset - (unsigned long)addr;
269
270
271 if (!is_offset_in_branch_range(offset))
272 return 1;
273
274
275 *instr = ppc_inst(0x48000000 | (flags & 0x3) | (offset & 0x03FFFFFC));
276
277 return 0;
278}
279
280int create_cond_branch(ppc_inst_t *instr, const u32 *addr,
281 unsigned long target, int flags)
282{
283 long offset;
284
285 offset = target;
286 if (! (flags & BRANCH_ABSOLUTE))
287 offset = offset - (unsigned long)addr;
288
289
290 if (!is_offset_in_cond_branch_range(offset))
291 return 1;
292
293
294 *instr = ppc_inst(0x40000000 | (flags & 0x3FF0003) | (offset & 0xFFFC));
295
296 return 0;
297}
298
299int instr_is_relative_branch(ppc_inst_t instr)
300{
301 if (ppc_inst_val(instr) & BRANCH_ABSOLUTE)
302 return 0;
303
304 return instr_is_branch_iform(instr) || instr_is_branch_bform(instr);
305}
306
307int instr_is_relative_link_branch(ppc_inst_t instr)
308{
309 return instr_is_relative_branch(instr) && (ppc_inst_val(instr) & BRANCH_SET_LINK);
310}
311
312static unsigned long branch_iform_target(const u32 *instr)
313{
314 signed long imm;
315
316 imm = ppc_inst_val(ppc_inst_read(instr)) & 0x3FFFFFC;
317
318
319 if (imm & 0x2000000)
320 imm -= 0x4000000;
321
322 if ((ppc_inst_val(ppc_inst_read(instr)) & BRANCH_ABSOLUTE) == 0)
323 imm += (unsigned long)instr;
324
325 return (unsigned long)imm;
326}
327
328static unsigned long branch_bform_target(const u32 *instr)
329{
330 signed long imm;
331
332 imm = ppc_inst_val(ppc_inst_read(instr)) & 0xFFFC;
333
334
335 if (imm & 0x8000)
336 imm -= 0x10000;
337
338 if ((ppc_inst_val(ppc_inst_read(instr)) & BRANCH_ABSOLUTE) == 0)
339 imm += (unsigned long)instr;
340
341 return (unsigned long)imm;
342}
343
344unsigned long branch_target(const u32 *instr)
345{
346 if (instr_is_branch_iform(ppc_inst_read(instr)))
347 return branch_iform_target(instr);
348 else if (instr_is_branch_bform(ppc_inst_read(instr)))
349 return branch_bform_target(instr);
350
351 return 0;
352}
353
354int translate_branch(ppc_inst_t *instr, const u32 *dest, const u32 *src)
355{
356 unsigned long target;
357 target = branch_target(src);
358
359 if (instr_is_branch_iform(ppc_inst_read(src)))
360 return create_branch(instr, dest, target,
361 ppc_inst_val(ppc_inst_read(src)));
362 else if (instr_is_branch_bform(ppc_inst_read(src)))
363 return create_cond_branch(instr, dest, target,
364 ppc_inst_val(ppc_inst_read(src)));
365
366 return 1;
367}
368