|
2 | 2 | /*
|
3 | 3 | * Just-In-Time compiler for eBPF filters on 32bit ARM
|
4 | 4 | *
|
| 5 | + * Copyright (c) 2023 Puranjay Mohan <puranjay12@gmail.com> |
5 | 6 | * Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com>
|
6 | 7 | * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com>
|
7 | 8 | */
|
|
15 | 16 | #include <linux/string.h>
|
16 | 17 | #include <linux/slab.h>
|
17 | 18 | #include <linux/if_vlan.h>
|
| 19 | +#include <linux/math64.h> |
18 | 20 |
|
19 | 21 | #include <asm/cacheflush.h>
|
20 | 22 | #include <asm/hwcap.h>
|
@@ -238,6 +240,34 @@ static s32 jit_smod32(s32 dividend, s32 divisor)
|
238 | 240 | return dividend % divisor;
|
239 | 241 | }
|
240 | 242 |
|
| 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 | + |
241 | 271 | static inline void _emit(int cond, u32 inst, struct jit_ctx *ctx)
|
242 | 272 | {
|
243 | 273 | inst |= (cond << 28);
|
@@ -555,6 +585,78 @@ static inline void emit_udivmod(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx, u8 op,
|
555 | 585 | emit(ARM_MOV_R(ARM_R0, tmp[1]), ctx);
|
556 | 586 | }
|
557 | 587 |
|
| 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 | + |
558 | 660 | /* Is the translated BPF register on stack? */
|
559 | 661 | static bool is_stacked(s8 reg)
|
560 | 662 | {
|
@@ -1582,7 +1684,19 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
|
1582 | 1684 | case BPF_ALU64 | BPF_DIV | BPF_X:
|
1583 | 1685 | case BPF_ALU64 | BPF_MOD | BPF_K:
|
1584 | 1686 | 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; |
1586 | 1700 | /* dst = dst << imm */
|
1587 | 1701 | /* dst = dst >> imm */
|
1588 | 1702 | /* dst = dst >> imm (signed) */
|
|
0 commit comments