003-v6.17-loongArch-BPF-Add-dynamic-code-modification-support.patch 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. From 9fbd18cf4c69f512f7de3ab73235078f3e32ecec Mon Sep 17 00:00:00 2001
  2. From: Chenghao Duan <[email protected]>
  3. Date: Tue, 5 Aug 2025 19:00:18 +0800
  4. Subject: [PATCH 03/12] LoongArch: BPF: Add dynamic code modification support
  5. This commit adds support for BPF dynamic code modification on the
  6. LoongArch architecture:
  7. 1. Add bpf_arch_text_copy() for instruction block copying.
  8. 2. Add bpf_arch_text_poke() for runtime instruction patching.
  9. 3. Add bpf_arch_text_invalidate() for code invalidation.
  10. On LoongArch, since symbol addresses in the direct mapping region can't
  11. be reached via relative jump instructions from the paged mapping region,
  12. we use the move_imm+jirl instruction pair as absolute jump instructions.
  13. These require 2-5 instructions, so we reserve 5 NOP instructions in the
  14. program as placeholders for function jumps.
  15. The larch_insn_text_copy() function is solely used for BPF. And the use
  16. of larch_insn_text_copy() requires PAGE_SIZE alignment. Currently, only
  17. the size of the BPF trampoline is page-aligned.
  18. Co-developed-by: George Guo <[email protected]>
  19. Signed-off-by: George Guo <[email protected]>
  20. Signed-off-by: Chenghao Duan <[email protected]>
  21. Signed-off-by: Huacai Chen <[email protected]>
  22. ---
  23. arch/loongarch/include/asm/inst.h | 1 +
  24. arch/loongarch/kernel/inst.c | 46 +++++++++++++
  25. arch/loongarch/net/bpf_jit.c | 105 +++++++++++++++++++++++++++++-
  26. 3 files changed, 151 insertions(+), 1 deletion(-)
  27. --- a/arch/loongarch/include/asm/inst.h
  28. +++ b/arch/loongarch/include/asm/inst.h
  29. @@ -502,6 +502,7 @@ void arch_simulate_insn(union loongarch_
  30. int larch_insn_read(void *addr, u32 *insnp);
  31. int larch_insn_write(void *addr, u32 insn);
  32. int larch_insn_patch_text(void *addr, u32 insn);
  33. +int larch_insn_text_copy(void *dst, void *src, size_t len);
  34. u32 larch_insn_gen_nop(void);
  35. u32 larch_insn_gen_b(unsigned long pc, unsigned long dest);
  36. --- a/arch/loongarch/kernel/inst.c
  37. +++ b/arch/loongarch/kernel/inst.c
  38. @@ -4,6 +4,8 @@
  39. */
  40. #include <linux/sizes.h>
  41. #include <linux/uaccess.h>
  42. +#include <linux/set_memory.h>
  43. +#include <linux/stop_machine.h>
  44. #include <asm/cacheflush.h>
  45. #include <asm/inst.h>
  46. @@ -229,6 +231,50 @@ int larch_insn_patch_text(void *addr, u3
  47. return ret;
  48. }
  49. +
  50. +struct insn_copy {
  51. + void *dst;
  52. + void *src;
  53. + size_t len;
  54. + unsigned int cpu;
  55. +};
  56. +
  57. +static int text_copy_cb(void *data)
  58. +{
  59. + int ret = 0;
  60. + struct insn_copy *copy = data;
  61. +
  62. + if (smp_processor_id() == copy->cpu) {
  63. + ret = copy_to_kernel_nofault(copy->dst, copy->src, copy->len);
  64. + if (ret)
  65. + pr_err("%s: operation failed\n", __func__);
  66. + }
  67. +
  68. + flush_icache_range((unsigned long)copy->dst, (unsigned long)copy->dst + copy->len);
  69. +
  70. + return ret;
  71. +}
  72. +
  73. +int larch_insn_text_copy(void *dst, void *src, size_t len)
  74. +{
  75. + int ret = 0;
  76. + size_t start, end;
  77. + struct insn_copy copy = {
  78. + .dst = dst,
  79. + .src = src,
  80. + .len = len,
  81. + .cpu = smp_processor_id(),
  82. + };
  83. +
  84. + start = round_down((size_t)dst, PAGE_SIZE);
  85. + end = round_up((size_t)dst + len, PAGE_SIZE);
  86. +
  87. + set_memory_rw(start, (end - start) / PAGE_SIZE);
  88. + ret = stop_machine(text_copy_cb, &copy, cpu_online_mask);
  89. + set_memory_rox(start, (end - start) / PAGE_SIZE);
  90. +
  91. + return ret;
  92. +}
  93. u32 larch_insn_gen_nop(void)
  94. {
  95. --- a/arch/loongarch/net/bpf_jit.c
  96. +++ b/arch/loongarch/net/bpf_jit.c
  97. @@ -4,8 +4,12 @@
  98. *
  99. * Copyright (C) 2022 Loongson Technology Corporation Limited
  100. */
  101. +#include <linux/memory.h>
  102. #include "bpf_jit.h"
  103. +#define LOONGARCH_LONG_JUMP_NINSNS 5
  104. +#define LOONGARCH_LONG_JUMP_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4)
  105. +
  106. #define REG_TCC LOONGARCH_GPR_A6
  107. #define TCC_SAVED LOONGARCH_GPR_S5
  108. @@ -88,7 +92,7 @@ static u8 tail_call_reg(struct jit_ctx *
  109. */
  110. static void build_prologue(struct jit_ctx *ctx)
  111. {
  112. - int stack_adjust = 0, store_offset, bpf_stack_adjust;
  113. + int i, stack_adjust = 0, store_offset, bpf_stack_adjust;
  114. bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16);
  115. @@ -98,6 +102,10 @@ static void build_prologue(struct jit_ct
  116. stack_adjust = round_up(stack_adjust, 16);
  117. stack_adjust += bpf_stack_adjust;
  118. + /* Reserve space for the move_imm + jirl instruction */
  119. + for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++)
  120. + emit_insn(ctx, nop);
  121. +
  122. /*
  123. * First instruction initializes the tail call count (TCC).
  124. * On tail call we skip this instruction, and the TCC is
  125. @@ -1202,6 +1210,101 @@ static int validate_ctx(struct jit_ctx *
  126. return 0;
  127. }
  128. +static int emit_jump_and_link(struct jit_ctx *ctx, u8 rd, u64 target)
  129. +{
  130. + if (!target) {
  131. + pr_err("bpf_jit: jump target address is error\n");
  132. + return -EFAULT;
  133. + }
  134. +
  135. + move_imm(ctx, LOONGARCH_GPR_T1, target, false);
  136. + emit_insn(ctx, jirl, rd, LOONGARCH_GPR_T1, 0);
  137. +
  138. + return 0;
  139. +}
  140. +
  141. +static int emit_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call)
  142. +{
  143. + int i;
  144. + struct jit_ctx ctx;
  145. +
  146. + ctx.idx = 0;
  147. + ctx.image = (union loongarch_instruction *)insns;
  148. +
  149. + if (!target) {
  150. + for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++)
  151. + emit_insn((&ctx), nop);
  152. + return 0;
  153. + }
  154. +
  155. + return emit_jump_and_link(&ctx, is_call ? LOONGARCH_GPR_T0 : LOONGARCH_GPR_ZERO, (u64)target);
  156. +}
  157. +
  158. +void *bpf_arch_text_copy(void *dst, void *src, size_t len)
  159. +{
  160. + int ret;
  161. +
  162. + mutex_lock(&text_mutex);
  163. + ret = larch_insn_text_copy(dst, src, len);
  164. + mutex_unlock(&text_mutex);
  165. +
  166. + return ret ? ERR_PTR(-EINVAL) : dst;
  167. +}
  168. +
  169. +int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
  170. + void *old_addr, void *new_addr)
  171. +{
  172. + int ret;
  173. + bool is_call = (poke_type == BPF_MOD_CALL);
  174. + u32 old_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP};
  175. + u32 new_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP};
  176. +
  177. + if (!is_kernel_text((unsigned long)ip) &&
  178. + !is_bpf_text_address((unsigned long)ip))
  179. + return -ENOTSUPP;
  180. +
  181. + ret = emit_jump_or_nops(old_addr, ip, old_insns, is_call);
  182. + if (ret)
  183. + return ret;
  184. +
  185. + if (memcmp(ip, old_insns, LOONGARCH_LONG_JUMP_NBYTES))
  186. + return -EFAULT;
  187. +
  188. + ret = emit_jump_or_nops(new_addr, ip, new_insns, is_call);
  189. + if (ret)
  190. + return ret;
  191. +
  192. + mutex_lock(&text_mutex);
  193. + if (memcmp(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES))
  194. + ret = larch_insn_text_copy(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES);
  195. + mutex_unlock(&text_mutex);
  196. +
  197. + return ret;
  198. +}
  199. +
  200. +int bpf_arch_text_invalidate(void *dst, size_t len)
  201. +{
  202. + int i;
  203. + int ret = 0;
  204. + u32 *inst;
  205. +
  206. + inst = kvmalloc(len, GFP_KERNEL);
  207. + if (!inst)
  208. + return -ENOMEM;
  209. +
  210. + for (i = 0; i < (len / sizeof(u32)); i++)
  211. + inst[i] = INSN_BREAK;
  212. +
  213. + mutex_lock(&text_mutex);
  214. + if (larch_insn_text_copy(dst, inst, len))
  215. + ret = -EINVAL;
  216. + mutex_unlock(&text_mutex);
  217. +
  218. + kvfree(inst);
  219. +
  220. + return ret;
  221. +}
  222. +
  223. struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
  224. {
  225. bool tmp_blinded = false, extra_pass = false;