Skip to content

Commit 7108604

Browse files
puranjaymohanAlexei Starovoitov
authored and
Alexei Starovoitov
committed
arm32, bpf: add support for 64 bit division instruction
ARM32 doesn't have instructions to do 64-bit/64-bit divisions. So, to implement the following instructions: BPF_ALU64 | BPF_DIV BPF_ALU64 | BPF_MOD BPF_ALU64 | BPF_SDIV BPF_ALU64 | BPF_SMOD We implement the above instructions by doing function calls to div64_u64() and div64_u64_rem() for unsigned division/mod and calls to div64_s64() for signed division/mod. Signed-off-by: Puranjay Mohan <puranjay12@gmail.com> Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> Link: https://lore.kernel.org/r/20230907230550.1417590-7-puranjay12@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 5097faa commit 7108604

File tree

1 file changed

+115
-1
lines changed

1 file changed

+115
-1
lines changed

arch/arm/net/bpf_jit_32.c

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/*
33
* Just-In-Time compiler for eBPF filters on 32bit ARM
44
*
5+
* Copyright (c) 2023 Puranjay Mohan <puranjay12@gmail.com>
56
* Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com>
67
* Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com>
78
*/
@@ -15,6 +16,7 @@
1516
#include <linux/string.h>
1617
#include <linux/slab.h>
1718
#include <linux/if_vlan.h>
19+
#include <linux/math64.h>
1820

1921
#include <asm/cacheflush.h>
2022
#include <asm/hwcap.h>
@@ -238,6 +240,34 @@ static s32 jit_smod32(s32 dividend, s32 divisor)
238240
return dividend % divisor;
239241
}
240242

243+
/* Wrappers for 64-bit div/mod */
244+
static u64 jit_udiv64(u64 dividend, u64 divisor)
245+
{
246+
return div64_u64(dividend, divisor);
247+
}
248+
249+
static u64 jit_mod64(u64 dividend, u64 divisor)
250+
{
251+
u64 rem;
252+
253+
div64_u64_rem(dividend, divisor, &rem);
254+
return rem;
255+
}
256+
257+
static s64 jit_sdiv64(s64 dividend, s64 divisor)
258+
{
259+
return div64_s64(dividend, divisor);
260+
}
261+
262+
static s64 jit_smod64(s64 dividend, s64 divisor)
263+
{
264+
u64 q;
265+
266+
q = div64_s64(dividend, divisor);
267+
268+
return dividend - q * divisor;
269+
}
270+
241271
static inline void _emit(int cond, u32 inst, struct jit_ctx *ctx)
242272
{
243273
inst |= (cond << 28);
@@ -555,6 +585,78 @@ static inline void emit_udivmod(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx, u8 op,
555585
emit(ARM_MOV_R(ARM_R0, tmp[1]), ctx);
556586
}
557587

588+
static inline void emit_udivmod64(const s8 *rd, const s8 *rm, const s8 *rn, struct jit_ctx *ctx,
589+
u8 op, u8 sign)
590+
{
591+
u32 dst;
592+
593+
/* Push caller-saved registers on stack */
594+
emit(ARM_PUSH(CALLER_MASK), ctx);
595+
596+
/*
597+
* As we are implementing 64-bit div/mod as function calls, We need to put the dividend in
598+
* R0-R1 and the divisor in R2-R3. As we have already pushed these registers on the stack,
599+
* we can recover them later after returning from the function call.
600+
*/
601+
if (rm[1] != ARM_R0 || rn[1] != ARM_R2) {
602+
/*
603+
* Move Rm to {R1, R0} if it is not already there.
604+
*/
605+
if (rm[1] != ARM_R0) {
606+
if (rn[1] == ARM_R0)
607+
emit(ARM_PUSH(BIT(ARM_R0) | BIT(ARM_R1)), ctx);
608+
emit(ARM_MOV_R(ARM_R1, rm[0]), ctx);
609+
emit(ARM_MOV_R(ARM_R0, rm[1]), ctx);
610+
if (rn[1] == ARM_R0) {
611+
emit(ARM_POP(BIT(ARM_R2) | BIT(ARM_R3)), ctx);
612+
goto cont;
613+
}
614+
}
615+
/*
616+
* Move Rn to {R3, R2} if it is not already there.
617+
*/
618+
if (rn[1] != ARM_R2) {
619+
emit(ARM_MOV_R(ARM_R3, rn[0]), ctx);
620+
emit(ARM_MOV_R(ARM_R2, rn[1]), ctx);
621+
}
622+
}
623+
624+
cont:
625+
626+
/* Call appropriate function */
627+
if (sign) {
628+
if (op == BPF_DIV)
629+
dst = (u32)jit_sdiv64;
630+
else
631+
dst = (u32)jit_smod64;
632+
} else {
633+
if (op == BPF_DIV)
634+
dst = (u32)jit_udiv64;
635+
else
636+
dst = (u32)jit_mod64;
637+
}
638+
639+
emit_mov_i(ARM_IP, dst, ctx);
640+
emit_blx_r(ARM_IP, ctx);
641+
642+
/* Save return value */
643+
if (rd[1] != ARM_R0) {
644+
emit(ARM_MOV_R(rd[0], ARM_R1), ctx);
645+
emit(ARM_MOV_R(rd[1], ARM_R0), ctx);
646+
}
647+
648+
/* Recover {R3, R2} and {R1, R0} from stack if they are not Rd */
649+
if (rd[1] != ARM_R0 && rd[1] != ARM_R2) {
650+
emit(ARM_POP(CALLER_MASK), ctx);
651+
} else if (rd[1] != ARM_R0) {
652+
emit(ARM_POP(BIT(ARM_R0) | BIT(ARM_R1)), ctx);
653+
emit(ARM_ADD_I(ARM_SP, ARM_SP, 8), ctx);
654+
} else {
655+
emit(ARM_ADD_I(ARM_SP, ARM_SP, 8), ctx);
656+
emit(ARM_POP(BIT(ARM_R2) | BIT(ARM_R3)), ctx);
657+
}
658+
}
659+
558660
/* Is the translated BPF register on stack? */
559661
static bool is_stacked(s8 reg)
560662
{
@@ -1582,7 +1684,19 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
15821684
case BPF_ALU64 | BPF_DIV | BPF_X:
15831685
case BPF_ALU64 | BPF_MOD | BPF_K:
15841686
case BPF_ALU64 | BPF_MOD | BPF_X:
1585-
goto notyet;
1687+
rd = arm_bpf_get_reg64(dst, tmp2, ctx);
1688+
switch (BPF_SRC(code)) {
1689+
case BPF_X:
1690+
rs = arm_bpf_get_reg64(src, tmp, ctx);
1691+
break;
1692+
case BPF_K:
1693+
rs = tmp;
1694+
emit_a32_mov_se_i64(is64, rs, imm, ctx);
1695+
break;
1696+
}
1697+
emit_udivmod64(rd, rd, rs, ctx, BPF_OP(code), off);
1698+
arm_bpf_put_reg64(dst, rd, ctx);
1699+
break;
15861700
/* dst = dst << imm */
15871701
/* dst = dst >> imm */
15881702
/* dst = dst >> imm (signed) */

0 commit comments

Comments
 (0)