• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright 2008 Michael Ellerman, IBM Corporation.
3  *
4  *  This program is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU General Public License
6  *  as published by the Free Software Foundation; either version
7  *  2 of the License, or (at your option) any later version.
8  */
9 
10 #include <linux/kernel.h>
11 #include <linux/vmalloc.h>
12 #include <linux/init.h>
13 #include <linux/mm.h>
14 #include <asm/page.h>
15 #include <asm/code-patching.h>
16 #include <asm/uaccess.h>
17 
18 
patch_instruction(unsigned int * addr,unsigned int instr)19 int patch_instruction(unsigned int *addr, unsigned int instr)
20 {
21 	int err;
22 
23 	__put_user_size(instr, addr, 4, err);
24 	if (err)
25 		return err;
26 	asm ("dcbst 0, %0; sync; icbi 0,%0; sync; isync" : : "r" (addr));
27 	return 0;
28 }
29 
patch_branch(unsigned int * addr,unsigned long target,int flags)30 int patch_branch(unsigned int *addr, unsigned long target, int flags)
31 {
32 	return patch_instruction(addr, create_branch(addr, target, flags));
33 }
34 
create_branch(const unsigned int * addr,unsigned long target,int flags)35 unsigned int create_branch(const unsigned int *addr,
36 			   unsigned long target, int flags)
37 {
38 	unsigned int instruction;
39 	long offset;
40 
41 	offset = target;
42 	if (! (flags & BRANCH_ABSOLUTE))
43 		offset = offset - (unsigned long)addr;
44 
45 	/* Check we can represent the target in the instruction format */
46 	if (offset < -0x2000000 || offset > 0x1fffffc || offset & 0x3)
47 		return 0;
48 
49 	/* Mask out the flags and target, so they don't step on each other. */
50 	instruction = 0x48000000 | (flags & 0x3) | (offset & 0x03FFFFFC);
51 
52 	return instruction;
53 }
54 
create_cond_branch(const unsigned int * addr,unsigned long target,int flags)55 unsigned int create_cond_branch(const unsigned int *addr,
56 				unsigned long target, int flags)
57 {
58 	unsigned int instruction;
59 	long offset;
60 
61 	offset = target;
62 	if (! (flags & BRANCH_ABSOLUTE))
63 		offset = offset - (unsigned long)addr;
64 
65 	/* Check we can represent the target in the instruction format */
66 	if (offset < -0x8000 || offset > 0x7FFF || offset & 0x3)
67 		return 0;
68 
69 	/* Mask out the flags and target, so they don't step on each other. */
70 	instruction = 0x40000000 | (flags & 0x3FF0003) | (offset & 0xFFFC);
71 
72 	return instruction;
73 }
74 
branch_opcode(unsigned int instr)75 static unsigned int branch_opcode(unsigned int instr)
76 {
77 	return (instr >> 26) & 0x3F;
78 }
79 
instr_is_branch_iform(unsigned int instr)80 static int instr_is_branch_iform(unsigned int instr)
81 {
82 	return branch_opcode(instr) == 18;
83 }
84 
instr_is_branch_bform(unsigned int instr)85 static int instr_is_branch_bform(unsigned int instr)
86 {
87 	return branch_opcode(instr) == 16;
88 }
89 
instr_is_relative_branch(unsigned int instr)90 int instr_is_relative_branch(unsigned int instr)
91 {
92 	if (instr & BRANCH_ABSOLUTE)
93 		return 0;
94 
95 	return instr_is_branch_iform(instr) || instr_is_branch_bform(instr);
96 }
97 
instr_is_relative_link_branch(unsigned int instr)98 int instr_is_relative_link_branch(unsigned int instr)
99 {
100 	return instr_is_relative_branch(instr) && (instr & BRANCH_SET_LINK);
101 }
102 
branch_iform_target(const unsigned int * instr)103 static unsigned long branch_iform_target(const unsigned int *instr)
104 {
105 	signed long imm;
106 
107 	imm = *instr & 0x3FFFFFC;
108 
109 	/* If the top bit of the immediate value is set this is negative */
110 	if (imm & 0x2000000)
111 		imm -= 0x4000000;
112 
113 	if ((*instr & BRANCH_ABSOLUTE) == 0)
114 		imm += (unsigned long)instr;
115 
116 	return (unsigned long)imm;
117 }
118 
branch_bform_target(const unsigned int * instr)119 static unsigned long branch_bform_target(const unsigned int *instr)
120 {
121 	signed long imm;
122 
123 	imm = *instr & 0xFFFC;
124 
125 	/* If the top bit of the immediate value is set this is negative */
126 	if (imm & 0x8000)
127 		imm -= 0x10000;
128 
129 	if ((*instr & BRANCH_ABSOLUTE) == 0)
130 		imm += (unsigned long)instr;
131 
132 	return (unsigned long)imm;
133 }
134 
branch_target(const unsigned int * instr)135 unsigned long branch_target(const unsigned int *instr)
136 {
137 	if (instr_is_branch_iform(*instr))
138 		return branch_iform_target(instr);
139 	else if (instr_is_branch_bform(*instr))
140 		return branch_bform_target(instr);
141 
142 	return 0;
143 }
144 
instr_is_branch_to_addr(const unsigned int * instr,unsigned long addr)145 int instr_is_branch_to_addr(const unsigned int *instr, unsigned long addr)
146 {
147 	if (instr_is_branch_iform(*instr) || instr_is_branch_bform(*instr))
148 		return branch_target(instr) == addr;
149 
150 	return 0;
151 }
152 
translate_branch(const unsigned int * dest,const unsigned int * src)153 unsigned int translate_branch(const unsigned int *dest, const unsigned int *src)
154 {
155 	unsigned long target;
156 
157 	target = branch_target(src);
158 
159 	if (instr_is_branch_iform(*src))
160 		return create_branch(dest, target, *src);
161 	else if (instr_is_branch_bform(*src))
162 		return create_cond_branch(dest, target, *src);
163 
164 	return 0;
165 }
166 
167 #ifdef CONFIG_PPC_BOOK3E_64
__patch_exception(int exc,unsigned long addr)168 void __patch_exception(int exc, unsigned long addr)
169 {
170 	extern unsigned int interrupt_base_book3e;
171 	unsigned int *ibase = &interrupt_base_book3e;
172 
173 	/* Our exceptions vectors start with a NOP and -then- a branch
174 	 * to deal with single stepping from userspace which stops on
175 	 * the second instruction. Thus we need to patch the second
176 	 * instruction of the exception, not the first one
177 	 */
178 
179 	patch_branch(ibase + (exc / 4) + 1, addr, 0);
180 }
181 #endif
182 
183 #ifdef CONFIG_CODE_PATCHING_SELFTEST
184 
test_trampoline(void)185 static void __init test_trampoline(void)
186 {
187 	asm ("nop;\n");
188 }
189 
190 #define check(x)	\
191 	if (!(x)) printk("code-patching: test failed at line %d\n", __LINE__);
192 
test_branch_iform(void)193 static void __init test_branch_iform(void)
194 {
195 	unsigned int instr;
196 	unsigned long addr;
197 
198 	addr = (unsigned long)&instr;
199 
200 	/* The simplest case, branch to self, no flags */
201 	check(instr_is_branch_iform(0x48000000));
202 	/* All bits of target set, and flags */
203 	check(instr_is_branch_iform(0x4bffffff));
204 	/* High bit of opcode set, which is wrong */
205 	check(!instr_is_branch_iform(0xcbffffff));
206 	/* Middle bits of opcode set, which is wrong */
207 	check(!instr_is_branch_iform(0x7bffffff));
208 
209 	/* Simplest case, branch to self with link */
210 	check(instr_is_branch_iform(0x48000001));
211 	/* All bits of targets set */
212 	check(instr_is_branch_iform(0x4bfffffd));
213 	/* Some bits of targets set */
214 	check(instr_is_branch_iform(0x4bff00fd));
215 	/* Must be a valid branch to start with */
216 	check(!instr_is_branch_iform(0x7bfffffd));
217 
218 	/* Absolute branch to 0x100 */
219 	instr = 0x48000103;
220 	check(instr_is_branch_to_addr(&instr, 0x100));
221 	/* Absolute branch to 0x420fc */
222 	instr = 0x480420ff;
223 	check(instr_is_branch_to_addr(&instr, 0x420fc));
224 	/* Maximum positive relative branch, + 20MB - 4B */
225 	instr = 0x49fffffc;
226 	check(instr_is_branch_to_addr(&instr, addr + 0x1FFFFFC));
227 	/* Smallest negative relative branch, - 4B */
228 	instr = 0x4bfffffc;
229 	check(instr_is_branch_to_addr(&instr, addr - 4));
230 	/* Largest negative relative branch, - 32 MB */
231 	instr = 0x4a000000;
232 	check(instr_is_branch_to_addr(&instr, addr - 0x2000000));
233 
234 	/* Branch to self, with link */
235 	instr = create_branch(&instr, addr, BRANCH_SET_LINK);
236 	check(instr_is_branch_to_addr(&instr, addr));
237 
238 	/* Branch to self - 0x100, with link */
239 	instr = create_branch(&instr, addr - 0x100, BRANCH_SET_LINK);
240 	check(instr_is_branch_to_addr(&instr, addr - 0x100));
241 
242 	/* Branch to self + 0x100, no link */
243 	instr = create_branch(&instr, addr + 0x100, 0);
244 	check(instr_is_branch_to_addr(&instr, addr + 0x100));
245 
246 	/* Maximum relative negative offset, - 32 MB */
247 	instr = create_branch(&instr, addr - 0x2000000, BRANCH_SET_LINK);
248 	check(instr_is_branch_to_addr(&instr, addr - 0x2000000));
249 
250 	/* Out of range relative negative offset, - 32 MB + 4*/
251 	instr = create_branch(&instr, addr - 0x2000004, BRANCH_SET_LINK);
252 	check(instr == 0);
253 
254 	/* Out of range relative positive offset, + 32 MB */
255 	instr = create_branch(&instr, addr + 0x2000000, BRANCH_SET_LINK);
256 	check(instr == 0);
257 
258 	/* Unaligned target */
259 	instr = create_branch(&instr, addr + 3, BRANCH_SET_LINK);
260 	check(instr == 0);
261 
262 	/* Check flags are masked correctly */
263 	instr = create_branch(&instr, addr, 0xFFFFFFFC);
264 	check(instr_is_branch_to_addr(&instr, addr));
265 	check(instr == 0x48000000);
266 }
267 
test_create_function_call(void)268 static void __init test_create_function_call(void)
269 {
270 	unsigned int *iptr;
271 	unsigned long dest;
272 
273 	/* Check we can create a function call */
274 	iptr = (unsigned int *)ppc_function_entry(test_trampoline);
275 	dest = ppc_function_entry(test_create_function_call);
276 	patch_instruction(iptr, create_branch(iptr, dest, BRANCH_SET_LINK));
277 	check(instr_is_branch_to_addr(iptr, dest));
278 }
279 
test_branch_bform(void)280 static void __init test_branch_bform(void)
281 {
282 	unsigned long addr;
283 	unsigned int *iptr, instr, flags;
284 
285 	iptr = &instr;
286 	addr = (unsigned long)iptr;
287 
288 	/* The simplest case, branch to self, no flags */
289 	check(instr_is_branch_bform(0x40000000));
290 	/* All bits of target set, and flags */
291 	check(instr_is_branch_bform(0x43ffffff));
292 	/* High bit of opcode set, which is wrong */
293 	check(!instr_is_branch_bform(0xc3ffffff));
294 	/* Middle bits of opcode set, which is wrong */
295 	check(!instr_is_branch_bform(0x7bffffff));
296 
297 	/* Absolute conditional branch to 0x100 */
298 	instr = 0x43ff0103;
299 	check(instr_is_branch_to_addr(&instr, 0x100));
300 	/* Absolute conditional branch to 0x20fc */
301 	instr = 0x43ff20ff;
302 	check(instr_is_branch_to_addr(&instr, 0x20fc));
303 	/* Maximum positive relative conditional branch, + 32 KB - 4B */
304 	instr = 0x43ff7ffc;
305 	check(instr_is_branch_to_addr(&instr, addr + 0x7FFC));
306 	/* Smallest negative relative conditional branch, - 4B */
307 	instr = 0x43fffffc;
308 	check(instr_is_branch_to_addr(&instr, addr - 4));
309 	/* Largest negative relative conditional branch, - 32 KB */
310 	instr = 0x43ff8000;
311 	check(instr_is_branch_to_addr(&instr, addr - 0x8000));
312 
313 	/* All condition code bits set & link */
314 	flags = 0x3ff000 | BRANCH_SET_LINK;
315 
316 	/* Branch to self */
317 	instr = create_cond_branch(iptr, addr, flags);
318 	check(instr_is_branch_to_addr(&instr, addr));
319 
320 	/* Branch to self - 0x100 */
321 	instr = create_cond_branch(iptr, addr - 0x100, flags);
322 	check(instr_is_branch_to_addr(&instr, addr - 0x100));
323 
324 	/* Branch to self + 0x100 */
325 	instr = create_cond_branch(iptr, addr + 0x100, flags);
326 	check(instr_is_branch_to_addr(&instr, addr + 0x100));
327 
328 	/* Maximum relative negative offset, - 32 KB */
329 	instr = create_cond_branch(iptr, addr - 0x8000, flags);
330 	check(instr_is_branch_to_addr(&instr, addr - 0x8000));
331 
332 	/* Out of range relative negative offset, - 32 KB + 4*/
333 	instr = create_cond_branch(iptr, addr - 0x8004, flags);
334 	check(instr == 0);
335 
336 	/* Out of range relative positive offset, + 32 KB */
337 	instr = create_cond_branch(iptr, addr + 0x8000, flags);
338 	check(instr == 0);
339 
340 	/* Unaligned target */
341 	instr = create_cond_branch(iptr, addr + 3, flags);
342 	check(instr == 0);
343 
344 	/* Check flags are masked correctly */
345 	instr = create_cond_branch(iptr, addr, 0xFFFFFFFC);
346 	check(instr_is_branch_to_addr(&instr, addr));
347 	check(instr == 0x43FF0000);
348 }
349 
test_translate_branch(void)350 static void __init test_translate_branch(void)
351 {
352 	unsigned long addr;
353 	unsigned int *p, *q;
354 	void *buf;
355 
356 	buf = vmalloc(PAGE_ALIGN(0x2000000 + 1));
357 	check(buf);
358 	if (!buf)
359 		return;
360 
361 	/* Simple case, branch to self moved a little */
362 	p = buf;
363 	addr = (unsigned long)p;
364 	patch_branch(p, addr, 0);
365 	check(instr_is_branch_to_addr(p, addr));
366 	q = p + 1;
367 	patch_instruction(q, translate_branch(q, p));
368 	check(instr_is_branch_to_addr(q, addr));
369 
370 	/* Maximum negative case, move b . to addr + 32 MB */
371 	p = buf;
372 	addr = (unsigned long)p;
373 	patch_branch(p, addr, 0);
374 	q = buf + 0x2000000;
375 	patch_instruction(q, translate_branch(q, p));
376 	check(instr_is_branch_to_addr(p, addr));
377 	check(instr_is_branch_to_addr(q, addr));
378 	check(*q == 0x4a000000);
379 
380 	/* Maximum positive case, move x to x - 32 MB + 4 */
381 	p = buf + 0x2000000;
382 	addr = (unsigned long)p;
383 	patch_branch(p, addr, 0);
384 	q = buf + 4;
385 	patch_instruction(q, translate_branch(q, p));
386 	check(instr_is_branch_to_addr(p, addr));
387 	check(instr_is_branch_to_addr(q, addr));
388 	check(*q == 0x49fffffc);
389 
390 	/* Jump to x + 16 MB moved to x + 20 MB */
391 	p = buf;
392 	addr = 0x1000000 + (unsigned long)buf;
393 	patch_branch(p, addr, BRANCH_SET_LINK);
394 	q = buf + 0x1400000;
395 	patch_instruction(q, translate_branch(q, p));
396 	check(instr_is_branch_to_addr(p, addr));
397 	check(instr_is_branch_to_addr(q, addr));
398 
399 	/* Jump to x + 16 MB moved to x - 16 MB + 4 */
400 	p = buf + 0x1000000;
401 	addr = 0x2000000 + (unsigned long)buf;
402 	patch_branch(p, addr, 0);
403 	q = buf + 4;
404 	patch_instruction(q, translate_branch(q, p));
405 	check(instr_is_branch_to_addr(p, addr));
406 	check(instr_is_branch_to_addr(q, addr));
407 
408 
409 	/* Conditional branch tests */
410 
411 	/* Simple case, branch to self moved a little */
412 	p = buf;
413 	addr = (unsigned long)p;
414 	patch_instruction(p, create_cond_branch(p, addr, 0));
415 	check(instr_is_branch_to_addr(p, addr));
416 	q = p + 1;
417 	patch_instruction(q, translate_branch(q, p));
418 	check(instr_is_branch_to_addr(q, addr));
419 
420 	/* Maximum negative case, move b . to addr + 32 KB */
421 	p = buf;
422 	addr = (unsigned long)p;
423 	patch_instruction(p, create_cond_branch(p, addr, 0xFFFFFFFC));
424 	q = buf + 0x8000;
425 	patch_instruction(q, translate_branch(q, p));
426 	check(instr_is_branch_to_addr(p, addr));
427 	check(instr_is_branch_to_addr(q, addr));
428 	check(*q == 0x43ff8000);
429 
430 	/* Maximum positive case, move x to x - 32 KB + 4 */
431 	p = buf + 0x8000;
432 	addr = (unsigned long)p;
433 	patch_instruction(p, create_cond_branch(p, addr, 0xFFFFFFFC));
434 	q = buf + 4;
435 	patch_instruction(q, translate_branch(q, p));
436 	check(instr_is_branch_to_addr(p, addr));
437 	check(instr_is_branch_to_addr(q, addr));
438 	check(*q == 0x43ff7ffc);
439 
440 	/* Jump to x + 12 KB moved to x + 20 KB */
441 	p = buf;
442 	addr = 0x3000 + (unsigned long)buf;
443 	patch_instruction(p, create_cond_branch(p, addr, BRANCH_SET_LINK));
444 	q = buf + 0x5000;
445 	patch_instruction(q, translate_branch(q, p));
446 	check(instr_is_branch_to_addr(p, addr));
447 	check(instr_is_branch_to_addr(q, addr));
448 
449 	/* Jump to x + 8 KB moved to x - 8 KB + 4 */
450 	p = buf + 0x2000;
451 	addr = 0x4000 + (unsigned long)buf;
452 	patch_instruction(p, create_cond_branch(p, addr, 0));
453 	q = buf + 4;
454 	patch_instruction(q, translate_branch(q, p));
455 	check(instr_is_branch_to_addr(p, addr));
456 	check(instr_is_branch_to_addr(q, addr));
457 
458 	/* Free the buffer we were using */
459 	vfree(buf);
460 }
461 
test_code_patching(void)462 static int __init test_code_patching(void)
463 {
464 	printk(KERN_DEBUG "Running code patching self-tests ...\n");
465 
466 	test_branch_iform();
467 	test_branch_bform();
468 	test_create_function_call();
469 	test_translate_branch();
470 
471 	return 0;
472 }
473 late_initcall(test_code_patching);
474 
475 #endif /* CONFIG_CODE_PATCHING_SELFTEST */
476