patch.c 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. #include <linux/kernel.h>
  2. #include <linux/kprobes.h>
  3. #include <linux/stop_machine.h>
  4. #include <asm/cacheflush.h>
  5. #include <asm/smp_plat.h>
  6. #include <asm/opcodes.h>
  7. #include "patch.h"
  8. struct patch {
  9. void *addr;
  10. unsigned int insn;
  11. };
  12. void __kprobes __patch_text(void *addr, unsigned int insn)
  13. {
  14. bool thumb2 = IS_ENABLED(CONFIG_THUMB2_KERNEL);
  15. int size;
  16. if (thumb2 && __opcode_is_thumb16(insn)) {
  17. *(u16 *)addr = __opcode_to_mem_thumb16(insn);
  18. size = sizeof(u16);
  19. } else if (thumb2 && ((uintptr_t)addr & 2)) {
  20. u16 first = __opcode_thumb32_first(insn);
  21. u16 second = __opcode_thumb32_second(insn);
  22. u16 *addrh = addr;
  23. addrh[0] = __opcode_to_mem_thumb16(first);
  24. addrh[1] = __opcode_to_mem_thumb16(second);
  25. size = sizeof(u32);
  26. } else {
  27. if (thumb2)
  28. insn = __opcode_to_mem_thumb32(insn);
  29. else
  30. insn = __opcode_to_mem_arm(insn);
  31. *(u32 *)addr = insn;
  32. size = sizeof(u32);
  33. }
  34. flush_icache_range((uintptr_t)(addr),
  35. (uintptr_t)(addr) + size);
  36. }
  37. static int __kprobes patch_text_stop_machine(void *data)
  38. {
  39. struct patch *patch = data;
  40. __patch_text(patch->addr, patch->insn);
  41. return 0;
  42. }
  43. void __kprobes patch_text(void *addr, unsigned int insn)
  44. {
  45. struct patch patch = {
  46. .addr = addr,
  47. .insn = insn,
  48. };
  49. if (cache_ops_need_broadcast()) {
  50. stop_machine(patch_text_stop_machine, &patch, cpu_online_mask);
  51. } else {
  52. bool straddles_word = IS_ENABLED(CONFIG_THUMB2_KERNEL)
  53. && __opcode_is_thumb32(insn)
  54. && ((uintptr_t)addr & 2);
  55. if (straddles_word)
  56. stop_machine(patch_text_stop_machine, &patch, NULL);
  57. else
  58. __patch_text(addr, insn);
  59. }
  60. }