|
@@ -112,3 +112,98 @@ static u32 jit_udiv(u32 dividend, u32 divisor)
|
|
}
|
|
}
|
|
|
|
|
|
static inline void _emit(int cond, u32 inst, struct jit_ctx *ctx)
|
|
static inline void _emit(int cond, u32 inst, struct jit_ctx *ctx)
|
|
|
|
+{
|
|
|
|
+ if (ctx->target != NULL)
|
|
|
|
+ ctx->target[ctx->idx] = inst | (cond << 28);
|
|
|
|
+
|
|
|
|
+ ctx->idx++;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Emit an instruction that will be executed unconditionally.
|
|
|
|
+ */
|
|
|
|
+static inline void emit(u32 inst, struct jit_ctx *ctx)
|
|
|
|
+{
|
|
|
|
+ _emit(ARM_COND_AL, inst, ctx);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static u16 saved_regs(struct jit_ctx *ctx)
|
|
|
|
+{
|
|
|
|
+ u16 ret = 0;
|
|
|
|
+
|
|
|
|
+ if ((ctx->skf->len > 1) ||
|
|
|
|
+ (ctx->skf->insns[0].code == BPF_S_RET_A))
|
|
|
|
+ ret |= 1 << r_A;
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_FRAME_POINTER
|
|
|
|
+ ret |= (1 << ARM_FP) | (1 << ARM_IP) | (1 << ARM_LR) | (1 << ARM_PC);
|
|
|
|
+#else
|
|
|
|
+ if (ctx->seen & SEEN_CALL)
|
|
|
|
+ ret |= 1 << ARM_LR;
|
|
|
|
+#endif
|
|
|
|
+ if (ctx->seen & (SEEN_DATA | SEEN_SKB))
|
|
|
|
+ ret |= 1 << r_skb;
|
|
|
|
+ if (ctx->seen & SEEN_DATA)
|
|
|
|
+ ret |= (1 << r_skb_data) | (1 << r_skb_hl);
|
|
|
|
+ if (ctx->seen & SEEN_X)
|
|
|
|
+ ret |= 1 << r_X;
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline int mem_words_used(struct jit_ctx *ctx)
|
|
|
|
+{
|
|
|
|
+ /* yes, we do waste some stack space IF there are "holes" in the set" */
|
|
|
|
+ return fls(ctx->seen & SEEN_MEM);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline bool is_load_to_a(u16 inst)
|
|
|
|
+{
|
|
|
|
+ switch (inst) {
|
|
|
|
+ case BPF_S_LD_W_LEN:
|
|
|
|
+ case BPF_S_LD_W_ABS:
|
|
|
|
+ case BPF_S_LD_H_ABS:
|
|
|
|
+ case BPF_S_LD_B_ABS:
|
|
|
|
+ case BPF_S_ANC_CPU:
|
|
|
|
+ case BPF_S_ANC_IFINDEX:
|
|
|
|
+ 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:
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void build_prologue(struct jit_ctx *ctx)
|
|
|
|
+{
|
|
|
|
+ u16 reg_set = saved_regs(ctx);
|
|
|
|
+ u16 first_inst = ctx->skf->insns[0].code;
|
|
|
|
+ u16 off;
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_FRAME_POINTER
|
|
|
|
+ emit(ARM_MOV_R(ARM_IP, ARM_SP), ctx);
|
|
|
|
+ emit(ARM_PUSH(reg_set), ctx);
|
|
|
|
+ emit(ARM_SUB_I(ARM_FP, ARM_IP, 4), ctx);
|
|
|
|
+#else
|
|
|
|
+ if (reg_set)
|
|
|
|
+ emit(ARM_PUSH(reg_set), ctx);
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ if (ctx->seen & (SEEN_DATA | SEEN_SKB))
|
|
|
|
+ emit(ARM_MOV_R(r_skb, ARM_R0), ctx);
|
|
|
|
+
|
|
|
|
+ if (ctx->seen & SEEN_DATA) {
|
|
|
|
+ off = offsetof(struct sk_buff, data);
|
|
|
|
+ emit(ARM_LDR_I(r_skb_data, r_skb, off), ctx);
|
|
|
|
+ /* headlen = len - data_len */
|
|
|
|
+ off = offsetof(struct sk_buff, len);
|
|
|
|
+ emit(ARM_LDR_I(r_skb_hl, r_skb, off), ctx);
|
|
|
|
+ off = offsetof(struct sk_buff, data_len);
|
|
|
|
+ emit(ARM_LDR_I(r_scratch, r_skb, off), ctx);
|
|
|
|
+ emit(ARM_SUB_R(r_skb_hl, r_skb_hl, r_scratch), ctx);
|
|
|
|
+ }
|
|
|
|
+
|