|
@@ -449,3 +449,202 @@ static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx)
|
|
|
|
|
|
if (rd != ARM_R0)
|
|
if (rd != ARM_R0)
|
|
emit(ARM_MOV_R(rd, ARM_R0), ctx);
|
|
emit(ARM_MOV_R(rd, ARM_R0), ctx);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline void update_on_xread(struct jit_ctx *ctx)
|
|
|
|
+{
|
|
|
|
+ if (!(ctx->seen & SEEN_X))
|
|
|
|
+ ctx->flags |= FLAG_NEED_X_RESET;
|
|
|
|
+
|
|
|
|
+ ctx->seen |= SEEN_X;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int build_body(struct jit_ctx *ctx)
|
|
|
|
+{
|
|
|
|
+ void *load_func[] = {jit_get_skb_b, jit_get_skb_h, jit_get_skb_w};
|
|
|
|
+ const struct sk_filter *prog = ctx->skf;
|
|
|
|
+ const struct sock_filter *inst;
|
|
|
|
+ unsigned i, load_order, off, condt;
|
|
|
|
+ int imm12;
|
|
|
|
+ u32 k;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < prog->len; i++) {
|
|
|
|
+ inst = &(prog->insns[i]);
|
|
|
|
+ /* K as an immediate value operand */
|
|
|
|
+ k = inst->k;
|
|
|
|
+
|
|
|
|
+ /* compute offsets only in the fake pass */
|
|
|
|
+ if (ctx->target == NULL)
|
|
|
|
+ ctx->offsets[i] = ctx->idx * 4;
|
|
|
|
+
|
|
|
|
+ switch (inst->code) {
|
|
|
|
+ case BPF_S_LD_IMM:
|
|
|
|
+ emit_mov_i(r_A, k, ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_LD_W_LEN:
|
|
|
|
+ ctx->seen |= SEEN_SKB;
|
|
|
|
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
|
|
|
|
+ emit(ARM_LDR_I(r_A, r_skb,
|
|
|
|
+ offsetof(struct sk_buff, len)), ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_LD_MEM:
|
|
|
|
+ /* A = scratch[k] */
|
|
|
|
+ ctx->seen |= SEEN_MEM_WORD(k);
|
|
|
|
+ emit(ARM_LDR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_LD_W_ABS:
|
|
|
|
+ load_order = 2;
|
|
|
|
+ goto load;
|
|
|
|
+ case BPF_S_LD_H_ABS:
|
|
|
|
+ load_order = 1;
|
|
|
|
+ goto load;
|
|
|
|
+ case BPF_S_LD_B_ABS:
|
|
|
|
+ load_order = 0;
|
|
|
|
+load:
|
|
|
|
+ /* the interpreter will deal with the negative K */
|
|
|
|
+ if ((int)k < 0)
|
|
|
|
+ return -ENOTSUPP;
|
|
|
|
+ emit_mov_i(r_off, k, ctx);
|
|
|
|
+load_common:
|
|
|
|
+ ctx->seen |= SEEN_DATA | SEEN_CALL;
|
|
|
|
+
|
|
|
|
+ if (load_order > 0) {
|
|
|
|
+ emit(ARM_SUB_I(r_scratch, r_skb_hl,
|
|
|
|
+ 1 << load_order), ctx);
|
|
|
|
+ emit(ARM_CMP_R(r_scratch, r_off), ctx);
|
|
|
|
+ condt = ARM_COND_HS;
|
|
|
|
+ } else {
|
|
|
|
+ emit(ARM_CMP_R(r_skb_hl, r_off), ctx);
|
|
|
|
+ condt = ARM_COND_HI;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _emit(condt, ARM_ADD_R(r_scratch, r_off, r_skb_data),
|
|
|
|
+ ctx);
|
|
|
|
+
|
|
|
|
+ if (load_order == 0)
|
|
|
|
+ _emit(condt, ARM_LDRB_I(r_A, r_scratch, 0),
|
|
|
|
+ ctx);
|
|
|
|
+ else if (load_order == 1)
|
|
|
|
+ emit_load_be16(condt, r_A, r_scratch, ctx);
|
|
|
|
+ else if (load_order == 2)
|
|
|
|
+ emit_load_be32(condt, r_A, r_scratch, ctx);
|
|
|
|
+
|
|
|
|
+ _emit(condt, ARM_B(b_imm(i + 1, ctx)), ctx);
|
|
|
|
+
|
|
|
|
+ /* the slowpath */
|
|
|
|
+ emit_mov_i(ARM_R3, (u32)load_func[load_order], ctx);
|
|
|
|
+ emit(ARM_MOV_R(ARM_R0, r_skb), ctx);
|
|
|
|
+ /* the offset is already in R1 */
|
|
|
|
+ emit_blx_r(ARM_R3, ctx);
|
|
|
|
+ /* check the result of skb_copy_bits */
|
|
|
|
+ emit(ARM_CMP_I(ARM_R1, 0), ctx);
|
|
|
|
+ emit_err_ret(ARM_COND_NE, ctx);
|
|
|
|
+ emit(ARM_MOV_R(r_A, ARM_R0), ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_LD_W_IND:
|
|
|
|
+ load_order = 2;
|
|
|
|
+ goto load_ind;
|
|
|
|
+ case BPF_S_LD_H_IND:
|
|
|
|
+ load_order = 1;
|
|
|
|
+ goto load_ind;
|
|
|
|
+ case BPF_S_LD_B_IND:
|
|
|
|
+ load_order = 0;
|
|
|
|
+load_ind:
|
|
|
|
+ OP_IMM3(ARM_ADD, r_off, r_X, k, ctx);
|
|
|
|
+ goto load_common;
|
|
|
|
+ case BPF_S_LDX_IMM:
|
|
|
|
+ ctx->seen |= SEEN_X;
|
|
|
|
+ emit_mov_i(r_X, k, ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_LDX_W_LEN:
|
|
|
|
+ ctx->seen |= SEEN_X | SEEN_SKB;
|
|
|
|
+ emit(ARM_LDR_I(r_X, r_skb,
|
|
|
|
+ offsetof(struct sk_buff, len)), ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_LDX_MEM:
|
|
|
|
+ ctx->seen |= SEEN_X | SEEN_MEM_WORD(k);
|
|
|
|
+ emit(ARM_LDR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_LDX_B_MSH:
|
|
|
|
+ /* x = ((*(frame + k)) & 0xf) << 2; */
|
|
|
|
+ ctx->seen |= SEEN_X | SEEN_DATA | SEEN_CALL;
|
|
|
|
+ /* the interpreter should deal with the negative K */
|
|
|
|
+ if (k < 0)
|
|
|
|
+ return -1;
|
|
|
|
+ /* offset in r1: we might have to take the slow path */
|
|
|
|
+ emit_mov_i(r_off, k, ctx);
|
|
|
|
+ emit(ARM_CMP_R(r_skb_hl, r_off), ctx);
|
|
|
|
+
|
|
|
|
+ /* load in r0: common with the slowpath */
|
|
|
|
+ _emit(ARM_COND_HI, ARM_LDRB_R(ARM_R0, r_skb_data,
|
|
|
|
+ ARM_R1), ctx);
|
|
|
|
+ /*
|
|
|
|
+ * emit_mov_i() might generate one or two instructions,
|
|
|
|
+ * the same holds for emit_blx_r()
|
|
|
|
+ */
|
|
|
|
+ _emit(ARM_COND_HI, ARM_B(b_imm(i + 1, ctx) - 2), ctx);
|
|
|
|
+
|
|
|
|
+ emit(ARM_MOV_R(ARM_R0, r_skb), ctx);
|
|
|
|
+ /* r_off is r1 */
|
|
|
|
+ emit_mov_i(ARM_R3, (u32)jit_get_skb_b, ctx);
|
|
|
|
+ emit_blx_r(ARM_R3, ctx);
|
|
|
|
+ /* check the return value of skb_copy_bits */
|
|
|
|
+ emit(ARM_CMP_I(ARM_R1, 0), ctx);
|
|
|
|
+ emit_err_ret(ARM_COND_NE, ctx);
|
|
|
|
+
|
|
|
|
+ emit(ARM_AND_I(r_X, ARM_R0, 0x00f), ctx);
|
|
|
|
+ emit(ARM_LSL_I(r_X, r_X, 2), ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_ST:
|
|
|
|
+ ctx->seen |= SEEN_MEM_WORD(k);
|
|
|
|
+ emit(ARM_STR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_STX:
|
|
|
|
+ update_on_xread(ctx);
|
|
|
|
+ ctx->seen |= SEEN_MEM_WORD(k);
|
|
|
|
+ emit(ARM_STR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_ALU_ADD_K:
|
|
|
|
+ /* A += K */
|
|
|
|
+ OP_IMM3(ARM_ADD, r_A, r_A, k, ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_ALU_ADD_X:
|
|
|
|
+ update_on_xread(ctx);
|
|
|
|
+ emit(ARM_ADD_R(r_A, r_A, r_X), ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_ALU_SUB_K:
|
|
|
|
+ /* A -= K */
|
|
|
|
+ OP_IMM3(ARM_SUB, r_A, r_A, k, ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_ALU_SUB_X:
|
|
|
|
+ update_on_xread(ctx);
|
|
|
|
+ emit(ARM_SUB_R(r_A, r_A, r_X), ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_ALU_MUL_K:
|
|
|
|
+ /* A *= K */
|
|
|
|
+ emit_mov_i(r_scratch, k, ctx);
|
|
|
|
+ emit(ARM_MUL(r_A, r_A, r_scratch), ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_ALU_MUL_X:
|
|
|
|
+ update_on_xread(ctx);
|
|
|
|
+ emit(ARM_MUL(r_A, r_A, r_X), ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_ALU_DIV_K:
|
|
|
|
+ /* current k == reciprocal_value(userspace k) */
|
|
|
|
+ emit_mov_i(r_scratch, k, ctx);
|
|
|
|
+ /* A = top 32 bits of the product */
|
|
|
|
+ emit(ARM_UMULL(r_scratch, r_A, r_A, r_scratch), ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_ALU_DIV_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);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_ALU_OR_K:
|
|
|
|
+ /* A |= K */
|
|
|
|
+ OP_IMM3(ARM_ORR, r_A, r_A, k, ctx);
|
|
|
|
+ break;
|
|
|
|
+ case BPF_S_ALU_OR_X:
|
|
|
|
+ update_on_xread(ctx);
|
|
|
|
+ emit(ARM_ORR_R(r_A, r_A, r_X), ctx);
|