summaryrefslogtreecommitdiff
path: root/arch/arm/net/bpf_jit_32.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/net/bpf_jit_32.c')
-rw-r--r--arch/arm/net/bpf_jit_32.c50
1 files changed, 38 insertions, 12 deletions
diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c
index c641fb685017..6828ef6ce80e 100644
--- a/arch/arm/net/bpf_jit_32.c
+++ b/arch/arm/net/bpf_jit_32.c
@@ -16,6 +16,7 @@
#include <linux/netdevice.h>
#include <linux/string.h>
#include <linux/slab.h>
+#include <linux/if_vlan.h>
#include <asm/cacheflush.h>
#include <asm/hwcap.h>
@@ -42,7 +43,7 @@
#define r_skb_hl ARM_R8
#define SCRATCH_SP_OFFSET 0
-#define SCRATCH_OFF(k) (SCRATCH_SP_OFFSET + (k))
+#define SCRATCH_OFF(k) (SCRATCH_SP_OFFSET + 4 * (k))
#define SEEN_MEM ((1 << BPF_MEMWORDS) - 1)
#define SEEN_MEM_WORD(k) (1 << (k))
@@ -168,6 +169,8 @@ static inline bool is_load_to_a(u16 inst)
case BPF_S_ANC_MARK:
case BPF_S_ANC_PROTOCOL:
case BPF_S_ANC_RXHASH:
+ case BPF_S_ANC_VLAN_TAG:
+ case BPF_S_ANC_VLAN_TAG_PRESENT:
case BPF_S_ANC_QUEUE:
return true;
default:
@@ -338,10 +341,17 @@ static void emit_load_be16(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx)
static inline void emit_swap16(u8 r_dst, u8 r_src, struct jit_ctx *ctx)
{
- emit(ARM_LSL_R(ARM_R1, r_src, 8), ctx);
- emit(ARM_ORR_S(r_dst, ARM_R1, r_src, SRTYPE_LSL, 8), ctx);
- emit(ARM_LSL_I(r_dst, r_dst, 8), ctx);
- emit(ARM_LSL_R(r_dst, r_dst, 8), ctx);
+ /* r_dst = (r_src << 8) | (r_src >> 8) */
+ emit(ARM_LSL_I(ARM_R1, r_src, 8), ctx);
+ emit(ARM_ORR_S(r_dst, ARM_R1, r_src, SRTYPE_LSR, 8), ctx);
+
+ /*
+ * we need to mask out the bits set in r_dst[23:16] due to
+ * the first shift instruction.
+ *
+ * note that 0x8ff is the encoded immediate 0x00ff0000.
+ */
+ emit(ARM_BIC_I(r_dst, r_dst, 0x8ff), ctx);
}
#else /* ARMv6+ */
@@ -646,6 +656,16 @@ load_ind:
update_on_xread(ctx);
emit(ARM_ORR_R(r_A, r_A, r_X), ctx);
break;
+ case BPF_S_ALU_XOR_K:
+ /* A ^= K; */
+ OP_IMM3(ARM_EOR, r_A, r_A, k, ctx);
+ break;
+ case BPF_S_ANC_ALU_XOR_X:
+ case BPF_S_ALU_XOR_X:
+ /* A ^= X */
+ update_on_xread(ctx);
+ emit(ARM_EOR_R(r_A, r_A, r_X), ctx);
+ break;
case BPF_S_ALU_AND_K:
/* A &= K */
OP_IMM3(ARM_AND, r_A, r_A, k, ctx);
@@ -762,11 +782,6 @@ b_epilogue:
update_on_xread(ctx);
emit(ARM_MOV_R(r_A, r_X), ctx);
break;
- case BPF_S_ANC_ALU_XOR_X:
- /* A ^= X */
- update_on_xread(ctx);
- emit(ARM_EOR_R(r_A, r_A, r_X), ctx);
- break;
case BPF_S_ANC_PROTOCOL:
/* A = ntohs(skb->protocol) */
ctx->seen |= SEEN_SKB;
@@ -810,6 +825,17 @@ b_epilogue:
off = offsetof(struct sk_buff, rxhash);
emit(ARM_LDR_I(r_A, r_skb, off), ctx);
break;
+ case BPF_S_ANC_VLAN_TAG:
+ case BPF_S_ANC_VLAN_TAG_PRESENT:
+ ctx->seen |= SEEN_SKB;
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
+ off = offsetof(struct sk_buff, vlan_tci);
+ emit(ARM_LDRH_I(r_A, r_skb, off), ctx);
+ if (inst->code == BPF_S_ANC_VLAN_TAG)
+ OP_IMM3(ARM_AND, r_A, r_A, VLAN_VID_MASK, ctx);
+ else
+ OP_IMM3(ARM_AND, r_A, r_A, VLAN_TAG_PRESENT, ctx);
+ break;
case BPF_S_ANC_QUEUE:
ctx->seen |= SEEN_SKB;
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
@@ -845,7 +871,7 @@ void bpf_jit_compile(struct sk_filter *fp)
ctx.skf = fp;
ctx.ret0_fp_idx = -1;
- ctx.offsets = kzalloc(GFP_KERNEL, 4 * (ctx.skf->len + 1));
+ ctx.offsets = kzalloc(4 * (ctx.skf->len + 1), GFP_KERNEL);
if (ctx.offsets == NULL)
return;
@@ -864,7 +890,7 @@ void bpf_jit_compile(struct sk_filter *fp)
ctx.idx += ctx.imm_count;
if (ctx.imm_count) {
- ctx.imms = kzalloc(GFP_KERNEL, 4 * ctx.imm_count);
+ ctx.imms = kzalloc(4 * ctx.imm_count, GFP_KERNEL);
if (ctx.imms == NULL)
goto out;
}