diff options
Diffstat (limited to 'arch/powerpc/net/bpf_jit_comp64.c')
-rw-r--r-- | arch/powerpc/net/bpf_jit_comp64.c | 118 |
1 files changed, 88 insertions, 30 deletions
diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c index 7703dcf48be8..2cbcdf93cc19 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c @@ -510,20 +510,33 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code case BPF_ALU | BPF_DIV | BPF_X: /* (u32) dst /= (u32) src */ case BPF_ALU | BPF_MOD | BPF_X: /* (u32) dst %= (u32) src */ if (BPF_OP(code) == BPF_MOD) { - EMIT(PPC_RAW_DIVWU(tmp1_reg, dst_reg, src_reg)); + if (off) + EMIT(PPC_RAW_DIVW(tmp1_reg, dst_reg, src_reg)); + else + EMIT(PPC_RAW_DIVWU(tmp1_reg, dst_reg, src_reg)); + EMIT(PPC_RAW_MULW(tmp1_reg, src_reg, tmp1_reg)); EMIT(PPC_RAW_SUB(dst_reg, dst_reg, tmp1_reg)); } else - EMIT(PPC_RAW_DIVWU(dst_reg, dst_reg, src_reg)); + if (off) + EMIT(PPC_RAW_DIVW(dst_reg, dst_reg, src_reg)); + else + EMIT(PPC_RAW_DIVWU(dst_reg, dst_reg, src_reg)); goto bpf_alu32_trunc; case BPF_ALU64 | BPF_DIV | BPF_X: /* dst /= src */ case BPF_ALU64 | BPF_MOD | BPF_X: /* dst %= src */ if (BPF_OP(code) == BPF_MOD) { - EMIT(PPC_RAW_DIVDU(tmp1_reg, dst_reg, src_reg)); + if (off) + EMIT(PPC_RAW_DIVD(tmp1_reg, dst_reg, src_reg)); + else + EMIT(PPC_RAW_DIVDU(tmp1_reg, dst_reg, src_reg)); EMIT(PPC_RAW_MULD(tmp1_reg, src_reg, tmp1_reg)); EMIT(PPC_RAW_SUB(dst_reg, dst_reg, tmp1_reg)); } else - EMIT(PPC_RAW_DIVDU(dst_reg, dst_reg, src_reg)); + if (off) + EMIT(PPC_RAW_DIVD(dst_reg, dst_reg, src_reg)); + else + EMIT(PPC_RAW_DIVDU(dst_reg, dst_reg, src_reg)); break; case BPF_ALU | BPF_MOD | BPF_K: /* (u32) dst %= (u32) imm */ case BPF_ALU | BPF_DIV | BPF_K: /* (u32) dst /= (u32) imm */ @@ -544,19 +557,31 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code switch (BPF_CLASS(code)) { case BPF_ALU: if (BPF_OP(code) == BPF_MOD) { - EMIT(PPC_RAW_DIVWU(tmp2_reg, dst_reg, tmp1_reg)); + if (off) + EMIT(PPC_RAW_DIVW(tmp2_reg, dst_reg, tmp1_reg)); + else + EMIT(PPC_RAW_DIVWU(tmp2_reg, dst_reg, tmp1_reg)); EMIT(PPC_RAW_MULW(tmp1_reg, tmp1_reg, tmp2_reg)); EMIT(PPC_RAW_SUB(dst_reg, dst_reg, tmp1_reg)); } else - EMIT(PPC_RAW_DIVWU(dst_reg, dst_reg, tmp1_reg)); + if (off) + EMIT(PPC_RAW_DIVW(dst_reg, dst_reg, tmp1_reg)); + else + EMIT(PPC_RAW_DIVWU(dst_reg, dst_reg, tmp1_reg)); break; case BPF_ALU64: if (BPF_OP(code) == BPF_MOD) { - EMIT(PPC_RAW_DIVDU(tmp2_reg, dst_reg, tmp1_reg)); + if (off) + EMIT(PPC_RAW_DIVD(tmp2_reg, dst_reg, tmp1_reg)); + else + EMIT(PPC_RAW_DIVDU(tmp2_reg, dst_reg, tmp1_reg)); EMIT(PPC_RAW_MULD(tmp1_reg, tmp1_reg, tmp2_reg)); EMIT(PPC_RAW_SUB(dst_reg, dst_reg, tmp1_reg)); } else - EMIT(PPC_RAW_DIVDU(dst_reg, dst_reg, tmp1_reg)); + if (off) + EMIT(PPC_RAW_DIVD(dst_reg, dst_reg, tmp1_reg)); + else + EMIT(PPC_RAW_DIVDU(dst_reg, dst_reg, tmp1_reg)); break; } goto bpf_alu32_trunc; @@ -676,8 +701,14 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code /* special mov32 for zext */ EMIT(PPC_RAW_RLWINM(dst_reg, dst_reg, 0, 0, 31)); break; - } - EMIT(PPC_RAW_MR(dst_reg, src_reg)); + } else if (off == 8) { + EMIT(PPC_RAW_EXTSB(dst_reg, src_reg)); + } else if (off == 16) { + EMIT(PPC_RAW_EXTSH(dst_reg, src_reg)); + } else if (off == 32) { + EMIT(PPC_RAW_EXTSW(dst_reg, src_reg)); + } else if (dst_reg != src_reg) + EMIT(PPC_RAW_MR(dst_reg, src_reg)); goto bpf_alu32_trunc; case BPF_ALU | BPF_MOV | BPF_K: /* (u32) dst = imm */ case BPF_ALU64 | BPF_MOV | BPF_K: /* dst = (s64) imm */ @@ -699,11 +730,12 @@ bpf_alu32_trunc: */ case BPF_ALU | BPF_END | BPF_FROM_LE: case BPF_ALU | BPF_END | BPF_FROM_BE: + case BPF_ALU64 | BPF_END | BPF_FROM_LE: #ifdef __BIG_ENDIAN__ if (BPF_SRC(code) == BPF_FROM_BE) goto emit_clear; #else /* !__BIG_ENDIAN__ */ - if (BPF_SRC(code) == BPF_FROM_LE) + if (BPF_CLASS(code) == BPF_ALU && BPF_SRC(code) == BPF_FROM_LE) goto emit_clear; #endif switch (imm) { @@ -936,13 +968,19 @@ emit_clear: */ /* dst = *(u8 *)(ul) (src + off) */ case BPF_LDX | BPF_MEM | BPF_B: + case BPF_LDX | BPF_MEMSX | BPF_B: case BPF_LDX | BPF_PROBE_MEM | BPF_B: + case BPF_LDX | BPF_PROBE_MEMSX | BPF_B: /* dst = *(u16 *)(ul) (src + off) */ case BPF_LDX | BPF_MEM | BPF_H: + case BPF_LDX | BPF_MEMSX | BPF_H: case BPF_LDX | BPF_PROBE_MEM | BPF_H: + case BPF_LDX | BPF_PROBE_MEMSX | BPF_H: /* dst = *(u32 *)(ul) (src + off) */ case BPF_LDX | BPF_MEM | BPF_W: + case BPF_LDX | BPF_MEMSX | BPF_W: case BPF_LDX | BPF_PROBE_MEM | BPF_W: + case BPF_LDX | BPF_PROBE_MEMSX | BPF_W: /* dst = *(u64 *)(ul) (src + off) */ case BPF_LDX | BPF_MEM | BPF_DW: case BPF_LDX | BPF_PROBE_MEM | BPF_DW: @@ -952,7 +990,7 @@ emit_clear: * load only if addr is kernel address (see is_kernel_addr()), otherwise * set dst_reg=0 and move on. */ - if (BPF_MODE(code) == BPF_PROBE_MEM) { + if (BPF_MODE(code) == BPF_PROBE_MEM || BPF_MODE(code) == BPF_PROBE_MEMSX) { EMIT(PPC_RAW_ADDI(tmp1_reg, src_reg, off)); if (IS_ENABLED(CONFIG_PPC_BOOK3E_64)) PPC_LI64(tmp2_reg, 0x8000000000000000ul); @@ -965,30 +1003,47 @@ emit_clear: * Check if 'off' is word aligned for BPF_DW, because * we might generate two instructions. */ - if (BPF_SIZE(code) == BPF_DW && (off & 3)) + if ((BPF_SIZE(code) == BPF_DW || + (BPF_SIZE(code) == BPF_B && BPF_MODE(code) == BPF_PROBE_MEMSX)) && + (off & 3)) PPC_JMP((ctx->idx + 3) * 4); else PPC_JMP((ctx->idx + 2) * 4); } - switch (size) { - case BPF_B: - EMIT(PPC_RAW_LBZ(dst_reg, src_reg, off)); - break; - case BPF_H: - EMIT(PPC_RAW_LHZ(dst_reg, src_reg, off)); - break; - case BPF_W: - EMIT(PPC_RAW_LWZ(dst_reg, src_reg, off)); - break; - case BPF_DW: - if (off % 4) { - EMIT(PPC_RAW_LI(tmp1_reg, off)); - EMIT(PPC_RAW_LDX(dst_reg, src_reg, tmp1_reg)); - } else { - EMIT(PPC_RAW_LD(dst_reg, src_reg, off)); + if (BPF_MODE(code) == BPF_MEMSX || BPF_MODE(code) == BPF_PROBE_MEMSX) { + switch (size) { + case BPF_B: + EMIT(PPC_RAW_LBZ(dst_reg, src_reg, off)); + EMIT(PPC_RAW_EXTSB(dst_reg, dst_reg)); + break; + case BPF_H: + EMIT(PPC_RAW_LHA(dst_reg, src_reg, off)); + break; + case BPF_W: + EMIT(PPC_RAW_LWA(dst_reg, src_reg, off)); + break; + } + } else { + switch (size) { + case BPF_B: + EMIT(PPC_RAW_LBZ(dst_reg, src_reg, off)); + break; + case BPF_H: + EMIT(PPC_RAW_LHZ(dst_reg, src_reg, off)); + break; + case BPF_W: + EMIT(PPC_RAW_LWZ(dst_reg, src_reg, off)); + break; + case BPF_DW: + if (off % 4) { + EMIT(PPC_RAW_LI(tmp1_reg, off)); + EMIT(PPC_RAW_LDX(dst_reg, src_reg, tmp1_reg)); + } else { + EMIT(PPC_RAW_LD(dst_reg, src_reg, off)); + } + break; } - break; } if (size != BPF_DW && insn_is_zext(&insn[i + 1])) @@ -1065,6 +1120,9 @@ emit_clear: case BPF_JMP | BPF_JA: PPC_JMP(addrs[i + 1 + off]); break; + case BPF_JMP32 | BPF_JA: + PPC_JMP(addrs[i + 1 + imm]); + break; case BPF_JMP | BPF_JGT | BPF_K: case BPF_JMP | BPF_JGT | BPF_X: |