Skip to content

Commit a7227e3

Browse files
Chenghao Duanintel-lab-lkp
authored andcommitted
LoongArch: BPF: Add bpf_arch_xxxxx support for Loongarch
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 <[email protected]> Signed-off-by: George Guo <[email protected]> Signed-off-by: Chenghao Duan <[email protected]> Reviewed-by: Hengqi Chen <[email protected]> Reviewed-by: Huacai Chen <[email protected]>
1 parent 24d9a8e commit a7227e3

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-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: 32 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,37 @@ 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+
unsigned long flags;
225+
size_t wlen = 0;
226+
size_t size;
227+
void *ptr;
228+
int ret = 0;
229+
230+
set_memory_rw((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE);
231+
raw_spin_lock_irqsave(&patch_lock, flags);
232+
while (wlen < len) {
233+
ptr = dst + wlen;
234+
size = min_t(size_t, PAGE_SIZE - offset_in_page(ptr),
235+
len - wlen);
236+
237+
ret = copy_to_kernel_nofault(ptr, src + wlen, size);
238+
if (ret) {
239+
pr_err("%s: operation failed\n", __func__);
240+
break;
241+
}
242+
wlen += size;
243+
}
244+
raw_spin_unlock_irqrestore(&patch_lock, flags);
245+
set_memory_rox((unsigned long)dst, round_up(len, PAGE_SIZE) / PAGE_SIZE);
246+
247+
if (!ret)
248+
flush_icache_range((unsigned long)dst, (unsigned long)dst + len);
249+
250+
return ret;
251+
}
252+
221253
u32 larch_insn_gen_nop(void)
222254
{
223255
return INSN_NOP;

arch/loongarch/net/bpf_jit.c

Lines changed: 97 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,91 @@ 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+
if (larch_insn_text_copy(dst, inst, len))
1454+
ret = -EINVAL;
1455+
1456+
kvfree(inst);
1457+
return ret;
1458+
}
1459+
1460+
void *bpf_arch_text_copy(void *dst, void *src, size_t len)
1461+
{
1462+
if (larch_insn_text_copy(dst, src, len))
1463+
return ERR_PTR(-EINVAL);
1464+
1465+
return dst;
1466+
}

0 commit comments

Comments
 (0)