Skip to content

Commit 4e81abb

Browse files
Chenghao DuanKernel Patches Daemon
authored andcommitted
LoongArch: BPF: Implement dynamic code modification support
This commit adds support for BPF dynamic code modification on the 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. 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 <[email protected]> Signed-off-by: George Guo <[email protected]> Signed-off-by: Chenghao Duan <[email protected]>
1 parent 18d5b31 commit 4e81abb

File tree

3 files changed

+132
-0
lines changed

3 files changed

+132
-0
lines changed

arch/loongarch/include/asm/inst.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ void arch_simulate_insn(union loongarch_instruction insn, struct pt_regs *regs);
497497
int larch_insn_read(void *addr, u32 *insnp);
498498
int larch_insn_write(void *addr, u32 insn);
499499
int larch_insn_patch_text(void *addr, u32 insn);
500+
int larch_insn_text_copy(void *dst, void *src, size_t len);
500501

501502
u32 larch_insn_gen_nop(void);
502503
u32 larch_insn_gen_b(unsigned long pc, unsigned long dest);

arch/loongarch/kernel/inst.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
#include <linux/sizes.h>
66
#include <linux/uaccess.h>
7+
#include <linux/set_memory.h>
78

89
#include <asm/cacheflush.h>
910
#include <asm/inst.h>
@@ -218,6 +219,32 @@ int larch_insn_patch_text(void *addr, u32 insn)
218219
return ret;
219220
}
220221

222+
int larch_insn_text_copy(void *dst, void *src, size_t len)
223+
{
224+
int ret;
225+
unsigned long flags;
226+
unsigned long dst_start, dst_end, dst_len;
227+
228+
dst_start = round_down((unsigned long)dst, PAGE_SIZE);
229+
dst_end = round_up((unsigned long)dst + len, PAGE_SIZE);
230+
dst_len = dst_end - dst_start;
231+
232+
set_memory_rw(dst_start, dst_len / PAGE_SIZE);
233+
raw_spin_lock_irqsave(&patch_lock, flags);
234+
235+
ret = copy_to_kernel_nofault(dst, src, len);
236+
if (ret)
237+
pr_err("%s: operation failed\n", __func__);
238+
239+
raw_spin_unlock_irqrestore(&patch_lock, flags);
240+
set_memory_rox(dst_start, dst_len / PAGE_SIZE);
241+
242+
if (!ret)
243+
flush_icache_range((unsigned long)dst, (unsigned long)dst + len);
244+
245+
return ret;
246+
}
247+
221248
u32 larch_insn_gen_nop(void)
222249
{
223250
return INSN_NOP;

arch/loongarch/net/bpf_jit.c

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
*
55
* Copyright (C) 2022 Loongson Technology Corporation Limited
66
*/
7+
#include <linux/memory.h>
78
#include "bpf_jit.h"
89

10+
#define LOONGARCH_LONG_JUMP_NINSNS 5
11+
#define LOONGARCH_LONG_JUMP_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4)
12+
913
#define REG_TCC LOONGARCH_GPR_A6
1014
#define TCC_SAVED LOONGARCH_GPR_S5
1115

@@ -88,6 +92,7 @@ static u8 tail_call_reg(struct jit_ctx *ctx)
8892
*/
8993
static void build_prologue(struct jit_ctx *ctx)
9094
{
95+
int i;
9196
int stack_adjust = 0, store_offset, bpf_stack_adjust;
9297

9398
bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16);
@@ -98,6 +103,10 @@ static void build_prologue(struct jit_ctx *ctx)
98103
stack_adjust = round_up(stack_adjust, 16);
99104
stack_adjust += bpf_stack_adjust;
100105

106+
/* Reserve space for the move_imm + jirl instruction */
107+
for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++)
108+
emit_insn(ctx, nop);
109+
101110
/*
102111
* First instruction initializes the tail call count (TCC).
103112
* On tail call we skip this instruction, and the TCC is
@@ -1367,3 +1376,98 @@ bool bpf_jit_supports_subprog_tailcalls(void)
13671376
{
13681377
return true;
13691378
}
1379+
1380+
static int emit_jump_and_link(struct jit_ctx *ctx, u8 rd, u64 target)
1381+
{
1382+
if (!target) {
1383+
pr_err("bpf_jit: jump target address is error\n");
1384+
return -EFAULT;
1385+
}
1386+
1387+
move_imm(ctx, LOONGARCH_GPR_T1, target, false);
1388+
emit_insn(ctx, jirl, rd, LOONGARCH_GPR_T1, 0);
1389+
1390+
return 0;
1391+
}
1392+
1393+
static int gen_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call)
1394+
{
1395+
struct jit_ctx ctx;
1396+
1397+
ctx.idx = 0;
1398+
ctx.image = (union loongarch_instruction *)insns;
1399+
1400+
if (!target) {
1401+
emit_insn((&ctx), nop);
1402+
emit_insn((&ctx), nop);
1403+
return 0;
1404+
}
1405+
1406+
return emit_jump_and_link(&ctx, is_call ? LOONGARCH_GPR_T0 : LOONGARCH_GPR_ZERO,
1407+
(unsigned long)target);
1408+
}
1409+
1410+
int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
1411+
void *old_addr, void *new_addr)
1412+
{
1413+
u32 old_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP};
1414+
u32 new_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP};
1415+
bool is_call = poke_type == BPF_MOD_CALL;
1416+
int ret;
1417+
1418+
if (!is_kernel_text((unsigned long)ip) &&
1419+
!is_bpf_text_address((unsigned long)ip))
1420+
return -ENOTSUPP;
1421+
1422+
ret = gen_jump_or_nops(old_addr, ip, old_insns, is_call);
1423+
if (ret)
1424+
return ret;
1425+
1426+
if (memcmp(ip, old_insns, LOONGARCH_LONG_JUMP_NBYTES))
1427+
return -EFAULT;
1428+
1429+
ret = gen_jump_or_nops(new_addr, ip, new_insns, is_call);
1430+
if (ret)
1431+
return ret;
1432+
1433+
mutex_lock(&text_mutex);
1434+
if (memcmp(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES))
1435+
ret = larch_insn_text_copy(ip, new_insns, LOONGARCH_LONG_JUMP_NBYTES);
1436+
mutex_unlock(&text_mutex);
1437+
return ret;
1438+
}
1439+
1440+
int bpf_arch_text_invalidate(void *dst, size_t len)
1441+
{
1442+
int i;
1443+
int ret = 0;
1444+
u32 *inst;
1445+
1446+
inst = kvmalloc(len, GFP_KERNEL);
1447+
if (!inst)
1448+
return -ENOMEM;
1449+
1450+
for (i = 0; i < (len/sizeof(u32)); i++)
1451+
inst[i] = INSN_BREAK;
1452+
1453+
mutex_lock(&text_mutex);
1454+
if (larch_insn_text_copy(dst, inst, len))
1455+
ret = -EINVAL;
1456+
mutex_unlock(&text_mutex);
1457+
1458+
kvfree(inst);
1459+
return ret;
1460+
}
1461+
1462+
void *bpf_arch_text_copy(void *dst, void *src, size_t len)
1463+
{
1464+
int ret;
1465+
1466+
mutex_lock(&text_mutex);
1467+
ret = larch_insn_text_copy(dst, src, len);
1468+
mutex_unlock(&text_mutex);
1469+
if (ret)
1470+
return ERR_PTR(-EINVAL);
1471+
1472+
return dst;
1473+
}

0 commit comments

Comments
 (0)