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