code-patching.c (411781a290b0d0a31fd73826b3ee110f1e3cc3b6) | code-patching.c (ae0dc73625f9b0e636ccd130e394c9b654a062fb) |
---|---|
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> | 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 <asm/page.h> |
|
11#include <asm/code-patching.h> 12 13 14void patch_instruction(unsigned int *addr, unsigned int instr) 15{ 16 *addr = instr; 17 asm ("dcbst 0, %0; sync; icbi 0,%0; sync; isync" : : "r" (addr)); 18} --- 124 unchanged lines hidden (view full) --- 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} | 14#include <asm/code-patching.h> 15 16 17void patch_instruction(unsigned int *addr, unsigned int instr) 18{ 19 *addr = instr; 20 asm ("dcbst 0, %0; sync; icbi 0,%0; sync; isync" : : "r" (addr)); 21} --- 124 unchanged lines hidden (view full) --- 146 147 if (instr_is_branch_iform(*src)) 148 return create_branch(dest, target, *src); 149 else if (instr_is_branch_bform(*src)) 150 return create_cond_branch(dest, target, *src); 151 152 return 0; 153} |
154 155 156#ifdef CONFIG_CODE_PATCHING_SELFTEST 157 158static void __init test_trampoline(void) 159{ 160 asm ("nop;\n"); 161} 162 163#define check(x) \ 164 if (!(x)) printk("code-patching: test failed at line %d\n", __LINE__); 165 166static void __init test_branch_iform(void) 167{ 168 unsigned int instr; 169 unsigned long addr; 170 171 addr = (unsigned long)&instr; 172 173 /* The simplest case, branch to self, no flags */ 174 check(instr_is_branch_iform(0x48000000)); 175 /* All bits of target set, and flags */ 176 check(instr_is_branch_iform(0x4bffffff)); 177 /* High bit of opcode set, which is wrong */ 178 check(!instr_is_branch_iform(0xcbffffff)); 179 /* Middle bits of opcode set, which is wrong */ 180 check(!instr_is_branch_iform(0x7bffffff)); 181 182 /* Simplest case, branch to self with link */ 183 check(instr_is_branch_iform(0x48000001)); 184 /* All bits of targets set */ 185 check(instr_is_branch_iform(0x4bfffffd)); 186 /* Some bits of targets set */ 187 check(instr_is_branch_iform(0x4bff00fd)); 188 /* Must be a valid branch to start with */ 189 check(!instr_is_branch_iform(0x7bfffffd)); 190 191 /* Absolute branch to 0x100 */ 192 instr = 0x48000103; 193 check(instr_is_branch_to_addr(&instr, 0x100)); 194 /* Absolute branch to 0x420fc */ 195 instr = 0x480420ff; 196 check(instr_is_branch_to_addr(&instr, 0x420fc)); 197 /* Maximum positive relative branch, + 20MB - 4B */ 198 instr = 0x49fffffc; 199 check(instr_is_branch_to_addr(&instr, addr + 0x1FFFFFC)); 200 /* Smallest negative relative branch, - 4B */ 201 instr = 0x4bfffffc; 202 check(instr_is_branch_to_addr(&instr, addr - 4)); 203 /* Largest negative relative branch, - 32 MB */ 204 instr = 0x4a000000; 205 check(instr_is_branch_to_addr(&instr, addr - 0x2000000)); 206 207 /* Branch to self, with link */ 208 instr = create_branch(&instr, addr, BRANCH_SET_LINK); 209 check(instr_is_branch_to_addr(&instr, addr)); 210 211 /* Branch to self - 0x100, with link */ 212 instr = create_branch(&instr, addr - 0x100, BRANCH_SET_LINK); 213 check(instr_is_branch_to_addr(&instr, addr - 0x100)); 214 215 /* Branch to self + 0x100, no link */ 216 instr = create_branch(&instr, addr + 0x100, 0); 217 check(instr_is_branch_to_addr(&instr, addr + 0x100)); 218 219 /* Maximum relative negative offset, - 32 MB */ 220 instr = create_branch(&instr, addr - 0x2000000, BRANCH_SET_LINK); 221 check(instr_is_branch_to_addr(&instr, addr - 0x2000000)); 222 223 /* Out of range relative negative offset, - 32 MB + 4*/ 224 instr = create_branch(&instr, addr - 0x2000004, BRANCH_SET_LINK); 225 check(instr == 0); 226 227 /* Out of range relative positive offset, + 32 MB */ 228 instr = create_branch(&instr, addr + 0x2000000, BRANCH_SET_LINK); 229 check(instr == 0); 230 231 /* Unaligned target */ 232 instr = create_branch(&instr, addr + 3, BRANCH_SET_LINK); 233 check(instr == 0); 234 235 /* Check flags are masked correctly */ 236 instr = create_branch(&instr, addr, 0xFFFFFFFC); 237 check(instr_is_branch_to_addr(&instr, addr)); 238 check(instr == 0x48000000); 239} 240 241static void __init test_create_function_call(void) 242{ 243 unsigned int *iptr; 244 unsigned long dest; 245 246 /* Check we can create a function call */ 247 iptr = (unsigned int *)ppc_function_entry(test_trampoline); 248 dest = ppc_function_entry(test_create_function_call); 249 patch_instruction(iptr, create_branch(iptr, dest, BRANCH_SET_LINK)); 250 check(instr_is_branch_to_addr(iptr, dest)); 251} 252 253static void __init test_branch_bform(void) 254{ 255 unsigned long addr; 256 unsigned int *iptr, instr, flags; 257 258 iptr = &instr; 259 addr = (unsigned long)iptr; 260 261 /* The simplest case, branch to self, no flags */ 262 check(instr_is_branch_bform(0x40000000)); 263 /* All bits of target set, and flags */ 264 check(instr_is_branch_bform(0x43ffffff)); 265 /* High bit of opcode set, which is wrong */ 266 check(!instr_is_branch_bform(0xc3ffffff)); 267 /* Middle bits of opcode set, which is wrong */ 268 check(!instr_is_branch_bform(0x7bffffff)); 269 270 /* Absolute conditional branch to 0x100 */ 271 instr = 0x43ff0103; 272 check(instr_is_branch_to_addr(&instr, 0x100)); 273 /* Absolute conditional branch to 0x20fc */ 274 instr = 0x43ff20ff; 275 check(instr_is_branch_to_addr(&instr, 0x20fc)); 276 /* Maximum positive relative conditional branch, + 32 KB - 4B */ 277 instr = 0x43ff7ffc; 278 check(instr_is_branch_to_addr(&instr, addr + 0x7FFC)); 279 /* Smallest negative relative conditional branch, - 4B */ 280 instr = 0x43fffffc; 281 check(instr_is_branch_to_addr(&instr, addr - 4)); 282 /* Largest negative relative conditional branch, - 32 KB */ 283 instr = 0x43ff8000; 284 check(instr_is_branch_to_addr(&instr, addr - 0x8000)); 285 286 /* All condition code bits set & link */ 287 flags = 0x3ff000 | BRANCH_SET_LINK; 288 289 /* Branch to self */ 290 instr = create_cond_branch(iptr, addr, flags); 291 check(instr_is_branch_to_addr(&instr, addr)); 292 293 /* Branch to self - 0x100 */ 294 instr = create_cond_branch(iptr, addr - 0x100, flags); 295 check(instr_is_branch_to_addr(&instr, addr - 0x100)); 296 297 /* Branch to self + 0x100 */ 298 instr = create_cond_branch(iptr, addr + 0x100, flags); 299 check(instr_is_branch_to_addr(&instr, addr + 0x100)); 300 301 /* Maximum relative negative offset, - 32 KB */ 302 instr = create_cond_branch(iptr, addr - 0x8000, flags); 303 check(instr_is_branch_to_addr(&instr, addr - 0x8000)); 304 305 /* Out of range relative negative offset, - 32 KB + 4*/ 306 instr = create_cond_branch(iptr, addr - 0x8004, flags); 307 check(instr == 0); 308 309 /* Out of range relative positive offset, + 32 KB */ 310 instr = create_cond_branch(iptr, addr + 0x8000, flags); 311 check(instr == 0); 312 313 /* Unaligned target */ 314 instr = create_cond_branch(iptr, addr + 3, flags); 315 check(instr == 0); 316 317 /* Check flags are masked correctly */ 318 instr = create_cond_branch(iptr, addr, 0xFFFFFFFC); 319 check(instr_is_branch_to_addr(&instr, addr)); 320 check(instr == 0x43FF0000); 321} 322 323static void __init test_translate_branch(void) 324{ 325 unsigned long addr; 326 unsigned int *p, *q; 327 void *buf; 328 329 buf = vmalloc(PAGE_ALIGN(0x2000000 + 1)); 330 check(buf); 331 if (!buf) 332 return; 333 334 /* Simple case, branch to self moved a little */ 335 p = buf; 336 addr = (unsigned long)p; 337 patch_branch(p, addr, 0); 338 check(instr_is_branch_to_addr(p, addr)); 339 q = p + 1; 340 patch_instruction(q, translate_branch(q, p)); 341 check(instr_is_branch_to_addr(q, addr)); 342 343 /* Maximum negative case, move b . to addr + 32 MB */ 344 p = buf; 345 addr = (unsigned long)p; 346 patch_branch(p, addr, 0); 347 q = buf + 0x2000000; 348 patch_instruction(q, translate_branch(q, p)); 349 check(instr_is_branch_to_addr(p, addr)); 350 check(instr_is_branch_to_addr(q, addr)); 351 check(*q == 0x4a000000); 352 353 /* Maximum positive case, move x to x - 32 MB + 4 */ 354 p = buf + 0x2000000; 355 addr = (unsigned long)p; 356 patch_branch(p, addr, 0); 357 q = buf + 4; 358 patch_instruction(q, translate_branch(q, p)); 359 check(instr_is_branch_to_addr(p, addr)); 360 check(instr_is_branch_to_addr(q, addr)); 361 check(*q == 0x49fffffc); 362 363 /* Jump to x + 16 MB moved to x + 20 MB */ 364 p = buf; 365 addr = 0x1000000 + (unsigned long)buf; 366 patch_branch(p, addr, BRANCH_SET_LINK); 367 q = buf + 0x1400000; 368 patch_instruction(q, translate_branch(q, p)); 369 check(instr_is_branch_to_addr(p, addr)); 370 check(instr_is_branch_to_addr(q, addr)); 371 372 /* Jump to x + 16 MB moved to x - 16 MB + 4 */ 373 p = buf + 0x1000000; 374 addr = 0x2000000 + (unsigned long)buf; 375 patch_branch(p, addr, 0); 376 q = buf + 4; 377 patch_instruction(q, translate_branch(q, p)); 378 check(instr_is_branch_to_addr(p, addr)); 379 check(instr_is_branch_to_addr(q, addr)); 380 381 382 /* Conditional branch tests */ 383 384 /* Simple case, branch to self moved a little */ 385 p = buf; 386 addr = (unsigned long)p; 387 patch_instruction(p, create_cond_branch(p, addr, 0)); 388 check(instr_is_branch_to_addr(p, addr)); 389 q = p + 1; 390 patch_instruction(q, translate_branch(q, p)); 391 check(instr_is_branch_to_addr(q, addr)); 392 393 /* Maximum negative case, move b . to addr + 32 KB */ 394 p = buf; 395 addr = (unsigned long)p; 396 patch_instruction(p, create_cond_branch(p, addr, 0xFFFFFFFC)); 397 q = buf + 0x8000; 398 patch_instruction(q, translate_branch(q, p)); 399 check(instr_is_branch_to_addr(p, addr)); 400 check(instr_is_branch_to_addr(q, addr)); 401 check(*q == 0x43ff8000); 402 403 /* Maximum positive case, move x to x - 32 KB + 4 */ 404 p = buf + 0x8000; 405 addr = (unsigned long)p; 406 patch_instruction(p, create_cond_branch(p, addr, 0xFFFFFFFC)); 407 q = buf + 4; 408 patch_instruction(q, translate_branch(q, p)); 409 check(instr_is_branch_to_addr(p, addr)); 410 check(instr_is_branch_to_addr(q, addr)); 411 check(*q == 0x43ff7ffc); 412 413 /* Jump to x + 12 KB moved to x + 20 KB */ 414 p = buf; 415 addr = 0x3000 + (unsigned long)buf; 416 patch_instruction(p, create_cond_branch(p, addr, BRANCH_SET_LINK)); 417 q = buf + 0x5000; 418 patch_instruction(q, translate_branch(q, p)); 419 check(instr_is_branch_to_addr(p, addr)); 420 check(instr_is_branch_to_addr(q, addr)); 421 422 /* Jump to x + 8 KB moved to x - 8 KB + 4 */ 423 p = buf + 0x2000; 424 addr = 0x4000 + (unsigned long)buf; 425 patch_instruction(p, create_cond_branch(p, addr, 0)); 426 q = buf + 4; 427 patch_instruction(q, translate_branch(q, p)); 428 check(instr_is_branch_to_addr(p, addr)); 429 check(instr_is_branch_to_addr(q, addr)); 430 431 /* Free the buffer we were using */ 432 vfree(buf); 433} 434 435static int __init test_code_patching(void) 436{ 437 printk(KERN_DEBUG "Running code patching self-tests ...\n"); 438 439 test_branch_iform(); 440 test_branch_bform(); 441 test_create_function_call(); 442 test_translate_branch(); 443 444 return 0; 445} 446late_initcall(test_code_patching); 447 448#endif /* CONFIG_CODE_PATCHING_SELFTEST */ |
|