diff options
Diffstat (limited to 'arch/arm/net/bpf_jit_32.c')
-rw-r--r-- | arch/arm/net/bpf_jit_32.c | 38 |
1 files changed, 32 insertions, 6 deletions
diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c index 0df5fd561513..6be415111eec 100644 --- a/arch/arm/net/bpf_jit_32.c +++ b/arch/arm/net/bpf_jit_32.c @@ -125,7 +125,7 @@ static u64 jit_get_skb_w(struct sk_buff *skb, int offset) } /* - * Wrapper that handles both OABI and EABI and assures Thumb2 interworking + * Wrappers which handle both OABI and EABI and assures Thumb2 interworking * (where the assembly routines like __aeabi_uidiv could cause problems). */ static u32 jit_udiv(u32 dividend, u32 divisor) @@ -133,6 +133,11 @@ static u32 jit_udiv(u32 dividend, u32 divisor) return dividend / divisor; } +static u32 jit_mod(u32 dividend, u32 divisor) +{ + return dividend % divisor; +} + static inline void _emit(int cond, u32 inst, struct jit_ctx *ctx) { inst |= (cond << 28); @@ -471,11 +476,17 @@ static inline void emit_blx_r(u8 tgt_reg, struct jit_ctx *ctx) #endif } -static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx) +static inline void emit_udivmod(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx, + int bpf_op) { #if __LINUX_ARM_ARCH__ == 7 if (elf_hwcap & HWCAP_IDIVA) { - emit(ARM_UDIV(rd, rm, rn), ctx); + if (bpf_op == BPF_DIV) + emit(ARM_UDIV(rd, rm, rn), ctx); + else { + emit(ARM_UDIV(ARM_R3, rm, rn), ctx); + emit(ARM_MLS(rd, rn, ARM_R3, rm), ctx); + } return; } #endif @@ -496,7 +507,8 @@ static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx) emit(ARM_MOV_R(ARM_R0, rm), ctx); ctx->seen |= SEEN_CALL; - emit_mov_i(ARM_R3, (u32)jit_udiv, ctx); + emit_mov_i(ARM_R3, bpf_op == BPF_DIV ? (u32)jit_udiv : (u32)jit_mod, + ctx); emit_blx_r(ARM_R3, ctx); if (rd != ARM_R0) @@ -697,13 +709,27 @@ load_ind: if (k == 1) break; emit_mov_i(r_scratch, k, ctx); - emit_udiv(r_A, r_A, r_scratch, ctx); + emit_udivmod(r_A, r_A, r_scratch, ctx, BPF_DIV); break; case BPF_ALU | BPF_DIV | BPF_X: update_on_xread(ctx); emit(ARM_CMP_I(r_X, 0), ctx); emit_err_ret(ARM_COND_EQ, ctx); - emit_udiv(r_A, r_A, r_X, ctx); + emit_udivmod(r_A, r_A, r_X, ctx, BPF_DIV); + break; + case BPF_ALU | BPF_MOD | BPF_K: + if (k == 1) { + emit_mov_i(r_A, 0, ctx); + break; + } + emit_mov_i(r_scratch, k, ctx); + emit_udivmod(r_A, r_A, r_scratch, ctx, BPF_MOD); + break; + case BPF_ALU | BPF_MOD | BPF_X: + update_on_xread(ctx); + emit(ARM_CMP_I(r_X, 0), ctx); + emit_err_ret(ARM_COND_EQ, ctx); + emit_udivmod(r_A, r_A, r_X, ctx, BPF_MOD); break; case BPF_ALU | BPF_OR | BPF_K: /* A |= K */ |