code-patching.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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. #include <linux/kernel.h>
  10. #include <asm/code-patching.h>
  11. void patch_instruction(unsigned int *addr, unsigned int instr)
  12. {
  13. *addr = instr;
  14. asm ("dcbst 0, %0; sync; icbi 0,%0; sync; isync" : : "r" (addr));
  15. }
  16. void patch_branch(unsigned int *addr, unsigned long target, int flags)
  17. {
  18. patch_instruction(addr, create_branch(addr, target, flags));
  19. }
  20. unsigned int create_branch(const unsigned int *addr,
  21. unsigned long target, int flags)
  22. {
  23. unsigned int instruction;
  24. long offset;
  25. offset = target;
  26. if (! (flags & BRANCH_ABSOLUTE))
  27. offset = offset - (unsigned long)addr;
  28. /* Check we can represent the target in the instruction format */
  29. if (offset < -0x2000000 || offset > 0x1fffffc || offset & 0x3)
  30. return 0;
  31. /* Mask out the flags and target, so they don't step on each other. */
  32. instruction = 0x48000000 | (flags & 0x3) | (offset & 0x03FFFFFC);
  33. return instruction;
  34. }
  35. unsigned int create_cond_branch(const unsigned int *addr,
  36. unsigned long target, int flags)
  37. {
  38. unsigned int instruction;
  39. long offset;
  40. offset = target;
  41. if (! (flags & BRANCH_ABSOLUTE))
  42. offset = offset - (unsigned long)addr;
  43. /* Check we can represent the target in the instruction format */
  44. if (offset < -0x8000 || offset > 0x7FFF || offset & 0x3)
  45. return 0;
  46. /* Mask out the flags and target, so they don't step on each other. */
  47. instruction = 0x40000000 | (flags & 0x3FF0003) | (offset & 0xFFFC);
  48. return instruction;
  49. }
  50. static unsigned int branch_opcode(unsigned int instr)
  51. {
  52. return (instr >> 26) & 0x3F;
  53. }
  54. static int instr_is_branch_iform(unsigned int instr)
  55. {
  56. return branch_opcode(instr) == 18;
  57. }
  58. static int instr_is_branch_bform(unsigned int instr)
  59. {
  60. return branch_opcode(instr) == 16;
  61. }
  62. int instr_is_relative_branch(unsigned int instr)
  63. {
  64. if (instr & BRANCH_ABSOLUTE)
  65. return 0;
  66. return instr_is_branch_iform(instr) || instr_is_branch_bform(instr);
  67. }
  68. static unsigned long branch_iform_target(const unsigned int *instr)
  69. {
  70. signed long imm;
  71. imm = *instr & 0x3FFFFFC;
  72. /* If the top bit of the immediate value is set this is negative */
  73. if (imm & 0x2000000)
  74. imm -= 0x4000000;
  75. if ((*instr & BRANCH_ABSOLUTE) == 0)
  76. imm += (unsigned long)instr;
  77. return (unsigned long)imm;
  78. }
  79. static unsigned long branch_bform_target(const unsigned int *instr)
  80. {
  81. signed long imm;
  82. imm = *instr & 0xFFFC;
  83. /* If the top bit of the immediate value is set this is negative */
  84. if (imm & 0x8000)
  85. imm -= 0x10000;
  86. if ((*instr & BRANCH_ABSOLUTE) == 0)
  87. imm += (unsigned long)instr;
  88. return (unsigned long)imm;
  89. }
  90. unsigned long branch_target(const unsigned int *instr)
  91. {
  92. if (instr_is_branch_iform(*instr))
  93. return branch_iform_target(instr);
  94. else if (instr_is_branch_bform(*instr))
  95. return branch_bform_target(instr);
  96. return 0;
  97. }
  98. int instr_is_branch_to_addr(const unsigned int *instr, unsigned long addr)
  99. {
  100. if (instr_is_branch_iform(*instr) || instr_is_branch_bform(*instr))
  101. return branch_target(instr) == addr;
  102. return 0;
  103. }
  104. unsigned int translate_branch(const unsigned int *dest, const unsigned int *src)
  105. {
  106. unsigned long target;
  107. target = branch_target(src);
  108. if (instr_is_branch_iform(*src))
  109. return create_branch(dest, target, *src);
  110. else if (instr_is_branch_bform(*src))
  111. return create_cond_branch(dest, target, *src);
  112. return 0;
  113. }