Implement the functions of bpf_arch_text_poke, bpf_arch_text_copy, and
bpf_arch_text_invalidate on the LoongArch architecture.
On LoongArch, since symbol addresses in the direct mapping
region cannot be reached via relative jump instructions from the paged
mapping region, we use the move_imm+jirl instruction pair as absolute
jump instructions. These require 2-5 instructions, so we reserve 5 NOP
instructions in the program as placeholders for function jumps.
larch_insn_text_copy is solely used for BPF. The use of
larch_insn_text_copy() requires page_size alignment. Currently, only
the size of the trampoline is page-aligned.
Co-developed-by: George Guo <guodongtai@kylinos.cn>
Signed-off-by: George Guo <guodongtai@kylinos.cn>
Signed-off-by: Chenghao Duan <duanchenghao@kylinos.cn>
Reviewed-by: Hengqi Chen <hengqi.chen@gmail.com>
Reviewed-by: Huacai Chen <chenhuacai@kernel.org>
---
arch/loongarch/include/asm/inst.h | 1 +
arch/loongarch/kernel/inst.c | 32 ++++++++++
arch/loongarch/net/bpf_jit.c | 97 +++++++++++++++++++++++++++++++
3 files changed, 130 insertions(+)
diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
index 2ae96a35d..88bb73e46 100644
--- a/arch/loongarch/include/asm/inst.h
+++ b/arch/loongarch/include/asm/inst.h
@@ -497,6 +497,7 @@ void arch_simulate_insn(union loongarch_instruction insn, struct pt_regs *regs);
int larch_insn_read(void *addr, u32 *insnp);
int larch_insn_write(void *addr, u32 insn);
int larch_insn_patch_text(void *addr, u32 insn);
+int larch_insn_text_copy(void *dst, void *src, size_t len);
u32 larch_insn_gen_nop(void);
u32 larch_insn_gen_b(unsigned long pc, unsigned long dest);
diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c
index 674e3b322..8d6594968 100644
--- a/arch/loongarch/kernel/inst.c
+++ b/arch/loongarch/kernel/inst.c
@@ -4,6 +4,7 @@
*/
#include <linux/sizes.h>
#include <linux/uaccess.h>
+#include <linux/set_memory.h>
#include <asm/cacheflush.h>
#include <asm/inst.h>
@@ -218,6 +219,37 @@ int larch_insn_patch_text(void *addr, u32 insn)
return ret;
}
+int larch_insn_text_copy(void *dst, void *src, size_t len)
+{
+ unsigned long flags;
+ size_t wlen = 0;
+ size_t size;
+ void *ptr;
+ int ret = 0;
+
+ set_memory_rw((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE);
+ raw_spin_lock_irqsave(&patch_lock, flags);
+ while (wlen < len) {
+ ptr = dst + wlen;
+ size = min_t(size_t, PAGE_SIZE - offset_in_page(ptr),
+ len - wlen);
+
+ ret = copy_to_kernel_nofault(ptr, src + wlen, size);
+ if (ret) {
+ pr_err("%s: operation failed\n", __func__);
+ break;
+ }
+ wlen += size;
+ }
+ raw_spin_unlock_irqrestore(&patch_lock, flags);
+ set_memory_rox((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE);
+
+ if (!ret)
+ flush_icache_range((unsigned long)dst, (unsigned long)dst + len);
+
+ return ret;
+}
+
u32 larch_insn_gen_nop(void)
{
return INSN_NOP;
diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c
index 7032f11d3..86504e710 100644
--- a/arch/loongarch/net/bpf_jit.c
+++ b/arch/loongarch/net/bpf_jit.c
@@ -4,8 +4,12 @@
*
* Copyright (C) 2022 Loongson Technology Corporation Limited
*/
+#include <linux/memory.h>
#include "bpf_jit.h"
+#define LOONGARCH_LONG_JUMP_NINSNS 5
+#define LOONGARCH_LONG_JUMP_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4)
+
#define REG_TCC LOONGARCH_GPR_A6
#define TCC_SAVED LOONGARCH_GPR_S5
@@ -88,6 +92,7 @@ static u8 tail_call_reg(struct jit_ctx *ctx)
*/
static void build_prologue(struct jit_ctx *ctx)
{
+ int i;
int stack_adjust = 0, store_offset, bpf_stack_adjust;
bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16);
@@ -98,6 +103,10 @@ static void build_prologue(struct jit_ctx *ctx)
stack_adjust = round_up(stack_adjust, 16);
stack_adjust += bpf_stack_adjust;
+ /* Reserve space for the move_imm + jirl instruction */
+ for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++)
+ emit_insn(ctx, nop);
+
/*
* First instruction initializes the tail call count (TCC).
* On tail call we skip this instruction, and the TCC is
@@ -1367,3 +1376,91 @@ bool bpf_jit_supports_subprog_tailcalls(void)
{
return true;
}
+
+static int emit_jump_and_link(struct jit_ctx *ctx, u8 rd, u64 target)
+{
+ if (!target) {
+ pr_err("bpf_jit: jump target address is error\n");
+ return -EFAULT;
+ }
+
+ move_imm(ctx, LOONGARCH_GPR_T1, target, false);
+ emit_insn(ctx, jirl, rd, LOONGARCH_GPR_T1, 0);
+
+ return 0;
+}
+
+static int gen_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call)
+{
+ struct jit_ctx ctx;
+
+ ctx.idx = 0;
+ ctx.image = (union loongarch_instruction *)insns;
+
+ if (!target) {
+ emit_insn((&ctx), nop);
+ emit_insn((&ctx), nop);
+ return 0;
+ }
+
+ return emit_jump_and_link(&ctx, is_call ? LOONGARCH_GPR_T0 : LOONGARCH_GPR_ZERO,
+ (unsigned long)target);
+}
+
+int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
+ void *old_addr, void *new_addr)
+{
+ u32 old_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP};
+ u32 new_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP};
+ bool is_call = poke_type == BPF_MOD_CALL;
+ int ret;
+
+ if (!is_kernel_text((unsigned long)ip) &&
+ !is_bpf_text_address((unsigned long)ip))
+ return -ENOTSUPP;
+
+ ret = gen_jump_or_nops(old_addr, ip, old_insns, is_call);
+ if (ret)
+ return ret;
+
+ if (memcmp(ip, old_insns, LOONGARCH_LONG_JUMP_NBYTES))
+ return -EFAULT;
+
+ ret = gen_jump_or_nops(new_addr, ip, new_insns, is_call);
+ if (ret)
+ return ret;
+
+ mutex_lock(&text_mutex);
+ if (memcmp(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES))
+ ret = larch_insn_text_copy(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES);
+ mutex_unlock(&text_mutex);
+ return ret;
+}
+
+int bpf_arch_text_invalidate(void *dst, size_t len)
+{
+ int i;
+ int ret = 0;
+ u32 *inst;
+
+ inst = kvmalloc(len, GFP_KERNEL);
+ if (!inst)
+ return -ENOMEM;
+
+ for (i = 0; i < (len/sizeof(u32)); i++)
+ inst[i] = INSN_BREAK;
+
+ if (larch_insn_text_copy(dst, inst, len))
+ ret = -EINVAL;
+
+ kvfree(inst);
+ return ret;
+}
+
+void *bpf_arch_text_copy(void *dst, void *src, size_t len)
+{
+ if (larch_insn_text_copy(dst, src, len))
+ return ERR_PTR(-EINVAL);
+
+ return dst;
+}
--
2.25.1
On Thu, Jul 24, 2025 at 10:21 PM Chenghao Duan <duanchenghao@kylinos.cn> wrote: > > Implement the functions of bpf_arch_text_poke, bpf_arch_text_copy, and > bpf_arch_text_invalidate on the LoongArch architecture. > > On LoongArch, since symbol addresses in the direct mapping > region cannot be reached via relative jump instructions from the paged > mapping region, we use the move_imm+jirl instruction pair as absolute > jump instructions. These require 2-5 instructions, so we reserve 5 NOP > instructions in the program as placeholders for function jumps. > > larch_insn_text_copy is solely used for BPF. The use of > larch_insn_text_copy() requires page_size alignment. Currently, only > the size of the trampoline is page-aligned. > The subject line seems kind of casual, bpf_arch_xxxxx ? > Co-developed-by: George Guo <guodongtai@kylinos.cn> > Signed-off-by: George Guo <guodongtai@kylinos.cn> > Signed-off-by: Chenghao Duan <duanchenghao@kylinos.cn> > Reviewed-by: Hengqi Chen <hengqi.chen@gmail.com> > Reviewed-by: Huacai Chen <chenhuacai@kernel.org> I didn't leave a Reviewed-by tag last time, no ? > --- > arch/loongarch/include/asm/inst.h | 1 + > arch/loongarch/kernel/inst.c | 32 ++++++++++ > arch/loongarch/net/bpf_jit.c | 97 +++++++++++++++++++++++++++++++ > 3 files changed, 130 insertions(+) > > diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h > index 2ae96a35d..88bb73e46 100644 > --- a/arch/loongarch/include/asm/inst.h > +++ b/arch/loongarch/include/asm/inst.h > @@ -497,6 +497,7 @@ void arch_simulate_insn(union loongarch_instruction insn, struct pt_regs *regs); > int larch_insn_read(void *addr, u32 *insnp); > int larch_insn_write(void *addr, u32 insn); > int larch_insn_patch_text(void *addr, u32 insn); > +int larch_insn_text_copy(void *dst, void *src, size_t len); > > u32 larch_insn_gen_nop(void); > u32 larch_insn_gen_b(unsigned long pc, unsigned long dest); > diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c > index 674e3b322..8d6594968 100644 > --- a/arch/loongarch/kernel/inst.c > +++ b/arch/loongarch/kernel/inst.c > @@ -4,6 +4,7 @@ > */ > #include <linux/sizes.h> > #include <linux/uaccess.h> > +#include <linux/set_memory.h> > > #include <asm/cacheflush.h> > #include <asm/inst.h> > @@ -218,6 +219,37 @@ int larch_insn_patch_text(void *addr, u32 insn) > return ret; > } > > +int larch_insn_text_copy(void *dst, void *src, size_t len) > +{ > + unsigned long flags; > + size_t wlen = 0; > + size_t size; > + void *ptr; > + int ret = 0; > + > + set_memory_rw((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE); > + raw_spin_lock_irqsave(&patch_lock, flags); > + while (wlen < len) { > + ptr = dst + wlen; > + size = min_t(size_t, PAGE_SIZE - offset_in_page(ptr), > + len - wlen); > + > + ret = copy_to_kernel_nofault(ptr, src + wlen, size); > + if (ret) { > + pr_err("%s: operation failed\n", __func__); > + break; > + } > + wlen += size; > + } > + raw_spin_unlock_irqrestore(&patch_lock, flags); > + set_memory_rox((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE); > + > + if (!ret) > + flush_icache_range((unsigned long)dst, (unsigned long)dst + len); > + > + return ret; > +} > + > u32 larch_insn_gen_nop(void) > { > return INSN_NOP; > diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c > index 7032f11d3..86504e710 100644 > --- a/arch/loongarch/net/bpf_jit.c > +++ b/arch/loongarch/net/bpf_jit.c > @@ -4,8 +4,12 @@ > * > * Copyright (C) 2022 Loongson Technology Corporation Limited > */ > +#include <linux/memory.h> > #include "bpf_jit.h" > > +#define LOONGARCH_LONG_JUMP_NINSNS 5 > +#define LOONGARCH_LONG_JUMP_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4) > + > #define REG_TCC LOONGARCH_GPR_A6 > #define TCC_SAVED LOONGARCH_GPR_S5 > > @@ -88,6 +92,7 @@ static u8 tail_call_reg(struct jit_ctx *ctx) > */ > static void build_prologue(struct jit_ctx *ctx) > { > + int i; > int stack_adjust = 0, store_offset, bpf_stack_adjust; > > bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16); > @@ -98,6 +103,10 @@ static void build_prologue(struct jit_ctx *ctx) > stack_adjust = round_up(stack_adjust, 16); > stack_adjust += bpf_stack_adjust; > > + /* Reserve space for the move_imm + jirl instruction */ > + for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++) > + emit_insn(ctx, nop); > + > /* > * First instruction initializes the tail call count (TCC). > * On tail call we skip this instruction, and the TCC is > @@ -1367,3 +1376,91 @@ bool bpf_jit_supports_subprog_tailcalls(void) > { > return true; > } > + > +static int emit_jump_and_link(struct jit_ctx *ctx, u8 rd, u64 target) > +{ > + if (!target) { > + pr_err("bpf_jit: jump target address is error\n"); > + return -EFAULT; > + } > + > + move_imm(ctx, LOONGARCH_GPR_T1, target, false); > + emit_insn(ctx, jirl, rd, LOONGARCH_GPR_T1, 0); > + > + return 0; > +} > + > +static int gen_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call) > +{ > + struct jit_ctx ctx; > + > + ctx.idx = 0; > + ctx.image = (union loongarch_instruction *)insns; > + > + if (!target) { > + emit_insn((&ctx), nop); > + emit_insn((&ctx), nop); > + return 0; > + } > + > + return emit_jump_and_link(&ctx, is_call ? LOONGARCH_GPR_T0 : LOONGARCH_GPR_ZERO, > + (unsigned long)target); > +} > + > +int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, > + void *old_addr, void *new_addr) > +{ > + u32 old_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; > + u32 new_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; > + bool is_call = poke_type == BPF_MOD_CALL; > + int ret; > + > + if (!is_kernel_text((unsigned long)ip) && > + !is_bpf_text_address((unsigned long)ip)) > + return -ENOTSUPP; > + > + ret = gen_jump_or_nops(old_addr, ip, old_insns, is_call); > + if (ret) > + return ret; > + > + if (memcmp(ip, old_insns, LOONGARCH_LONG_JUMP_NBYTES)) > + return -EFAULT; > + > + ret = gen_jump_or_nops(new_addr, ip, new_insns, is_call); > + if (ret) > + return ret; > + > + mutex_lock(&text_mutex); > + if (memcmp(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES)) > + ret = larch_insn_text_copy(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES); > + mutex_unlock(&text_mutex); > + return ret; > +} > + > +int bpf_arch_text_invalidate(void *dst, size_t len) > +{ > + int i; > + int ret = 0; > + u32 *inst; > + > + inst = kvmalloc(len, GFP_KERNEL); > + if (!inst) > + return -ENOMEM; > + > + for (i = 0; i < (len/sizeof(u32)); i++) > + inst[i] = INSN_BREAK; > + > + if (larch_insn_text_copy(dst, inst, len)) > + ret = -EINVAL; > + > + kvfree(inst); > + return ret; > +} > + > +void *bpf_arch_text_copy(void *dst, void *src, size_t len) > +{ > + if (larch_insn_text_copy(dst, src, len)) > + return ERR_PTR(-EINVAL); > + > + return dst; > +} > -- > 2.25.1 >
On Mon, Jul 28, 2025 at 06:58:41PM +0800, Hengqi Chen wrote: > On Thu, Jul 24, 2025 at 10:21 PM Chenghao Duan <duanchenghao@kylinos.cn> wrote: > > > > Implement the functions of bpf_arch_text_poke, bpf_arch_text_copy, and > > bpf_arch_text_invalidate on the LoongArch architecture. > > > > On LoongArch, since symbol addresses in the direct mapping > > region cannot be reached via relative jump instructions from the paged > > mapping region, we use the move_imm+jirl instruction pair as absolute > > jump instructions. These require 2-5 instructions, so we reserve 5 NOP > > instructions in the program as placeholders for function jumps. > > > > larch_insn_text_copy is solely used for BPF. The use of > > larch_insn_text_copy() requires page_size alignment. Currently, only > > the size of the trampoline is page-aligned. > > > > The subject line seems kind of casual, bpf_arch_xxxxx ? Here is the modified commit log. Please take a look. LoongArch: BPF: Implement dynamic code modification support This commit adds the necessary infrastructure for BPF dynamic code modification on LoongArch architecture: 1. Implement bpf_arch_text_poke() for runtime instruction patching. 2. Add bpf_arch_text_copy() for instruction block copying. 3. Create bpf_arch_text_invalidate() for code invalidation. On LoongArch, since symbol addresses in the direct mapping region cannot be reached via relative jump instructions from the paged mapping region, we use the move_imm+jirl instruction pair as absolute jump instructions. These require 2-5 instructions, so we reserve 5 NOP instructions in the program as placeholders for function jumps. arch_insn_text_copy is solely used for BPF. The use of larch_insn_text_copy() requires page_size alignment. Currently, only the size of the trampoline is page-aligned. > > > Co-developed-by: George Guo <guodongtai@kylinos.cn> > > Signed-off-by: George Guo <guodongtai@kylinos.cn> > > Signed-off-by: Chenghao Duan <duanchenghao@kylinos.cn> > > Reviewed-by: Hengqi Chen <hengqi.chen@gmail.com> > > Reviewed-by: Huacai Chen <chenhuacai@kernel.org> > > I didn't leave a Reviewed-by tag last time, no ? I added this. If there are any mistakes, please correct them. > > > --- > > arch/loongarch/include/asm/inst.h | 1 + > > arch/loongarch/kernel/inst.c | 32 ++++++++++ > > arch/loongarch/net/bpf_jit.c | 97 +++++++++++++++++++++++++++++++ > > 3 files changed, 130 insertions(+) > > > > diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h > > index 2ae96a35d..88bb73e46 100644 > > --- a/arch/loongarch/include/asm/inst.h > > +++ b/arch/loongarch/include/asm/inst.h > > @@ -497,6 +497,7 @@ void arch_simulate_insn(union loongarch_instruction insn, struct pt_regs *regs); > > int larch_insn_read(void *addr, u32 *insnp); > > int larch_insn_write(void *addr, u32 insn); > > int larch_insn_patch_text(void *addr, u32 insn); > > +int larch_insn_text_copy(void *dst, void *src, size_t len); > > > > u32 larch_insn_gen_nop(void); > > u32 larch_insn_gen_b(unsigned long pc, unsigned long dest); > > diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c > > index 674e3b322..8d6594968 100644 > > --- a/arch/loongarch/kernel/inst.c > > +++ b/arch/loongarch/kernel/inst.c > > @@ -4,6 +4,7 @@ > > */ > > #include <linux/sizes.h> > > #include <linux/uaccess.h> > > +#include <linux/set_memory.h> > > > > #include <asm/cacheflush.h> > > #include <asm/inst.h> > > @@ -218,6 +219,37 @@ int larch_insn_patch_text(void *addr, u32 insn) > > return ret; > > } > > > > +int larch_insn_text_copy(void *dst, void *src, size_t len) > > +{ > > + unsigned long flags; > > + size_t wlen = 0; > > + size_t size; > > + void *ptr; > > + int ret = 0; > > + > > + set_memory_rw((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE); > > + raw_spin_lock_irqsave(&patch_lock, flags); > > + while (wlen < len) { > > + ptr = dst + wlen; > > + size = min_t(size_t, PAGE_SIZE - offset_in_page(ptr), > > + len - wlen); > > + > > + ret = copy_to_kernel_nofault(ptr, src + wlen, size); > > + if (ret) { > > + pr_err("%s: operation failed\n", __func__); > > + break; > > + } > > + wlen += size; > > + } > > + raw_spin_unlock_irqrestore(&patch_lock, flags); > > + set_memory_rox((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE); > > + > > + if (!ret) > > + flush_icache_range((unsigned long)dst, (unsigned long)dst + len); > > + > > + return ret; > > +} > > + > > u32 larch_insn_gen_nop(void) > > { > > return INSN_NOP; > > diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c > > index 7032f11d3..86504e710 100644 > > --- a/arch/loongarch/net/bpf_jit.c > > +++ b/arch/loongarch/net/bpf_jit.c > > @@ -4,8 +4,12 @@ > > * > > * Copyright (C) 2022 Loongson Technology Corporation Limited > > */ > > +#include <linux/memory.h> > > #include "bpf_jit.h" > > > > +#define LOONGARCH_LONG_JUMP_NINSNS 5 > > +#define LOONGARCH_LONG_JUMP_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4) > > + > > #define REG_TCC LOONGARCH_GPR_A6 > > #define TCC_SAVED LOONGARCH_GPR_S5 > > > > @@ -88,6 +92,7 @@ static u8 tail_call_reg(struct jit_ctx *ctx) > > */ > > static void build_prologue(struct jit_ctx *ctx) > > { > > + int i; > > int stack_adjust = 0, store_offset, bpf_stack_adjust; > > > > bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16); > > @@ -98,6 +103,10 @@ static void build_prologue(struct jit_ctx *ctx) > > stack_adjust = round_up(stack_adjust, 16); > > stack_adjust += bpf_stack_adjust; > > > > + /* Reserve space for the move_imm + jirl instruction */ > > + for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++) > > + emit_insn(ctx, nop); > > + > > /* > > * First instruction initializes the tail call count (TCC). > > * On tail call we skip this instruction, and the TCC is > > @@ -1367,3 +1376,91 @@ bool bpf_jit_supports_subprog_tailcalls(void) > > { > > return true; > > } > > + > > +static int emit_jump_and_link(struct jit_ctx *ctx, u8 rd, u64 target) > > +{ > > + if (!target) { > > + pr_err("bpf_jit: jump target address is error\n"); > > + return -EFAULT; > > + } > > + > > + move_imm(ctx, LOONGARCH_GPR_T1, target, false); > > + emit_insn(ctx, jirl, rd, LOONGARCH_GPR_T1, 0); > > + > > + return 0; > > +} > > + > > +static int gen_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call) > > +{ > > + struct jit_ctx ctx; > > + > > + ctx.idx = 0; > > + ctx.image = (union loongarch_instruction *)insns; > > + > > + if (!target) { > > + emit_insn((&ctx), nop); > > + emit_insn((&ctx), nop); > > + return 0; > > + } > > + > > + return emit_jump_and_link(&ctx, is_call ? LOONGARCH_GPR_T0 : LOONGARCH_GPR_ZERO, > > + (unsigned long)target); > > +} > > + > > +int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, > > + void *old_addr, void *new_addr) > > +{ > > + u32 old_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; > > + u32 new_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; > > + bool is_call = poke_type == BPF_MOD_CALL; > > + int ret; > > + > > + if (!is_kernel_text((unsigned long)ip) && > > + !is_bpf_text_address((unsigned long)ip)) > > + return -ENOTSUPP; > > + > > + ret = gen_jump_or_nops(old_addr, ip, old_insns, is_call); > > + if (ret) > > + return ret; > > + > > + if (memcmp(ip, old_insns, LOONGARCH_LONG_JUMP_NBYTES)) > > + return -EFAULT; > > + > > + ret = gen_jump_or_nops(new_addr, ip, new_insns, is_call); > > + if (ret) > > + return ret; > > + > > + mutex_lock(&text_mutex); > > + if (memcmp(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES)) > > + ret = larch_insn_text_copy(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES); > > + mutex_unlock(&text_mutex); > > + return ret; > > +} > > + > > +int bpf_arch_text_invalidate(void *dst, size_t len) > > +{ > > + int i; > > + int ret = 0; > > + u32 *inst; > > + > > + inst = kvmalloc(len, GFP_KERNEL); > > + if (!inst) > > + return -ENOMEM; > > + > > + for (i = 0; i < (len/sizeof(u32)); i++) > > + inst[i] = INSN_BREAK; > > + > > + if (larch_insn_text_copy(dst, inst, len)) > > + ret = -EINVAL; > > + > > + kvfree(inst); > > + return ret; > > +} > > + > > +void *bpf_arch_text_copy(void *dst, void *src, size_t len) > > +{ > > + if (larch_insn_text_copy(dst, src, len)) > > + return ERR_PTR(-EINVAL); > > + > > + return dst; > > +} > > -- > > 2.25.1 > >
On Thu, Jul 24, 2025 at 10:21 PM Chenghao Duan <duanchenghao@kylinos.cn> wrote: > > Implement the functions of bpf_arch_text_poke, bpf_arch_text_copy, and > bpf_arch_text_invalidate on the LoongArch architecture. > > On LoongArch, since symbol addresses in the direct mapping > region cannot be reached via relative jump instructions from the paged > mapping region, we use the move_imm+jirl instruction pair as absolute > jump instructions. These require 2-5 instructions, so we reserve 5 NOP > instructions in the program as placeholders for function jumps. > > larch_insn_text_copy is solely used for BPF. The use of > larch_insn_text_copy() requires page_size alignment. Currently, only > the size of the trampoline is page-aligned. > > Co-developed-by: George Guo <guodongtai@kylinos.cn> > Signed-off-by: George Guo <guodongtai@kylinos.cn> > Signed-off-by: Chenghao Duan <duanchenghao@kylinos.cn> > Reviewed-by: Hengqi Chen <hengqi.chen@gmail.com> > Reviewed-by: Huacai Chen <chenhuacai@kernel.org> > --- > arch/loongarch/include/asm/inst.h | 1 + > arch/loongarch/kernel/inst.c | 32 ++++++++++ > arch/loongarch/net/bpf_jit.c | 97 +++++++++++++++++++++++++++++++ > 3 files changed, 130 insertions(+) > > diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h > index 2ae96a35d..88bb73e46 100644 > --- a/arch/loongarch/include/asm/inst.h > +++ b/arch/loongarch/include/asm/inst.h > @@ -497,6 +497,7 @@ void arch_simulate_insn(union loongarch_instruction insn, struct pt_regs *regs); > int larch_insn_read(void *addr, u32 *insnp); > int larch_insn_write(void *addr, u32 insn); > int larch_insn_patch_text(void *addr, u32 insn); > +int larch_insn_text_copy(void *dst, void *src, size_t len); > > u32 larch_insn_gen_nop(void); > u32 larch_insn_gen_b(unsigned long pc, unsigned long dest); > diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c > index 674e3b322..8d6594968 100644 > --- a/arch/loongarch/kernel/inst.c > +++ b/arch/loongarch/kernel/inst.c > @@ -4,6 +4,7 @@ > */ > #include <linux/sizes.h> > #include <linux/uaccess.h> > +#include <linux/set_memory.h> > > #include <asm/cacheflush.h> > #include <asm/inst.h> > @@ -218,6 +219,37 @@ int larch_insn_patch_text(void *addr, u32 insn) > return ret; > } > > +int larch_insn_text_copy(void *dst, void *src, size_t len) > +{ > + unsigned long flags; > + size_t wlen = 0; > + size_t size; > + void *ptr; > + int ret = 0; > + > + set_memory_rw((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE); > + raw_spin_lock_irqsave(&patch_lock, flags); > + while (wlen < len) { > + ptr = dst + wlen; > + size = min_t(size_t, PAGE_SIZE - offset_in_page(ptr), > + len - wlen); > + > + ret = copy_to_kernel_nofault(ptr, src + wlen, size); > + if (ret) { > + pr_err("%s: operation failed\n", __func__); > + break; > + } > + wlen += size; > + } > + raw_spin_unlock_irqrestore(&patch_lock, flags); > + set_memory_rox((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE); > + > + if (!ret) > + flush_icache_range((unsigned long)dst, (unsigned long)dst + len); > + > + return ret; > +} > + > u32 larch_insn_gen_nop(void) > { > return INSN_NOP; > diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c > index 7032f11d3..86504e710 100644 > --- a/arch/loongarch/net/bpf_jit.c > +++ b/arch/loongarch/net/bpf_jit.c > @@ -4,8 +4,12 @@ > * > * Copyright (C) 2022 Loongson Technology Corporation Limited > */ > +#include <linux/memory.h> > #include "bpf_jit.h" > > +#define LOONGARCH_LONG_JUMP_NINSNS 5 > +#define LOONGARCH_LONG_JUMP_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4) > + > #define REG_TCC LOONGARCH_GPR_A6 > #define TCC_SAVED LOONGARCH_GPR_S5 > > @@ -88,6 +92,7 @@ static u8 tail_call_reg(struct jit_ctx *ctx) > */ > static void build_prologue(struct jit_ctx *ctx) > { > + int i; > int stack_adjust = 0, store_offset, bpf_stack_adjust; > > bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16); > @@ -98,6 +103,10 @@ static void build_prologue(struct jit_ctx *ctx) > stack_adjust = round_up(stack_adjust, 16); > stack_adjust += bpf_stack_adjust; > > + /* Reserve space for the move_imm + jirl instruction */ > + for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++) > + emit_insn(ctx, nop); > + > /* > * First instruction initializes the tail call count (TCC). > * On tail call we skip this instruction, and the TCC is > @@ -1367,3 +1376,91 @@ bool bpf_jit_supports_subprog_tailcalls(void) > { > return true; > } > + > +static int emit_jump_and_link(struct jit_ctx *ctx, u8 rd, u64 target) > +{ > + if (!target) { > + pr_err("bpf_jit: jump target address is error\n"); is error ? is NULL ? > + return -EFAULT; > + } > + > + move_imm(ctx, LOONGARCH_GPR_T1, target, false); > + emit_insn(ctx, jirl, rd, LOONGARCH_GPR_T1, 0); > + > + return 0; > +} > + > +static int gen_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call) > +{ > + struct jit_ctx ctx; > + > + ctx.idx = 0; > + ctx.image = (union loongarch_instruction *)insns; > + > + if (!target) { > + emit_insn((&ctx), nop); > + emit_insn((&ctx), nop); > + return 0; > + } > + > + return emit_jump_and_link(&ctx, is_call ? LOONGARCH_GPR_T0 : LOONGARCH_GPR_ZERO, > + (unsigned long)target); > +} > + > +int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, > + void *old_addr, void *new_addr) > +{ > + u32 old_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; > + u32 new_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; > + bool is_call = poke_type == BPF_MOD_CALL; > + int ret; > + > + if (!is_kernel_text((unsigned long)ip) && > + !is_bpf_text_address((unsigned long)ip)) > + return -ENOTSUPP; > + > + ret = gen_jump_or_nops(old_addr, ip, old_insns, is_call); > + if (ret) > + return ret; > + > + if (memcmp(ip, old_insns, LOONGARCH_LONG_JUMP_NBYTES)) > + return -EFAULT; > + > + ret = gen_jump_or_nops(new_addr, ip, new_insns, is_call); > + if (ret) > + return ret; > + > + mutex_lock(&text_mutex); > + if (memcmp(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES)) > + ret = larch_insn_text_copy(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES); > + mutex_unlock(&text_mutex); > + return ret; > +} > + > +int bpf_arch_text_invalidate(void *dst, size_t len) > +{ > + int i; > + int ret = 0; > + u32 *inst; > + > + inst = kvmalloc(len, GFP_KERNEL); > + if (!inst) > + return -ENOMEM; > + > + for (i = 0; i < (len/sizeof(u32)); i++) > + inst[i] = INSN_BREAK; > + > + if (larch_insn_text_copy(dst, inst, len)) Do we need text_mutex here and below for larch_insn_text_copy() ? > + ret = -EINVAL; > + > + kvfree(inst); > + return ret; > +} > + > +void *bpf_arch_text_copy(void *dst, void *src, size_t len) > +{ > + if (larch_insn_text_copy(dst, src, len)) > + return ERR_PTR(-EINVAL); > + > + return dst; > +} > -- > 2.25.1 >
On Mon, Jul 28, 2025 at 06:47:03PM +0800, Hengqi Chen wrote: > On Thu, Jul 24, 2025 at 10:21 PM Chenghao Duan <duanchenghao@kylinos.cn> wrote: > > > > Implement the functions of bpf_arch_text_poke, bpf_arch_text_copy, and > > bpf_arch_text_invalidate on the LoongArch architecture. > > > > On LoongArch, since symbol addresses in the direct mapping > > region cannot be reached via relative jump instructions from the paged > > mapping region, we use the move_imm+jirl instruction pair as absolute > > jump instructions. These require 2-5 instructions, so we reserve 5 NOP > > instructions in the program as placeholders for function jumps. > > > > larch_insn_text_copy is solely used for BPF. The use of > > larch_insn_text_copy() requires page_size alignment. Currently, only > > the size of the trampoline is page-aligned. > > > > Co-developed-by: George Guo <guodongtai@kylinos.cn> > > Signed-off-by: George Guo <guodongtai@kylinos.cn> > > Signed-off-by: Chenghao Duan <duanchenghao@kylinos.cn> > > Reviewed-by: Hengqi Chen <hengqi.chen@gmail.com> > > Reviewed-by: Huacai Chen <chenhuacai@kernel.org> > > --- > > arch/loongarch/include/asm/inst.h | 1 + > > arch/loongarch/kernel/inst.c | 32 ++++++++++ > > arch/loongarch/net/bpf_jit.c | 97 +++++++++++++++++++++++++++++++ > > 3 files changed, 130 insertions(+) > > > > diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h > > index 2ae96a35d..88bb73e46 100644 > > --- a/arch/loongarch/include/asm/inst.h > > +++ b/arch/loongarch/include/asm/inst.h > > @@ -497,6 +497,7 @@ void arch_simulate_insn(union loongarch_instruction insn, struct pt_regs *regs); > > int larch_insn_read(void *addr, u32 *insnp); > > int larch_insn_write(void *addr, u32 insn); > > int larch_insn_patch_text(void *addr, u32 insn); > > +int larch_insn_text_copy(void *dst, void *src, size_t len); > > > > u32 larch_insn_gen_nop(void); > > u32 larch_insn_gen_b(unsigned long pc, unsigned long dest); > > diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c > > index 674e3b322..8d6594968 100644 > > --- a/arch/loongarch/kernel/inst.c > > +++ b/arch/loongarch/kernel/inst.c > > @@ -4,6 +4,7 @@ > > */ > > #include <linux/sizes.h> > > #include <linux/uaccess.h> > > +#include <linux/set_memory.h> > > > > #include <asm/cacheflush.h> > > #include <asm/inst.h> > > @@ -218,6 +219,37 @@ int larch_insn_patch_text(void *addr, u32 insn) > > return ret; > > } > > > > +int larch_insn_text_copy(void *dst, void *src, size_t len) > > +{ > > + unsigned long flags; > > + size_t wlen = 0; > > + size_t size; > > + void *ptr; > > + int ret = 0; > > + > > + set_memory_rw((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE); > > + raw_spin_lock_irqsave(&patch_lock, flags); > > + while (wlen < len) { > > + ptr = dst + wlen; > > + size = min_t(size_t, PAGE_SIZE - offset_in_page(ptr), > > + len - wlen); > > + > > + ret = copy_to_kernel_nofault(ptr, src + wlen, size); > > + if (ret) { > > + pr_err("%s: operation failed\n", __func__); > > + break; > > + } > > + wlen += size; > > + } > > + raw_spin_unlock_irqrestore(&patch_lock, flags); > > + set_memory_rox((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE); > > + > > + if (!ret) > > + flush_icache_range((unsigned long)dst, (unsigned long)dst + len); > > + > > + return ret; > > +} > > + > > u32 larch_insn_gen_nop(void) > > { > > return INSN_NOP; > > diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c > > index 7032f11d3..86504e710 100644 > > --- a/arch/loongarch/net/bpf_jit.c > > +++ b/arch/loongarch/net/bpf_jit.c > > @@ -4,8 +4,12 @@ > > * > > * Copyright (C) 2022 Loongson Technology Corporation Limited > > */ > > +#include <linux/memory.h> > > #include "bpf_jit.h" > > > > +#define LOONGARCH_LONG_JUMP_NINSNS 5 > > +#define LOONGARCH_LONG_JUMP_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4) > > + > > #define REG_TCC LOONGARCH_GPR_A6 > > #define TCC_SAVED LOONGARCH_GPR_S5 > > > > @@ -88,6 +92,7 @@ static u8 tail_call_reg(struct jit_ctx *ctx) > > */ > > static void build_prologue(struct jit_ctx *ctx) > > { > > + int i; > > int stack_adjust = 0, store_offset, bpf_stack_adjust; > > > > bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16); > > @@ -98,6 +103,10 @@ static void build_prologue(struct jit_ctx *ctx) > > stack_adjust = round_up(stack_adjust, 16); > > stack_adjust += bpf_stack_adjust; > > > > + /* Reserve space for the move_imm + jirl instruction */ > > + for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++) > > + emit_insn(ctx, nop); > > + > > /* > > * First instruction initializes the tail call count (TCC). > > * On tail call we skip this instruction, and the TCC is > > @@ -1367,3 +1376,91 @@ bool bpf_jit_supports_subprog_tailcalls(void) > > { > > return true; > > } > > + > > +static int emit_jump_and_link(struct jit_ctx *ctx, u8 rd, u64 target) > > +{ > > + if (!target) { > > + pr_err("bpf_jit: jump target address is error\n"); > > is error ? is NULL ? What I mean is: This is an illegal target address. > > > + return -EFAULT; > > + } > > + > > + move_imm(ctx, LOONGARCH_GPR_T1, target, false); > > + emit_insn(ctx, jirl, rd, LOONGARCH_GPR_T1, 0); > > + > > + return 0; > > +} > > + > > +static int gen_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call) > > +{ > > + struct jit_ctx ctx; > > + > > + ctx.idx = 0; > > + ctx.image = (union loongarch_instruction *)insns; > > + > > + if (!target) { > > + emit_insn((&ctx), nop); > > + emit_insn((&ctx), nop); > > + return 0; > > + } > > + > > + return emit_jump_and_link(&ctx, is_call ? LOONGARCH_GPR_T0 : LOONGARCH_GPR_ZERO, > > + (unsigned long)target); > > +} > > + > > +int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, > > + void *old_addr, void *new_addr) > > +{ > > + u32 old_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; > > + u32 new_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; > > + bool is_call = poke_type == BPF_MOD_CALL; > > + int ret; > > + > > + if (!is_kernel_text((unsigned long)ip) && > > + !is_bpf_text_address((unsigned long)ip)) > > + return -ENOTSUPP; > > + > > + ret = gen_jump_or_nops(old_addr, ip, old_insns, is_call); > > + if (ret) > > + return ret; > > + > > + if (memcmp(ip, old_insns, LOONGARCH_LONG_JUMP_NBYTES)) > > + return -EFAULT; > > + > > + ret = gen_jump_or_nops(new_addr, ip, new_insns, is_call); > > + if (ret) > > + return ret; > > + > > + mutex_lock(&text_mutex); > > + if (memcmp(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES)) > > + ret = larch_insn_text_copy(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES); > > + mutex_unlock(&text_mutex); > > + return ret; > > +} > > + > > +int bpf_arch_text_invalidate(void *dst, size_t len) > > +{ > > + int i; > > + int ret = 0; > > + u32 *inst; > > + > > + inst = kvmalloc(len, GFP_KERNEL); > > + if (!inst) > > + return -ENOMEM; > > + > > + for (i = 0; i < (len/sizeof(u32)); i++) > > + inst[i] = INSN_BREAK; > > + > > + if (larch_insn_text_copy(dst, inst, len)) > > Do we need text_mutex here and below for larch_insn_text_copy() ? As a matter of fact, my use of text_mutex is modeled after the arm64 code. Arm64 also only adds text_mutex to bpf_arch_text_poke. Therefore, I have only added text_mutex to bpf_arch_text_poke as well. In the next version of the code, I will try to add text_mutex to all contexts where larch_insn_text_copy is used and conduct tests accordingly. > > > + ret = -EINVAL; > > + > > + kvfree(inst); > > + return ret; > > +} > > + > > +void *bpf_arch_text_copy(void *dst, void *src, size_t len) > > +{ > > + if (larch_insn_text_copy(dst, src, len)) > > + return ERR_PTR(-EINVAL); > > + > > + return dst; > > +} > > -- > > 2.25.1 > >
On Mon, Jul 28, 2025 at 09:21:52PM +0800, Chenghao Duan wrote: > On Mon, Jul 28, 2025 at 06:47:03PM +0800, Hengqi Chen wrote: > > On Thu, Jul 24, 2025 at 10:21 PM Chenghao Duan <duanchenghao@kylinos.cn> wrote: > > > > > > Implement the functions of bpf_arch_text_poke, bpf_arch_text_copy, and > > > bpf_arch_text_invalidate on the LoongArch architecture. > > > > > > On LoongArch, since symbol addresses in the direct mapping > > > region cannot be reached via relative jump instructions from the paged > > > mapping region, we use the move_imm+jirl instruction pair as absolute > > > jump instructions. These require 2-5 instructions, so we reserve 5 NOP > > > instructions in the program as placeholders for function jumps. > > > > > > larch_insn_text_copy is solely used for BPF. The use of > > > larch_insn_text_copy() requires page_size alignment. Currently, only > > > the size of the trampoline is page-aligned. > > > > > > Co-developed-by: George Guo <guodongtai@kylinos.cn> > > > Signed-off-by: George Guo <guodongtai@kylinos.cn> > > > Signed-off-by: Chenghao Duan <duanchenghao@kylinos.cn> > > > Reviewed-by: Hengqi Chen <hengqi.chen@gmail.com> > > > Reviewed-by: Huacai Chen <chenhuacai@kernel.org> > > > --- > > > arch/loongarch/include/asm/inst.h | 1 + > > > arch/loongarch/kernel/inst.c | 32 ++++++++++ > > > arch/loongarch/net/bpf_jit.c | 97 +++++++++++++++++++++++++++++++ > > > 3 files changed, 130 insertions(+) > > > > > > diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h > > > index 2ae96a35d..88bb73e46 100644 > > > --- a/arch/loongarch/include/asm/inst.h > > > +++ b/arch/loongarch/include/asm/inst.h > > > @@ -497,6 +497,7 @@ void arch_simulate_insn(union loongarch_instruction insn, struct pt_regs *regs); > > > int larch_insn_read(void *addr, u32 *insnp); > > > int larch_insn_write(void *addr, u32 insn); > > > int larch_insn_patch_text(void *addr, u32 insn); > > > +int larch_insn_text_copy(void *dst, void *src, size_t len); > > > > > > u32 larch_insn_gen_nop(void); > > > u32 larch_insn_gen_b(unsigned long pc, unsigned long dest); > > > diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c > > > index 674e3b322..8d6594968 100644 > > > --- a/arch/loongarch/kernel/inst.c > > > +++ b/arch/loongarch/kernel/inst.c > > > @@ -4,6 +4,7 @@ > > > */ > > > #include <linux/sizes.h> > > > #include <linux/uaccess.h> > > > +#include <linux/set_memory.h> > > > > > > #include <asm/cacheflush.h> > > > #include <asm/inst.h> > > > @@ -218,6 +219,37 @@ int larch_insn_patch_text(void *addr, u32 insn) > > > return ret; > > > } > > > > > > +int larch_insn_text_copy(void *dst, void *src, size_t len) > > > +{ > > > + unsigned long flags; > > > + size_t wlen = 0; > > > + size_t size; > > > + void *ptr; > > > + int ret = 0; > > > + > > > + set_memory_rw((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE); > > > + raw_spin_lock_irqsave(&patch_lock, flags); > > > + while (wlen < len) { > > > + ptr = dst + wlen; > > > + size = min_t(size_t, PAGE_SIZE - offset_in_page(ptr), > > > + len - wlen); > > > + > > > + ret = copy_to_kernel_nofault(ptr, src + wlen, size); > > > + if (ret) { > > > + pr_err("%s: operation failed\n", __func__); > > > + break; > > > + } > > > + wlen += size; > > > + } > > > + raw_spin_unlock_irqrestore(&patch_lock, flags); > > > + set_memory_rox((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE); > > > + > > > + if (!ret) > > > + flush_icache_range((unsigned long)dst, (unsigned long)dst + len); > > > + > > > + return ret; > > > +} > > > + > > > u32 larch_insn_gen_nop(void) > > > { > > > return INSN_NOP; > > > diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c > > > index 7032f11d3..86504e710 100644 > > > --- a/arch/loongarch/net/bpf_jit.c > > > +++ b/arch/loongarch/net/bpf_jit.c > > > @@ -4,8 +4,12 @@ > > > * > > > * Copyright (C) 2022 Loongson Technology Corporation Limited > > > */ > > > +#include <linux/memory.h> > > > #include "bpf_jit.h" > > > > > > +#define LOONGARCH_LONG_JUMP_NINSNS 5 > > > +#define LOONGARCH_LONG_JUMP_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4) > > > + > > > #define REG_TCC LOONGARCH_GPR_A6 > > > #define TCC_SAVED LOONGARCH_GPR_S5 > > > > > > @@ -88,6 +92,7 @@ static u8 tail_call_reg(struct jit_ctx *ctx) > > > */ > > > static void build_prologue(struct jit_ctx *ctx) > > > { > > > + int i; > > > int stack_adjust = 0, store_offset, bpf_stack_adjust; > > > > > > bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16); > > > @@ -98,6 +103,10 @@ static void build_prologue(struct jit_ctx *ctx) > > > stack_adjust = round_up(stack_adjust, 16); > > > stack_adjust += bpf_stack_adjust; > > > > > > + /* Reserve space for the move_imm + jirl instruction */ > > > + for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++) > > > + emit_insn(ctx, nop); > > > + > > > /* > > > * First instruction initializes the tail call count (TCC). > > > * On tail call we skip this instruction, and the TCC is > > > @@ -1367,3 +1376,91 @@ bool bpf_jit_supports_subprog_tailcalls(void) > > > { > > > return true; > > > } > > > + > > > +static int emit_jump_and_link(struct jit_ctx *ctx, u8 rd, u64 target) > > > +{ > > > + if (!target) { > > > + pr_err("bpf_jit: jump target address is error\n"); > > > > is error ? is NULL ? > > What I mean is: This is an illegal target address. > > > > > > + return -EFAULT; > > > + } > > > + > > > + move_imm(ctx, LOONGARCH_GPR_T1, target, false); > > > + emit_insn(ctx, jirl, rd, LOONGARCH_GPR_T1, 0); > > > + > > > + return 0; > > > +} > > > + > > > +static int gen_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call) > > > +{ > > > + struct jit_ctx ctx; > > > + > > > + ctx.idx = 0; > > > + ctx.image = (union loongarch_instruction *)insns; > > > + > > > + if (!target) { > > > + emit_insn((&ctx), nop); > > > + emit_insn((&ctx), nop); > > > + return 0; > > > + } > > > + > > > + return emit_jump_and_link(&ctx, is_call ? LOONGARCH_GPR_T0 : LOONGARCH_GPR_ZERO, > > > + (unsigned long)target); > > > +} > > > + > > > +int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, > > > + void *old_addr, void *new_addr) > > > +{ > > > + u32 old_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; > > > + u32 new_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; > > > + bool is_call = poke_type == BPF_MOD_CALL; > > > + int ret; > > > + > > > + if (!is_kernel_text((unsigned long)ip) && > > > + !is_bpf_text_address((unsigned long)ip)) > > > + return -ENOTSUPP; > > > + > > > + ret = gen_jump_or_nops(old_addr, ip, old_insns, is_call); > > > + if (ret) > > > + return ret; > > > + > > > + if (memcmp(ip, old_insns, LOONGARCH_LONG_JUMP_NBYTES)) > > > + return -EFAULT; > > > + > > > + ret = gen_jump_or_nops(new_addr, ip, new_insns, is_call); > > > + if (ret) > > > + return ret; > > > + > > > + mutex_lock(&text_mutex); > > > + if (memcmp(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES)) > > > + ret = larch_insn_text_copy(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES); > > > + mutex_unlock(&text_mutex); > > > + return ret; > > > +} > > > + > > > +int bpf_arch_text_invalidate(void *dst, size_t len) > > > +{ > > > + int i; > > > + int ret = 0; > > > + u32 *inst; > > > + > > > + inst = kvmalloc(len, GFP_KERNEL); > > > + if (!inst) > > > + return -ENOMEM; > > > + > > > + for (i = 0; i < (len/sizeof(u32)); i++) > > > + inst[i] = INSN_BREAK; > > > + > > > + if (larch_insn_text_copy(dst, inst, len)) > > > > Do we need text_mutex here and below for larch_insn_text_copy() ? > > As a matter of fact, my use of text_mutex is modeled after the arm64 > code. Arm64 also only adds text_mutex to bpf_arch_text_poke. Therefore, > I have only added text_mutex to bpf_arch_text_poke as well. > > In the next version of the code, I will try to add text_mutex to all > contexts where larch_insn_text_copy is used and conduct tests accordingly. Adding text_mutex tests in all contexts of larch_insn_text_copy passed. > > > > > + ret = -EINVAL; > > > + > > > + kvfree(inst); > > > + return ret; > > > +} > > > + > > > +void *bpf_arch_text_copy(void *dst, void *src, size_t len) > > > +{ > > > + if (larch_insn_text_copy(dst, src, len)) > > > + return ERR_PTR(-EINVAL); > > > + > > > + return dst; > > > +} > > > -- > > > 2.25.1 > > >
Hi, Chenghao, On Thu, Jul 24, 2025 at 10:21 PM Chenghao Duan <duanchenghao@kylinos.cn> wrote: > > Implement the functions of bpf_arch_text_poke, bpf_arch_text_copy, and > bpf_arch_text_invalidate on the LoongArch architecture. > > On LoongArch, since symbol addresses in the direct mapping > region cannot be reached via relative jump instructions from the paged > mapping region, we use the move_imm+jirl instruction pair as absolute > jump instructions. These require 2-5 instructions, so we reserve 5 NOP > instructions in the program as placeholders for function jumps. > > larch_insn_text_copy is solely used for BPF. The use of > larch_insn_text_copy() requires page_size alignment. Currently, only > the size of the trampoline is page-aligned. > > Co-developed-by: George Guo <guodongtai@kylinos.cn> > Signed-off-by: George Guo <guodongtai@kylinos.cn> > Signed-off-by: Chenghao Duan <duanchenghao@kylinos.cn> > Reviewed-by: Hengqi Chen <hengqi.chen@gmail.com> > Reviewed-by: Huacai Chen <chenhuacai@kernel.org> > --- > arch/loongarch/include/asm/inst.h | 1 + > arch/loongarch/kernel/inst.c | 32 ++++++++++ > arch/loongarch/net/bpf_jit.c | 97 +++++++++++++++++++++++++++++++ > 3 files changed, 130 insertions(+) > > diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h > index 2ae96a35d..88bb73e46 100644 > --- a/arch/loongarch/include/asm/inst.h > +++ b/arch/loongarch/include/asm/inst.h > @@ -497,6 +497,7 @@ void arch_simulate_insn(union loongarch_instruction insn, struct pt_regs *regs); > int larch_insn_read(void *addr, u32 *insnp); > int larch_insn_write(void *addr, u32 insn); > int larch_insn_patch_text(void *addr, u32 insn); > +int larch_insn_text_copy(void *dst, void *src, size_t len); > > u32 larch_insn_gen_nop(void); > u32 larch_insn_gen_b(unsigned long pc, unsigned long dest); > diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c > index 674e3b322..8d6594968 100644 > --- a/arch/loongarch/kernel/inst.c > +++ b/arch/loongarch/kernel/inst.c > @@ -4,6 +4,7 @@ > */ > #include <linux/sizes.h> > #include <linux/uaccess.h> > +#include <linux/set_memory.h> > > #include <asm/cacheflush.h> > #include <asm/inst.h> > @@ -218,6 +219,37 @@ int larch_insn_patch_text(void *addr, u32 insn) > return ret; > } > > +int larch_insn_text_copy(void *dst, void *src, size_t len) > +{ > + unsigned long flags; > + size_t wlen = 0; > + size_t size; > + void *ptr; > + int ret = 0; > + > + set_memory_rw((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE); > + raw_spin_lock_irqsave(&patch_lock, flags); > + while (wlen < len) { > + ptr = dst + wlen; > + size = min_t(size_t, PAGE_SIZE - offset_in_page(ptr), > + len - wlen); > + > + ret = copy_to_kernel_nofault(ptr, src + wlen, size); > + if (ret) { > + pr_err("%s: operation failed\n", __func__); > + break; > + } > + wlen += size; > + } I have an off-list discussion with Hengqi, and I know why he has questions on this. He said we don't need a loop, we can just copy the whole thing in a single operation, and I think he is right. RISC-V uses a loop because it uses fixmap, and with fixmap they can only handle a single page every time. We use set_memory_{rw, rox} so we have no limitation. > + raw_spin_unlock_irqrestore(&patch_lock, flags); > + set_memory_rox((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE); > + > + if (!ret) > + flush_icache_range((unsigned long)dst, (unsigned long)dst + len); > + > + return ret; > +} To save time, please test this method: +int larch_insn_text_copy(void *dst, void *src, size_t len) +{ + int ret = 0; + unsigned long flags; + + WARN_ON(!PAGE_ALIGNED(src)); //maybe this is unneeded + WARN_ON(!PAGE_ALIGNED(dst)); + WARN_ON(!PAGE_ALIGNED(size)); + + set_memory_rw((unsigned long)dst, len / PAGE_SIZE); + raw_spin_lock_irqsave(&patch_lock, flags); + + ret = copy_to_kernel_nofault(dst, src, len); + if (ret) + pr_err("%s: operation failed\n", __func__); + + raw_spin_unlock_irqrestore(&patch_lock, flags); + set_memory_rox((unsigned long)dst, len / PAGE_SIZE); + + if (!ret) + flush_icache_range((unsigned long)dst, (unsigned long)dst + len); + + return ret; +} Huacai > + > u32 larch_insn_gen_nop(void) > { > return INSN_NOP; > diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c > index 7032f11d3..86504e710 100644 > --- a/arch/loongarch/net/bpf_jit.c > +++ b/arch/loongarch/net/bpf_jit.c > @@ -4,8 +4,12 @@ > * > * Copyright (C) 2022 Loongson Technology Corporation Limited > */ > +#include <linux/memory.h> > #include "bpf_jit.h" > > +#define LOONGARCH_LONG_JUMP_NINSNS 5 > +#define LOONGARCH_LONG_JUMP_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4) > + > #define REG_TCC LOONGARCH_GPR_A6 > #define TCC_SAVED LOONGARCH_GPR_S5 > > @@ -88,6 +92,7 @@ static u8 tail_call_reg(struct jit_ctx *ctx) > */ > static void build_prologue(struct jit_ctx *ctx) > { > + int i; > int stack_adjust = 0, store_offset, bpf_stack_adjust; > > bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16); > @@ -98,6 +103,10 @@ static void build_prologue(struct jit_ctx *ctx) > stack_adjust = round_up(stack_adjust, 16); > stack_adjust += bpf_stack_adjust; > > + /* Reserve space for the move_imm + jirl instruction */ > + for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++) > + emit_insn(ctx, nop); > + > /* > * First instruction initializes the tail call count (TCC). > * On tail call we skip this instruction, and the TCC is > @@ -1367,3 +1376,91 @@ bool bpf_jit_supports_subprog_tailcalls(void) > { > return true; > } > + > +static int emit_jump_and_link(struct jit_ctx *ctx, u8 rd, u64 target) > +{ > + if (!target) { > + pr_err("bpf_jit: jump target address is error\n"); > + return -EFAULT; > + } > + > + move_imm(ctx, LOONGARCH_GPR_T1, target, false); > + emit_insn(ctx, jirl, rd, LOONGARCH_GPR_T1, 0); > + > + return 0; > +} > + > +static int gen_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call) > +{ > + struct jit_ctx ctx; > + > + ctx.idx = 0; > + ctx.image = (union loongarch_instruction *)insns; > + > + if (!target) { > + emit_insn((&ctx), nop); > + emit_insn((&ctx), nop); > + return 0; > + } > + > + return emit_jump_and_link(&ctx, is_call ? LOONGARCH_GPR_T0 : LOONGARCH_GPR_ZERO, > + (unsigned long)target); > +} > + > +int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, > + void *old_addr, void *new_addr) > +{ > + u32 old_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; > + u32 new_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP}; > + bool is_call = poke_type == BPF_MOD_CALL; > + int ret; > + > + if (!is_kernel_text((unsigned long)ip) && > + !is_bpf_text_address((unsigned long)ip)) > + return -ENOTSUPP; > + > + ret = gen_jump_or_nops(old_addr, ip, old_insns, is_call); > + if (ret) > + return ret; > + > + if (memcmp(ip, old_insns, LOONGARCH_LONG_JUMP_NBYTES)) > + return -EFAULT; > + > + ret = gen_jump_or_nops(new_addr, ip, new_insns, is_call); > + if (ret) > + return ret; > + > + mutex_lock(&text_mutex); > + if (memcmp(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES)) > + ret = larch_insn_text_copy(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES); > + mutex_unlock(&text_mutex); > + return ret; > +} > + > +int bpf_arch_text_invalidate(void *dst, size_t len) > +{ > + int i; > + int ret = 0; > + u32 *inst; > + > + inst = kvmalloc(len, GFP_KERNEL); > + if (!inst) > + return -ENOMEM; > + > + for (i = 0; i < (len/sizeof(u32)); i++) > + inst[i] = INSN_BREAK; > + > + if (larch_insn_text_copy(dst, inst, len)) > + ret = -EINVAL; > + > + kvfree(inst); > + return ret; > +} > + > +void *bpf_arch_text_copy(void *dst, void *src, size_t len) > +{ > + if (larch_insn_text_copy(dst, src, len)) > + return ERR_PTR(-EINVAL); > + > + return dst; > +} > -- > 2.25.1 >
© 2016 - 2025 Red Hat, Inc.