1ddecdfceSMircea Gherzan /* 2ddecdfceSMircea Gherzan * Just-In-Time compiler for BPF filters on 32bit ARM 3ddecdfceSMircea Gherzan * 4ddecdfceSMircea Gherzan * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com> 5ddecdfceSMircea Gherzan * 6ddecdfceSMircea Gherzan * This program is free software; you can redistribute it and/or modify it 7ddecdfceSMircea Gherzan * under the terms of the GNU General Public License as published by the 8ddecdfceSMircea Gherzan * Free Software Foundation; version 2 of the License. 9ddecdfceSMircea Gherzan */ 10ddecdfceSMircea Gherzan 11ddecdfceSMircea Gherzan #include <linux/bitops.h> 12ddecdfceSMircea Gherzan #include <linux/compiler.h> 13ddecdfceSMircea Gherzan #include <linux/errno.h> 14ddecdfceSMircea Gherzan #include <linux/filter.h> 15ddecdfceSMircea Gherzan #include <linux/moduleloader.h> 16ddecdfceSMircea Gherzan #include <linux/netdevice.h> 17ddecdfceSMircea Gherzan #include <linux/string.h> 18ddecdfceSMircea Gherzan #include <linux/slab.h> 19ddecdfceSMircea Gherzan #include <asm/cacheflush.h> 20ddecdfceSMircea Gherzan #include <asm/hwcap.h> 21ddecdfceSMircea Gherzan 22ddecdfceSMircea Gherzan #include "bpf_jit_32.h" 23ddecdfceSMircea Gherzan 24ddecdfceSMircea Gherzan /* 25ddecdfceSMircea Gherzan * ABI: 26ddecdfceSMircea Gherzan * 27ddecdfceSMircea Gherzan * r0 scratch register 28ddecdfceSMircea Gherzan * r4 BPF register A 29ddecdfceSMircea Gherzan * r5 BPF register X 30ddecdfceSMircea Gherzan * r6 pointer to the skb 31ddecdfceSMircea Gherzan * r7 skb->data 32ddecdfceSMircea Gherzan * r8 skb_headlen(skb) 33ddecdfceSMircea Gherzan */ 34ddecdfceSMircea Gherzan 35ddecdfceSMircea Gherzan #define r_scratch ARM_R0 36ddecdfceSMircea Gherzan /* r1-r3 are (also) used for the unaligned loads on the non-ARMv7 slowpath */ 37ddecdfceSMircea Gherzan #define r_off ARM_R1 38ddecdfceSMircea Gherzan #define r_A ARM_R4 39ddecdfceSMircea Gherzan #define r_X ARM_R5 40ddecdfceSMircea Gherzan #define r_skb ARM_R6 41ddecdfceSMircea Gherzan #define r_skb_data ARM_R7 42ddecdfceSMircea Gherzan #define r_skb_hl ARM_R8 43ddecdfceSMircea Gherzan 44ddecdfceSMircea Gherzan #define SCRATCH_SP_OFFSET 0 45*fe15f3f1SSchichan Nicolas #define SCRATCH_OFF(k) (SCRATCH_SP_OFFSET + 4 * (k)) 46ddecdfceSMircea Gherzan 47ddecdfceSMircea Gherzan #define SEEN_MEM ((1 << BPF_MEMWORDS) - 1) 48ddecdfceSMircea Gherzan #define SEEN_MEM_WORD(k) (1 << (k)) 49ddecdfceSMircea Gherzan #define SEEN_X (1 << BPF_MEMWORDS) 50ddecdfceSMircea Gherzan #define SEEN_CALL (1 << (BPF_MEMWORDS + 1)) 51ddecdfceSMircea Gherzan #define SEEN_SKB (1 << (BPF_MEMWORDS + 2)) 52ddecdfceSMircea Gherzan #define SEEN_DATA (1 << (BPF_MEMWORDS + 3)) 53ddecdfceSMircea Gherzan 54ddecdfceSMircea Gherzan #define FLAG_NEED_X_RESET (1 << 0) 55ddecdfceSMircea Gherzan 56ddecdfceSMircea Gherzan struct jit_ctx { 57ddecdfceSMircea Gherzan const struct sk_filter *skf; 58ddecdfceSMircea Gherzan unsigned idx; 59ddecdfceSMircea Gherzan unsigned prologue_bytes; 60ddecdfceSMircea Gherzan int ret0_fp_idx; 61ddecdfceSMircea Gherzan u32 seen; 62ddecdfceSMircea Gherzan u32 flags; 63ddecdfceSMircea Gherzan u32 *offsets; 64ddecdfceSMircea Gherzan u32 *target; 65ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ < 7 66ddecdfceSMircea Gherzan u16 epilogue_bytes; 67ddecdfceSMircea Gherzan u16 imm_count; 68ddecdfceSMircea Gherzan u32 *imms; 69ddecdfceSMircea Gherzan #endif 70ddecdfceSMircea Gherzan }; 71ddecdfceSMircea Gherzan 72ddecdfceSMircea Gherzan int bpf_jit_enable __read_mostly; 73ddecdfceSMircea Gherzan 74ddecdfceSMircea Gherzan static u64 jit_get_skb_b(struct sk_buff *skb, unsigned offset) 75ddecdfceSMircea Gherzan { 76ddecdfceSMircea Gherzan u8 ret; 77ddecdfceSMircea Gherzan int err; 78ddecdfceSMircea Gherzan 79ddecdfceSMircea Gherzan err = skb_copy_bits(skb, offset, &ret, 1); 80ddecdfceSMircea Gherzan 81ddecdfceSMircea Gherzan return (u64)err << 32 | ret; 82ddecdfceSMircea Gherzan } 83ddecdfceSMircea Gherzan 84ddecdfceSMircea Gherzan static u64 jit_get_skb_h(struct sk_buff *skb, unsigned offset) 85ddecdfceSMircea Gherzan { 86ddecdfceSMircea Gherzan u16 ret; 87ddecdfceSMircea Gherzan int err; 88ddecdfceSMircea Gherzan 89ddecdfceSMircea Gherzan err = skb_copy_bits(skb, offset, &ret, 2); 90ddecdfceSMircea Gherzan 91ddecdfceSMircea Gherzan return (u64)err << 32 | ntohs(ret); 92ddecdfceSMircea Gherzan } 93ddecdfceSMircea Gherzan 94ddecdfceSMircea Gherzan static u64 jit_get_skb_w(struct sk_buff *skb, unsigned offset) 95ddecdfceSMircea Gherzan { 96ddecdfceSMircea Gherzan u32 ret; 97ddecdfceSMircea Gherzan int err; 98ddecdfceSMircea Gherzan 99ddecdfceSMircea Gherzan err = skb_copy_bits(skb, offset, &ret, 4); 100ddecdfceSMircea Gherzan 101ddecdfceSMircea Gherzan return (u64)err << 32 | ntohl(ret); 102ddecdfceSMircea Gherzan } 103ddecdfceSMircea Gherzan 104ddecdfceSMircea Gherzan /* 105ddecdfceSMircea Gherzan * Wrapper that handles both OABI and EABI and assures Thumb2 interworking 106ddecdfceSMircea Gherzan * (where the assembly routines like __aeabi_uidiv could cause problems). 107ddecdfceSMircea Gherzan */ 108ddecdfceSMircea Gherzan static u32 jit_udiv(u32 dividend, u32 divisor) 109ddecdfceSMircea Gherzan { 110ddecdfceSMircea Gherzan return dividend / divisor; 111ddecdfceSMircea Gherzan } 112ddecdfceSMircea Gherzan 113ddecdfceSMircea Gherzan static inline void _emit(int cond, u32 inst, struct jit_ctx *ctx) 114ddecdfceSMircea Gherzan { 115ddecdfceSMircea Gherzan if (ctx->target != NULL) 116ddecdfceSMircea Gherzan ctx->target[ctx->idx] = inst | (cond << 28); 117ddecdfceSMircea Gherzan 118ddecdfceSMircea Gherzan ctx->idx++; 119ddecdfceSMircea Gherzan } 120ddecdfceSMircea Gherzan 121ddecdfceSMircea Gherzan /* 122ddecdfceSMircea Gherzan * Emit an instruction that will be executed unconditionally. 123ddecdfceSMircea Gherzan */ 124ddecdfceSMircea Gherzan static inline void emit(u32 inst, struct jit_ctx *ctx) 125ddecdfceSMircea Gherzan { 126ddecdfceSMircea Gherzan _emit(ARM_COND_AL, inst, ctx); 127ddecdfceSMircea Gherzan } 128ddecdfceSMircea Gherzan 129ddecdfceSMircea Gherzan static u16 saved_regs(struct jit_ctx *ctx) 130ddecdfceSMircea Gherzan { 131ddecdfceSMircea Gherzan u16 ret = 0; 132ddecdfceSMircea Gherzan 133ddecdfceSMircea Gherzan if ((ctx->skf->len > 1) || 134ddecdfceSMircea Gherzan (ctx->skf->insns[0].code == BPF_S_RET_A)) 135ddecdfceSMircea Gherzan ret |= 1 << r_A; 136ddecdfceSMircea Gherzan 137ddecdfceSMircea Gherzan #ifdef CONFIG_FRAME_POINTER 138ddecdfceSMircea Gherzan ret |= (1 << ARM_FP) | (1 << ARM_IP) | (1 << ARM_LR) | (1 << ARM_PC); 139ddecdfceSMircea Gherzan #else 140ddecdfceSMircea Gherzan if (ctx->seen & SEEN_CALL) 141ddecdfceSMircea Gherzan ret |= 1 << ARM_LR; 142ddecdfceSMircea Gherzan #endif 143ddecdfceSMircea Gherzan if (ctx->seen & (SEEN_DATA | SEEN_SKB)) 144ddecdfceSMircea Gherzan ret |= 1 << r_skb; 145ddecdfceSMircea Gherzan if (ctx->seen & SEEN_DATA) 146ddecdfceSMircea Gherzan ret |= (1 << r_skb_data) | (1 << r_skb_hl); 147ddecdfceSMircea Gherzan if (ctx->seen & SEEN_X) 148ddecdfceSMircea Gherzan ret |= 1 << r_X; 149ddecdfceSMircea Gherzan 150ddecdfceSMircea Gherzan return ret; 151ddecdfceSMircea Gherzan } 152ddecdfceSMircea Gherzan 153ddecdfceSMircea Gherzan static inline int mem_words_used(struct jit_ctx *ctx) 154ddecdfceSMircea Gherzan { 155ddecdfceSMircea Gherzan /* yes, we do waste some stack space IF there are "holes" in the set" */ 156ddecdfceSMircea Gherzan return fls(ctx->seen & SEEN_MEM); 157ddecdfceSMircea Gherzan } 158ddecdfceSMircea Gherzan 159ddecdfceSMircea Gherzan static inline bool is_load_to_a(u16 inst) 160ddecdfceSMircea Gherzan { 161ddecdfceSMircea Gherzan switch (inst) { 162ddecdfceSMircea Gherzan case BPF_S_LD_W_LEN: 163ddecdfceSMircea Gherzan case BPF_S_LD_W_ABS: 164ddecdfceSMircea Gherzan case BPF_S_LD_H_ABS: 165ddecdfceSMircea Gherzan case BPF_S_LD_B_ABS: 166ddecdfceSMircea Gherzan case BPF_S_ANC_CPU: 167ddecdfceSMircea Gherzan case BPF_S_ANC_IFINDEX: 168ddecdfceSMircea Gherzan case BPF_S_ANC_MARK: 169ddecdfceSMircea Gherzan case BPF_S_ANC_PROTOCOL: 170ddecdfceSMircea Gherzan case BPF_S_ANC_RXHASH: 171ddecdfceSMircea Gherzan case BPF_S_ANC_QUEUE: 172ddecdfceSMircea Gherzan return true; 173ddecdfceSMircea Gherzan default: 174ddecdfceSMircea Gherzan return false; 175ddecdfceSMircea Gherzan } 176ddecdfceSMircea Gherzan } 177ddecdfceSMircea Gherzan 178ddecdfceSMircea Gherzan static void build_prologue(struct jit_ctx *ctx) 179ddecdfceSMircea Gherzan { 180ddecdfceSMircea Gherzan u16 reg_set = saved_regs(ctx); 181ddecdfceSMircea Gherzan u16 first_inst = ctx->skf->insns[0].code; 182ddecdfceSMircea Gherzan u16 off; 183ddecdfceSMircea Gherzan 184ddecdfceSMircea Gherzan #ifdef CONFIG_FRAME_POINTER 185ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_IP, ARM_SP), ctx); 186ddecdfceSMircea Gherzan emit(ARM_PUSH(reg_set), ctx); 187ddecdfceSMircea Gherzan emit(ARM_SUB_I(ARM_FP, ARM_IP, 4), ctx); 188ddecdfceSMircea Gherzan #else 189ddecdfceSMircea Gherzan if (reg_set) 190ddecdfceSMircea Gherzan emit(ARM_PUSH(reg_set), ctx); 191ddecdfceSMircea Gherzan #endif 192ddecdfceSMircea Gherzan 193ddecdfceSMircea Gherzan if (ctx->seen & (SEEN_DATA | SEEN_SKB)) 194ddecdfceSMircea Gherzan emit(ARM_MOV_R(r_skb, ARM_R0), ctx); 195ddecdfceSMircea Gherzan 196ddecdfceSMircea Gherzan if (ctx->seen & SEEN_DATA) { 197ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, data); 198ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_skb_data, r_skb, off), ctx); 199ddecdfceSMircea Gherzan /* headlen = len - data_len */ 200ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, len); 201ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_skb_hl, r_skb, off), ctx); 202ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, data_len); 203ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_scratch, r_skb, off), ctx); 204ddecdfceSMircea Gherzan emit(ARM_SUB_R(r_skb_hl, r_skb_hl, r_scratch), ctx); 205ddecdfceSMircea Gherzan } 206ddecdfceSMircea Gherzan 207ddecdfceSMircea Gherzan if (ctx->flags & FLAG_NEED_X_RESET) 208ddecdfceSMircea Gherzan emit(ARM_MOV_I(r_X, 0), ctx); 209ddecdfceSMircea Gherzan 210ddecdfceSMircea Gherzan /* do not leak kernel data to userspace */ 211ddecdfceSMircea Gherzan if ((first_inst != BPF_S_RET_K) && !(is_load_to_a(first_inst))) 212ddecdfceSMircea Gherzan emit(ARM_MOV_I(r_A, 0), ctx); 213ddecdfceSMircea Gherzan 214ddecdfceSMircea Gherzan /* stack space for the BPF_MEM words */ 215ddecdfceSMircea Gherzan if (ctx->seen & SEEN_MEM) 216ddecdfceSMircea Gherzan emit(ARM_SUB_I(ARM_SP, ARM_SP, mem_words_used(ctx) * 4), ctx); 217ddecdfceSMircea Gherzan } 218ddecdfceSMircea Gherzan 219ddecdfceSMircea Gherzan static void build_epilogue(struct jit_ctx *ctx) 220ddecdfceSMircea Gherzan { 221ddecdfceSMircea Gherzan u16 reg_set = saved_regs(ctx); 222ddecdfceSMircea Gherzan 223ddecdfceSMircea Gherzan if (ctx->seen & SEEN_MEM) 224ddecdfceSMircea Gherzan emit(ARM_ADD_I(ARM_SP, ARM_SP, mem_words_used(ctx) * 4), ctx); 225ddecdfceSMircea Gherzan 226ddecdfceSMircea Gherzan reg_set &= ~(1 << ARM_LR); 227ddecdfceSMircea Gherzan 228ddecdfceSMircea Gherzan #ifdef CONFIG_FRAME_POINTER 229ddecdfceSMircea Gherzan /* the first instruction of the prologue was: mov ip, sp */ 230ddecdfceSMircea Gherzan reg_set &= ~(1 << ARM_IP); 231ddecdfceSMircea Gherzan reg_set |= (1 << ARM_SP); 232ddecdfceSMircea Gherzan emit(ARM_LDM(ARM_SP, reg_set), ctx); 233ddecdfceSMircea Gherzan #else 234ddecdfceSMircea Gherzan if (reg_set) { 235ddecdfceSMircea Gherzan if (ctx->seen & SEEN_CALL) 236ddecdfceSMircea Gherzan reg_set |= 1 << ARM_PC; 237ddecdfceSMircea Gherzan emit(ARM_POP(reg_set), ctx); 238ddecdfceSMircea Gherzan } 239ddecdfceSMircea Gherzan 240ddecdfceSMircea Gherzan if (!(ctx->seen & SEEN_CALL)) 241ddecdfceSMircea Gherzan emit(ARM_BX(ARM_LR), ctx); 242ddecdfceSMircea Gherzan #endif 243ddecdfceSMircea Gherzan } 244ddecdfceSMircea Gherzan 245ddecdfceSMircea Gherzan static int16_t imm8m(u32 x) 246ddecdfceSMircea Gherzan { 247ddecdfceSMircea Gherzan u32 rot; 248ddecdfceSMircea Gherzan 249ddecdfceSMircea Gherzan for (rot = 0; rot < 16; rot++) 250ddecdfceSMircea Gherzan if ((x & ~ror32(0xff, 2 * rot)) == 0) 251ddecdfceSMircea Gherzan return rol32(x, 2 * rot) | (rot << 8); 252ddecdfceSMircea Gherzan 253ddecdfceSMircea Gherzan return -1; 254ddecdfceSMircea Gherzan } 255ddecdfceSMircea Gherzan 256ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ < 7 257ddecdfceSMircea Gherzan 258ddecdfceSMircea Gherzan static u16 imm_offset(u32 k, struct jit_ctx *ctx) 259ddecdfceSMircea Gherzan { 260ddecdfceSMircea Gherzan unsigned i = 0, offset; 261ddecdfceSMircea Gherzan u16 imm; 262ddecdfceSMircea Gherzan 263ddecdfceSMircea Gherzan /* on the "fake" run we just count them (duplicates included) */ 264ddecdfceSMircea Gherzan if (ctx->target == NULL) { 265ddecdfceSMircea Gherzan ctx->imm_count++; 266ddecdfceSMircea Gherzan return 0; 267ddecdfceSMircea Gherzan } 268ddecdfceSMircea Gherzan 269ddecdfceSMircea Gherzan while ((i < ctx->imm_count) && ctx->imms[i]) { 270ddecdfceSMircea Gherzan if (ctx->imms[i] == k) 271ddecdfceSMircea Gherzan break; 272ddecdfceSMircea Gherzan i++; 273ddecdfceSMircea Gherzan } 274ddecdfceSMircea Gherzan 275ddecdfceSMircea Gherzan if (ctx->imms[i] == 0) 276ddecdfceSMircea Gherzan ctx->imms[i] = k; 277ddecdfceSMircea Gherzan 278ddecdfceSMircea Gherzan /* constants go just after the epilogue */ 279ddecdfceSMircea Gherzan offset = ctx->offsets[ctx->skf->len]; 280ddecdfceSMircea Gherzan offset += ctx->prologue_bytes; 281ddecdfceSMircea Gherzan offset += ctx->epilogue_bytes; 282ddecdfceSMircea Gherzan offset += i * 4; 283ddecdfceSMircea Gherzan 284ddecdfceSMircea Gherzan ctx->target[offset / 4] = k; 285ddecdfceSMircea Gherzan 286ddecdfceSMircea Gherzan /* PC in ARM mode == address of the instruction + 8 */ 287ddecdfceSMircea Gherzan imm = offset - (8 + ctx->idx * 4); 288ddecdfceSMircea Gherzan 289ddecdfceSMircea Gherzan return imm; 290ddecdfceSMircea Gherzan } 291ddecdfceSMircea Gherzan 292ddecdfceSMircea Gherzan #endif /* __LINUX_ARM_ARCH__ */ 293ddecdfceSMircea Gherzan 294ddecdfceSMircea Gherzan /* 295ddecdfceSMircea Gherzan * Move an immediate that's not an imm8m to a core register. 296ddecdfceSMircea Gherzan */ 297ddecdfceSMircea Gherzan static inline void emit_mov_i_no8m(int rd, u32 val, struct jit_ctx *ctx) 298ddecdfceSMircea Gherzan { 299ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ < 7 300ddecdfceSMircea Gherzan emit(ARM_LDR_I(rd, ARM_PC, imm_offset(val, ctx)), ctx); 301ddecdfceSMircea Gherzan #else 302ddecdfceSMircea Gherzan emit(ARM_MOVW(rd, val & 0xffff), ctx); 303ddecdfceSMircea Gherzan if (val > 0xffff) 304ddecdfceSMircea Gherzan emit(ARM_MOVT(rd, val >> 16), ctx); 305ddecdfceSMircea Gherzan #endif 306ddecdfceSMircea Gherzan } 307ddecdfceSMircea Gherzan 308ddecdfceSMircea Gherzan static inline void emit_mov_i(int rd, u32 val, struct jit_ctx *ctx) 309ddecdfceSMircea Gherzan { 310ddecdfceSMircea Gherzan int imm12 = imm8m(val); 311ddecdfceSMircea Gherzan 312ddecdfceSMircea Gherzan if (imm12 >= 0) 313ddecdfceSMircea Gherzan emit(ARM_MOV_I(rd, imm12), ctx); 314ddecdfceSMircea Gherzan else 315ddecdfceSMircea Gherzan emit_mov_i_no8m(rd, val, ctx); 316ddecdfceSMircea Gherzan } 317ddecdfceSMircea Gherzan 318ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ < 6 319ddecdfceSMircea Gherzan 320ddecdfceSMircea Gherzan static void emit_load_be32(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx) 321ddecdfceSMircea Gherzan { 322ddecdfceSMircea Gherzan _emit(cond, ARM_LDRB_I(ARM_R3, r_addr, 1), ctx); 323ddecdfceSMircea Gherzan _emit(cond, ARM_LDRB_I(ARM_R1, r_addr, 0), ctx); 324ddecdfceSMircea Gherzan _emit(cond, ARM_LDRB_I(ARM_R2, r_addr, 3), ctx); 325ddecdfceSMircea Gherzan _emit(cond, ARM_LSL_I(ARM_R3, ARM_R3, 16), ctx); 326ddecdfceSMircea Gherzan _emit(cond, ARM_LDRB_I(ARM_R0, r_addr, 2), ctx); 327ddecdfceSMircea Gherzan _emit(cond, ARM_ORR_S(ARM_R3, ARM_R3, ARM_R1, SRTYPE_LSL, 24), ctx); 328ddecdfceSMircea Gherzan _emit(cond, ARM_ORR_R(ARM_R3, ARM_R3, ARM_R2), ctx); 329ddecdfceSMircea Gherzan _emit(cond, ARM_ORR_S(r_res, ARM_R3, ARM_R0, SRTYPE_LSL, 8), ctx); 330ddecdfceSMircea Gherzan } 331ddecdfceSMircea Gherzan 332ddecdfceSMircea Gherzan static void emit_load_be16(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx) 333ddecdfceSMircea Gherzan { 334ddecdfceSMircea Gherzan _emit(cond, ARM_LDRB_I(ARM_R1, r_addr, 0), ctx); 335ddecdfceSMircea Gherzan _emit(cond, ARM_LDRB_I(ARM_R2, r_addr, 1), ctx); 336ddecdfceSMircea Gherzan _emit(cond, ARM_ORR_S(r_res, ARM_R2, ARM_R1, SRTYPE_LSL, 8), ctx); 337ddecdfceSMircea Gherzan } 338ddecdfceSMircea Gherzan 339ddecdfceSMircea Gherzan static inline void emit_swap16(u8 r_dst, u8 r_src, struct jit_ctx *ctx) 340ddecdfceSMircea Gherzan { 341ddecdfceSMircea Gherzan emit(ARM_LSL_R(ARM_R1, r_src, 8), ctx); 342ddecdfceSMircea Gherzan emit(ARM_ORR_S(r_dst, ARM_R1, r_src, SRTYPE_LSL, 8), ctx); 343ddecdfceSMircea Gherzan emit(ARM_LSL_I(r_dst, r_dst, 8), ctx); 344ddecdfceSMircea Gherzan emit(ARM_LSL_R(r_dst, r_dst, 8), ctx); 345ddecdfceSMircea Gherzan } 346ddecdfceSMircea Gherzan 347ddecdfceSMircea Gherzan #else /* ARMv6+ */ 348ddecdfceSMircea Gherzan 349ddecdfceSMircea Gherzan static void emit_load_be32(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx) 350ddecdfceSMircea Gherzan { 351ddecdfceSMircea Gherzan _emit(cond, ARM_LDR_I(r_res, r_addr, 0), ctx); 352ddecdfceSMircea Gherzan #ifdef __LITTLE_ENDIAN 353ddecdfceSMircea Gherzan _emit(cond, ARM_REV(r_res, r_res), ctx); 354ddecdfceSMircea Gherzan #endif 355ddecdfceSMircea Gherzan } 356ddecdfceSMircea Gherzan 357ddecdfceSMircea Gherzan static void emit_load_be16(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx) 358ddecdfceSMircea Gherzan { 359ddecdfceSMircea Gherzan _emit(cond, ARM_LDRH_I(r_res, r_addr, 0), ctx); 360ddecdfceSMircea Gherzan #ifdef __LITTLE_ENDIAN 361ddecdfceSMircea Gherzan _emit(cond, ARM_REV16(r_res, r_res), ctx); 362ddecdfceSMircea Gherzan #endif 363ddecdfceSMircea Gherzan } 364ddecdfceSMircea Gherzan 365ddecdfceSMircea Gherzan static inline void emit_swap16(u8 r_dst __maybe_unused, 366ddecdfceSMircea Gherzan u8 r_src __maybe_unused, 367ddecdfceSMircea Gherzan struct jit_ctx *ctx __maybe_unused) 368ddecdfceSMircea Gherzan { 369ddecdfceSMircea Gherzan #ifdef __LITTLE_ENDIAN 370ddecdfceSMircea Gherzan emit(ARM_REV16(r_dst, r_src), ctx); 371ddecdfceSMircea Gherzan #endif 372ddecdfceSMircea Gherzan } 373ddecdfceSMircea Gherzan 374ddecdfceSMircea Gherzan #endif /* __LINUX_ARM_ARCH__ < 6 */ 375ddecdfceSMircea Gherzan 376ddecdfceSMircea Gherzan 377ddecdfceSMircea Gherzan /* Compute the immediate value for a PC-relative branch. */ 378ddecdfceSMircea Gherzan static inline u32 b_imm(unsigned tgt, struct jit_ctx *ctx) 379ddecdfceSMircea Gherzan { 380ddecdfceSMircea Gherzan u32 imm; 381ddecdfceSMircea Gherzan 382ddecdfceSMircea Gherzan if (ctx->target == NULL) 383ddecdfceSMircea Gherzan return 0; 384ddecdfceSMircea Gherzan /* 385ddecdfceSMircea Gherzan * BPF allows only forward jumps and the offset of the target is 386ddecdfceSMircea Gherzan * still the one computed during the first pass. 387ddecdfceSMircea Gherzan */ 388ddecdfceSMircea Gherzan imm = ctx->offsets[tgt] + ctx->prologue_bytes - (ctx->idx * 4 + 8); 389ddecdfceSMircea Gherzan 390ddecdfceSMircea Gherzan return imm >> 2; 391ddecdfceSMircea Gherzan } 392ddecdfceSMircea Gherzan 393ddecdfceSMircea Gherzan #define OP_IMM3(op, r1, r2, imm_val, ctx) \ 394ddecdfceSMircea Gherzan do { \ 395ddecdfceSMircea Gherzan imm12 = imm8m(imm_val); \ 396ddecdfceSMircea Gherzan if (imm12 < 0) { \ 397ddecdfceSMircea Gherzan emit_mov_i_no8m(r_scratch, imm_val, ctx); \ 398ddecdfceSMircea Gherzan emit(op ## _R((r1), (r2), r_scratch), ctx); \ 399ddecdfceSMircea Gherzan } else { \ 400ddecdfceSMircea Gherzan emit(op ## _I((r1), (r2), imm12), ctx); \ 401ddecdfceSMircea Gherzan } \ 402ddecdfceSMircea Gherzan } while (0) 403ddecdfceSMircea Gherzan 404ddecdfceSMircea Gherzan static inline void emit_err_ret(u8 cond, struct jit_ctx *ctx) 405ddecdfceSMircea Gherzan { 406ddecdfceSMircea Gherzan if (ctx->ret0_fp_idx >= 0) { 407ddecdfceSMircea Gherzan _emit(cond, ARM_B(b_imm(ctx->ret0_fp_idx, ctx)), ctx); 408ddecdfceSMircea Gherzan /* NOP to keep the size constant between passes */ 409ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_R0, ARM_R0), ctx); 410ddecdfceSMircea Gherzan } else { 411ddecdfceSMircea Gherzan _emit(cond, ARM_MOV_I(ARM_R0, 0), ctx); 412ddecdfceSMircea Gherzan _emit(cond, ARM_B(b_imm(ctx->skf->len, ctx)), ctx); 413ddecdfceSMircea Gherzan } 414ddecdfceSMircea Gherzan } 415ddecdfceSMircea Gherzan 416ddecdfceSMircea Gherzan static inline void emit_blx_r(u8 tgt_reg, struct jit_ctx *ctx) 417ddecdfceSMircea Gherzan { 418ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ < 5 419ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_LR, ARM_PC), ctx); 420ddecdfceSMircea Gherzan 421ddecdfceSMircea Gherzan if (elf_hwcap & HWCAP_THUMB) 422ddecdfceSMircea Gherzan emit(ARM_BX(tgt_reg), ctx); 423ddecdfceSMircea Gherzan else 424ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_PC, tgt_reg), ctx); 425ddecdfceSMircea Gherzan #else 426ddecdfceSMircea Gherzan emit(ARM_BLX_R(tgt_reg), ctx); 427ddecdfceSMircea Gherzan #endif 428ddecdfceSMircea Gherzan } 429ddecdfceSMircea Gherzan 430ddecdfceSMircea Gherzan static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx) 431ddecdfceSMircea Gherzan { 432ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ == 7 433ddecdfceSMircea Gherzan if (elf_hwcap & HWCAP_IDIVA) { 434ddecdfceSMircea Gherzan emit(ARM_UDIV(rd, rm, rn), ctx); 435ddecdfceSMircea Gherzan return; 436ddecdfceSMircea Gherzan } 437ddecdfceSMircea Gherzan #endif 438ddecdfceSMircea Gherzan if (rm != ARM_R0) 439ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_R0, rm), ctx); 440ddecdfceSMircea Gherzan if (rn != ARM_R1) 441ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_R1, rn), ctx); 442ddecdfceSMircea Gherzan 443ddecdfceSMircea Gherzan ctx->seen |= SEEN_CALL; 444ddecdfceSMircea Gherzan emit_mov_i(ARM_R3, (u32)jit_udiv, ctx); 445ddecdfceSMircea Gherzan emit_blx_r(ARM_R3, ctx); 446ddecdfceSMircea Gherzan 447ddecdfceSMircea Gherzan if (rd != ARM_R0) 448ddecdfceSMircea Gherzan emit(ARM_MOV_R(rd, ARM_R0), ctx); 449ddecdfceSMircea Gherzan } 450ddecdfceSMircea Gherzan 451ddecdfceSMircea Gherzan static inline void update_on_xread(struct jit_ctx *ctx) 452ddecdfceSMircea Gherzan { 453ddecdfceSMircea Gherzan if (!(ctx->seen & SEEN_X)) 454ddecdfceSMircea Gherzan ctx->flags |= FLAG_NEED_X_RESET; 455ddecdfceSMircea Gherzan 456ddecdfceSMircea Gherzan ctx->seen |= SEEN_X; 457ddecdfceSMircea Gherzan } 458ddecdfceSMircea Gherzan 459ddecdfceSMircea Gherzan static int build_body(struct jit_ctx *ctx) 460ddecdfceSMircea Gherzan { 461ddecdfceSMircea Gherzan void *load_func[] = {jit_get_skb_b, jit_get_skb_h, jit_get_skb_w}; 462ddecdfceSMircea Gherzan const struct sk_filter *prog = ctx->skf; 463ddecdfceSMircea Gherzan const struct sock_filter *inst; 464ddecdfceSMircea Gherzan unsigned i, load_order, off, condt; 465ddecdfceSMircea Gherzan int imm12; 466ddecdfceSMircea Gherzan u32 k; 467ddecdfceSMircea Gherzan 468ddecdfceSMircea Gherzan for (i = 0; i < prog->len; i++) { 469ddecdfceSMircea Gherzan inst = &(prog->insns[i]); 470ddecdfceSMircea Gherzan /* K as an immediate value operand */ 471ddecdfceSMircea Gherzan k = inst->k; 472ddecdfceSMircea Gherzan 473ddecdfceSMircea Gherzan /* compute offsets only in the fake pass */ 474ddecdfceSMircea Gherzan if (ctx->target == NULL) 475ddecdfceSMircea Gherzan ctx->offsets[i] = ctx->idx * 4; 476ddecdfceSMircea Gherzan 477ddecdfceSMircea Gherzan switch (inst->code) { 478ddecdfceSMircea Gherzan case BPF_S_LD_IMM: 479ddecdfceSMircea Gherzan emit_mov_i(r_A, k, ctx); 480ddecdfceSMircea Gherzan break; 481ddecdfceSMircea Gherzan case BPF_S_LD_W_LEN: 482ddecdfceSMircea Gherzan ctx->seen |= SEEN_SKB; 483ddecdfceSMircea Gherzan BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4); 484ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_A, r_skb, 485ddecdfceSMircea Gherzan offsetof(struct sk_buff, len)), ctx); 486ddecdfceSMircea Gherzan break; 487ddecdfceSMircea Gherzan case BPF_S_LD_MEM: 488ddecdfceSMircea Gherzan /* A = scratch[k] */ 489ddecdfceSMircea Gherzan ctx->seen |= SEEN_MEM_WORD(k); 490ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx); 491ddecdfceSMircea Gherzan break; 492ddecdfceSMircea Gherzan case BPF_S_LD_W_ABS: 493ddecdfceSMircea Gherzan load_order = 2; 494ddecdfceSMircea Gherzan goto load; 495ddecdfceSMircea Gherzan case BPF_S_LD_H_ABS: 496ddecdfceSMircea Gherzan load_order = 1; 497ddecdfceSMircea Gherzan goto load; 498ddecdfceSMircea Gherzan case BPF_S_LD_B_ABS: 499ddecdfceSMircea Gherzan load_order = 0; 500ddecdfceSMircea Gherzan load: 501ddecdfceSMircea Gherzan /* the interpreter will deal with the negative K */ 502ddecdfceSMircea Gherzan if ((int)k < 0) 503ddecdfceSMircea Gherzan return -ENOTSUPP; 504ddecdfceSMircea Gherzan emit_mov_i(r_off, k, ctx); 505ddecdfceSMircea Gherzan load_common: 506ddecdfceSMircea Gherzan ctx->seen |= SEEN_DATA | SEEN_CALL; 507ddecdfceSMircea Gherzan 508ddecdfceSMircea Gherzan if (load_order > 0) { 509ddecdfceSMircea Gherzan emit(ARM_SUB_I(r_scratch, r_skb_hl, 510ddecdfceSMircea Gherzan 1 << load_order), ctx); 511ddecdfceSMircea Gherzan emit(ARM_CMP_R(r_scratch, r_off), ctx); 512ddecdfceSMircea Gherzan condt = ARM_COND_HS; 513ddecdfceSMircea Gherzan } else { 514ddecdfceSMircea Gherzan emit(ARM_CMP_R(r_skb_hl, r_off), ctx); 515ddecdfceSMircea Gherzan condt = ARM_COND_HI; 516ddecdfceSMircea Gherzan } 517ddecdfceSMircea Gherzan 518ddecdfceSMircea Gherzan _emit(condt, ARM_ADD_R(r_scratch, r_off, r_skb_data), 519ddecdfceSMircea Gherzan ctx); 520ddecdfceSMircea Gherzan 521ddecdfceSMircea Gherzan if (load_order == 0) 522ddecdfceSMircea Gherzan _emit(condt, ARM_LDRB_I(r_A, r_scratch, 0), 523ddecdfceSMircea Gherzan ctx); 524ddecdfceSMircea Gherzan else if (load_order == 1) 525ddecdfceSMircea Gherzan emit_load_be16(condt, r_A, r_scratch, ctx); 526ddecdfceSMircea Gherzan else if (load_order == 2) 527ddecdfceSMircea Gherzan emit_load_be32(condt, r_A, r_scratch, ctx); 528ddecdfceSMircea Gherzan 529ddecdfceSMircea Gherzan _emit(condt, ARM_B(b_imm(i + 1, ctx)), ctx); 530ddecdfceSMircea Gherzan 531ddecdfceSMircea Gherzan /* the slowpath */ 532ddecdfceSMircea Gherzan emit_mov_i(ARM_R3, (u32)load_func[load_order], ctx); 533ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_R0, r_skb), ctx); 534ddecdfceSMircea Gherzan /* the offset is already in R1 */ 535ddecdfceSMircea Gherzan emit_blx_r(ARM_R3, ctx); 536ddecdfceSMircea Gherzan /* check the result of skb_copy_bits */ 537ddecdfceSMircea Gherzan emit(ARM_CMP_I(ARM_R1, 0), ctx); 538ddecdfceSMircea Gherzan emit_err_ret(ARM_COND_NE, ctx); 539ddecdfceSMircea Gherzan emit(ARM_MOV_R(r_A, ARM_R0), ctx); 540ddecdfceSMircea Gherzan break; 541ddecdfceSMircea Gherzan case BPF_S_LD_W_IND: 542ddecdfceSMircea Gherzan load_order = 2; 543ddecdfceSMircea Gherzan goto load_ind; 544ddecdfceSMircea Gherzan case BPF_S_LD_H_IND: 545ddecdfceSMircea Gherzan load_order = 1; 546ddecdfceSMircea Gherzan goto load_ind; 547ddecdfceSMircea Gherzan case BPF_S_LD_B_IND: 548ddecdfceSMircea Gherzan load_order = 0; 549ddecdfceSMircea Gherzan load_ind: 550ddecdfceSMircea Gherzan OP_IMM3(ARM_ADD, r_off, r_X, k, ctx); 551ddecdfceSMircea Gherzan goto load_common; 552ddecdfceSMircea Gherzan case BPF_S_LDX_IMM: 553ddecdfceSMircea Gherzan ctx->seen |= SEEN_X; 554ddecdfceSMircea Gherzan emit_mov_i(r_X, k, ctx); 555ddecdfceSMircea Gherzan break; 556ddecdfceSMircea Gherzan case BPF_S_LDX_W_LEN: 557ddecdfceSMircea Gherzan ctx->seen |= SEEN_X | SEEN_SKB; 558ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_X, r_skb, 559ddecdfceSMircea Gherzan offsetof(struct sk_buff, len)), ctx); 560ddecdfceSMircea Gherzan break; 561ddecdfceSMircea Gherzan case BPF_S_LDX_MEM: 562ddecdfceSMircea Gherzan ctx->seen |= SEEN_X | SEEN_MEM_WORD(k); 563ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx); 564ddecdfceSMircea Gherzan break; 565ddecdfceSMircea Gherzan case BPF_S_LDX_B_MSH: 566ddecdfceSMircea Gherzan /* x = ((*(frame + k)) & 0xf) << 2; */ 567ddecdfceSMircea Gherzan ctx->seen |= SEEN_X | SEEN_DATA | SEEN_CALL; 568ddecdfceSMircea Gherzan /* the interpreter should deal with the negative K */ 569ddecdfceSMircea Gherzan if (k < 0) 570ddecdfceSMircea Gherzan return -1; 571ddecdfceSMircea Gherzan /* offset in r1: we might have to take the slow path */ 572ddecdfceSMircea Gherzan emit_mov_i(r_off, k, ctx); 573ddecdfceSMircea Gherzan emit(ARM_CMP_R(r_skb_hl, r_off), ctx); 574ddecdfceSMircea Gherzan 575ddecdfceSMircea Gherzan /* load in r0: common with the slowpath */ 576ddecdfceSMircea Gherzan _emit(ARM_COND_HI, ARM_LDRB_R(ARM_R0, r_skb_data, 577ddecdfceSMircea Gherzan ARM_R1), ctx); 578ddecdfceSMircea Gherzan /* 579ddecdfceSMircea Gherzan * emit_mov_i() might generate one or two instructions, 580ddecdfceSMircea Gherzan * the same holds for emit_blx_r() 581ddecdfceSMircea Gherzan */ 582ddecdfceSMircea Gherzan _emit(ARM_COND_HI, ARM_B(b_imm(i + 1, ctx) - 2), ctx); 583ddecdfceSMircea Gherzan 584ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_R0, r_skb), ctx); 585ddecdfceSMircea Gherzan /* r_off is r1 */ 586ddecdfceSMircea Gherzan emit_mov_i(ARM_R3, (u32)jit_get_skb_b, ctx); 587ddecdfceSMircea Gherzan emit_blx_r(ARM_R3, ctx); 588ddecdfceSMircea Gherzan /* check the return value of skb_copy_bits */ 589ddecdfceSMircea Gherzan emit(ARM_CMP_I(ARM_R1, 0), ctx); 590ddecdfceSMircea Gherzan emit_err_ret(ARM_COND_NE, ctx); 591ddecdfceSMircea Gherzan 592ddecdfceSMircea Gherzan emit(ARM_AND_I(r_X, ARM_R0, 0x00f), ctx); 593ddecdfceSMircea Gherzan emit(ARM_LSL_I(r_X, r_X, 2), ctx); 594ddecdfceSMircea Gherzan break; 595ddecdfceSMircea Gherzan case BPF_S_ST: 596ddecdfceSMircea Gherzan ctx->seen |= SEEN_MEM_WORD(k); 597ddecdfceSMircea Gherzan emit(ARM_STR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx); 598ddecdfceSMircea Gherzan break; 599ddecdfceSMircea Gherzan case BPF_S_STX: 600ddecdfceSMircea Gherzan update_on_xread(ctx); 601ddecdfceSMircea Gherzan ctx->seen |= SEEN_MEM_WORD(k); 602ddecdfceSMircea Gherzan emit(ARM_STR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx); 603ddecdfceSMircea Gherzan break; 604ddecdfceSMircea Gherzan case BPF_S_ALU_ADD_K: 605ddecdfceSMircea Gherzan /* A += K */ 606ddecdfceSMircea Gherzan OP_IMM3(ARM_ADD, r_A, r_A, k, ctx); 607ddecdfceSMircea Gherzan break; 608ddecdfceSMircea Gherzan case BPF_S_ALU_ADD_X: 609ddecdfceSMircea Gherzan update_on_xread(ctx); 610ddecdfceSMircea Gherzan emit(ARM_ADD_R(r_A, r_A, r_X), ctx); 611ddecdfceSMircea Gherzan break; 612ddecdfceSMircea Gherzan case BPF_S_ALU_SUB_K: 613ddecdfceSMircea Gherzan /* A -= K */ 614ddecdfceSMircea Gherzan OP_IMM3(ARM_SUB, r_A, r_A, k, ctx); 615ddecdfceSMircea Gherzan break; 616ddecdfceSMircea Gherzan case BPF_S_ALU_SUB_X: 617ddecdfceSMircea Gherzan update_on_xread(ctx); 618ddecdfceSMircea Gherzan emit(ARM_SUB_R(r_A, r_A, r_X), ctx); 619ddecdfceSMircea Gherzan break; 620ddecdfceSMircea Gherzan case BPF_S_ALU_MUL_K: 621ddecdfceSMircea Gherzan /* A *= K */ 622ddecdfceSMircea Gherzan emit_mov_i(r_scratch, k, ctx); 623ddecdfceSMircea Gherzan emit(ARM_MUL(r_A, r_A, r_scratch), ctx); 624ddecdfceSMircea Gherzan break; 625ddecdfceSMircea Gherzan case BPF_S_ALU_MUL_X: 626ddecdfceSMircea Gherzan update_on_xread(ctx); 627ddecdfceSMircea Gherzan emit(ARM_MUL(r_A, r_A, r_X), ctx); 628ddecdfceSMircea Gherzan break; 629ddecdfceSMircea Gherzan case BPF_S_ALU_DIV_K: 630ddecdfceSMircea Gherzan /* current k == reciprocal_value(userspace k) */ 631ddecdfceSMircea Gherzan emit_mov_i(r_scratch, k, ctx); 632ddecdfceSMircea Gherzan /* A = top 32 bits of the product */ 633ddecdfceSMircea Gherzan emit(ARM_UMULL(r_scratch, r_A, r_A, r_scratch), ctx); 634ddecdfceSMircea Gherzan break; 635ddecdfceSMircea Gherzan case BPF_S_ALU_DIV_X: 636ddecdfceSMircea Gherzan update_on_xread(ctx); 637ddecdfceSMircea Gherzan emit(ARM_CMP_I(r_X, 0), ctx); 638ddecdfceSMircea Gherzan emit_err_ret(ARM_COND_EQ, ctx); 639ddecdfceSMircea Gherzan emit_udiv(r_A, r_A, r_X, ctx); 640ddecdfceSMircea Gherzan break; 641ddecdfceSMircea Gherzan case BPF_S_ALU_OR_K: 642ddecdfceSMircea Gherzan /* A |= K */ 643ddecdfceSMircea Gherzan OP_IMM3(ARM_ORR, r_A, r_A, k, ctx); 644ddecdfceSMircea Gherzan break; 645ddecdfceSMircea Gherzan case BPF_S_ALU_OR_X: 646ddecdfceSMircea Gherzan update_on_xread(ctx); 647ddecdfceSMircea Gherzan emit(ARM_ORR_R(r_A, r_A, r_X), ctx); 648ddecdfceSMircea Gherzan break; 649ddecdfceSMircea Gherzan case BPF_S_ALU_AND_K: 650ddecdfceSMircea Gherzan /* A &= K */ 651ddecdfceSMircea Gherzan OP_IMM3(ARM_AND, r_A, r_A, k, ctx); 652ddecdfceSMircea Gherzan break; 653ddecdfceSMircea Gherzan case BPF_S_ALU_AND_X: 654ddecdfceSMircea Gherzan update_on_xread(ctx); 655ddecdfceSMircea Gherzan emit(ARM_AND_R(r_A, r_A, r_X), ctx); 656ddecdfceSMircea Gherzan break; 657ddecdfceSMircea Gherzan case BPF_S_ALU_LSH_K: 658ddecdfceSMircea Gherzan if (unlikely(k > 31)) 659ddecdfceSMircea Gherzan return -1; 660ddecdfceSMircea Gherzan emit(ARM_LSL_I(r_A, r_A, k), ctx); 661ddecdfceSMircea Gherzan break; 662ddecdfceSMircea Gherzan case BPF_S_ALU_LSH_X: 663ddecdfceSMircea Gherzan update_on_xread(ctx); 664ddecdfceSMircea Gherzan emit(ARM_LSL_R(r_A, r_A, r_X), ctx); 665ddecdfceSMircea Gherzan break; 666ddecdfceSMircea Gherzan case BPF_S_ALU_RSH_K: 667ddecdfceSMircea Gherzan if (unlikely(k > 31)) 668ddecdfceSMircea Gherzan return -1; 669ddecdfceSMircea Gherzan emit(ARM_LSR_I(r_A, r_A, k), ctx); 670ddecdfceSMircea Gherzan break; 671ddecdfceSMircea Gherzan case BPF_S_ALU_RSH_X: 672ddecdfceSMircea Gherzan update_on_xread(ctx); 673ddecdfceSMircea Gherzan emit(ARM_LSR_R(r_A, r_A, r_X), ctx); 674ddecdfceSMircea Gherzan break; 675ddecdfceSMircea Gherzan case BPF_S_ALU_NEG: 676ddecdfceSMircea Gherzan /* A = -A */ 677ddecdfceSMircea Gherzan emit(ARM_RSB_I(r_A, r_A, 0), ctx); 678ddecdfceSMircea Gherzan break; 679ddecdfceSMircea Gherzan case BPF_S_JMP_JA: 680ddecdfceSMircea Gherzan /* pc += K */ 681ddecdfceSMircea Gherzan emit(ARM_B(b_imm(i + k + 1, ctx)), ctx); 682ddecdfceSMircea Gherzan break; 683ddecdfceSMircea Gherzan case BPF_S_JMP_JEQ_K: 684ddecdfceSMircea Gherzan /* pc += (A == K) ? pc->jt : pc->jf */ 685ddecdfceSMircea Gherzan condt = ARM_COND_EQ; 686ddecdfceSMircea Gherzan goto cmp_imm; 687ddecdfceSMircea Gherzan case BPF_S_JMP_JGT_K: 688ddecdfceSMircea Gherzan /* pc += (A > K) ? pc->jt : pc->jf */ 689ddecdfceSMircea Gherzan condt = ARM_COND_HI; 690ddecdfceSMircea Gherzan goto cmp_imm; 691ddecdfceSMircea Gherzan case BPF_S_JMP_JGE_K: 692ddecdfceSMircea Gherzan /* pc += (A >= K) ? pc->jt : pc->jf */ 693ddecdfceSMircea Gherzan condt = ARM_COND_HS; 694ddecdfceSMircea Gherzan cmp_imm: 695ddecdfceSMircea Gherzan imm12 = imm8m(k); 696ddecdfceSMircea Gherzan if (imm12 < 0) { 697ddecdfceSMircea Gherzan emit_mov_i_no8m(r_scratch, k, ctx); 698ddecdfceSMircea Gherzan emit(ARM_CMP_R(r_A, r_scratch), ctx); 699ddecdfceSMircea Gherzan } else { 700ddecdfceSMircea Gherzan emit(ARM_CMP_I(r_A, imm12), ctx); 701ddecdfceSMircea Gherzan } 702ddecdfceSMircea Gherzan cond_jump: 703ddecdfceSMircea Gherzan if (inst->jt) 704ddecdfceSMircea Gherzan _emit(condt, ARM_B(b_imm(i + inst->jt + 1, 705ddecdfceSMircea Gherzan ctx)), ctx); 706ddecdfceSMircea Gherzan if (inst->jf) 707ddecdfceSMircea Gherzan _emit(condt ^ 1, ARM_B(b_imm(i + inst->jf + 1, 708ddecdfceSMircea Gherzan ctx)), ctx); 709ddecdfceSMircea Gherzan break; 710ddecdfceSMircea Gherzan case BPF_S_JMP_JEQ_X: 711ddecdfceSMircea Gherzan /* pc += (A == X) ? pc->jt : pc->jf */ 712ddecdfceSMircea Gherzan condt = ARM_COND_EQ; 713ddecdfceSMircea Gherzan goto cmp_x; 714ddecdfceSMircea Gherzan case BPF_S_JMP_JGT_X: 715ddecdfceSMircea Gherzan /* pc += (A > X) ? pc->jt : pc->jf */ 716ddecdfceSMircea Gherzan condt = ARM_COND_HI; 717ddecdfceSMircea Gherzan goto cmp_x; 718ddecdfceSMircea Gherzan case BPF_S_JMP_JGE_X: 719ddecdfceSMircea Gherzan /* pc += (A >= X) ? pc->jt : pc->jf */ 720ddecdfceSMircea Gherzan condt = ARM_COND_CS; 721ddecdfceSMircea Gherzan cmp_x: 722ddecdfceSMircea Gherzan update_on_xread(ctx); 723ddecdfceSMircea Gherzan emit(ARM_CMP_R(r_A, r_X), ctx); 724ddecdfceSMircea Gherzan goto cond_jump; 725ddecdfceSMircea Gherzan case BPF_S_JMP_JSET_K: 726ddecdfceSMircea Gherzan /* pc += (A & K) ? pc->jt : pc->jf */ 727ddecdfceSMircea Gherzan condt = ARM_COND_NE; 728ddecdfceSMircea Gherzan /* not set iff all zeroes iff Z==1 iff EQ */ 729ddecdfceSMircea Gherzan 730ddecdfceSMircea Gherzan imm12 = imm8m(k); 731ddecdfceSMircea Gherzan if (imm12 < 0) { 732ddecdfceSMircea Gherzan emit_mov_i_no8m(r_scratch, k, ctx); 733ddecdfceSMircea Gherzan emit(ARM_TST_R(r_A, r_scratch), ctx); 734ddecdfceSMircea Gherzan } else { 735ddecdfceSMircea Gherzan emit(ARM_TST_I(r_A, imm12), ctx); 736ddecdfceSMircea Gherzan } 737ddecdfceSMircea Gherzan goto cond_jump; 738ddecdfceSMircea Gherzan case BPF_S_JMP_JSET_X: 739ddecdfceSMircea Gherzan /* pc += (A & X) ? pc->jt : pc->jf */ 740ddecdfceSMircea Gherzan update_on_xread(ctx); 741ddecdfceSMircea Gherzan condt = ARM_COND_NE; 742ddecdfceSMircea Gherzan emit(ARM_TST_R(r_A, r_X), ctx); 743ddecdfceSMircea Gherzan goto cond_jump; 744ddecdfceSMircea Gherzan case BPF_S_RET_A: 745ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_R0, r_A), ctx); 746ddecdfceSMircea Gherzan goto b_epilogue; 747ddecdfceSMircea Gherzan case BPF_S_RET_K: 748ddecdfceSMircea Gherzan if ((k == 0) && (ctx->ret0_fp_idx < 0)) 749ddecdfceSMircea Gherzan ctx->ret0_fp_idx = i; 750ddecdfceSMircea Gherzan emit_mov_i(ARM_R0, k, ctx); 751ddecdfceSMircea Gherzan b_epilogue: 752ddecdfceSMircea Gherzan if (i != ctx->skf->len - 1) 753ddecdfceSMircea Gherzan emit(ARM_B(b_imm(prog->len, ctx)), ctx); 754ddecdfceSMircea Gherzan break; 755ddecdfceSMircea Gherzan case BPF_S_MISC_TAX: 756ddecdfceSMircea Gherzan /* X = A */ 757ddecdfceSMircea Gherzan ctx->seen |= SEEN_X; 758ddecdfceSMircea Gherzan emit(ARM_MOV_R(r_X, r_A), ctx); 759ddecdfceSMircea Gherzan break; 760ddecdfceSMircea Gherzan case BPF_S_MISC_TXA: 761ddecdfceSMircea Gherzan /* A = X */ 762ddecdfceSMircea Gherzan update_on_xread(ctx); 763ddecdfceSMircea Gherzan emit(ARM_MOV_R(r_A, r_X), ctx); 764ddecdfceSMircea Gherzan break; 7652bea29b7SMircea Gherzan case BPF_S_ANC_ALU_XOR_X: 7662bea29b7SMircea Gherzan /* A ^= X */ 7672bea29b7SMircea Gherzan update_on_xread(ctx); 7682bea29b7SMircea Gherzan emit(ARM_EOR_R(r_A, r_A, r_X), ctx); 7692bea29b7SMircea Gherzan break; 770ddecdfceSMircea Gherzan case BPF_S_ANC_PROTOCOL: 771ddecdfceSMircea Gherzan /* A = ntohs(skb->protocol) */ 772ddecdfceSMircea Gherzan ctx->seen |= SEEN_SKB; 773ddecdfceSMircea Gherzan BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, 774ddecdfceSMircea Gherzan protocol) != 2); 775ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, protocol); 776ddecdfceSMircea Gherzan emit(ARM_LDRH_I(r_scratch, r_skb, off), ctx); 777ddecdfceSMircea Gherzan emit_swap16(r_A, r_scratch, ctx); 778ddecdfceSMircea Gherzan break; 779ddecdfceSMircea Gherzan case BPF_S_ANC_CPU: 780ddecdfceSMircea Gherzan /* r_scratch = current_thread_info() */ 781ddecdfceSMircea Gherzan OP_IMM3(ARM_BIC, r_scratch, ARM_SP, THREAD_SIZE - 1, ctx); 782ddecdfceSMircea Gherzan /* A = current_thread_info()->cpu */ 783ddecdfceSMircea Gherzan BUILD_BUG_ON(FIELD_SIZEOF(struct thread_info, cpu) != 4); 784ddecdfceSMircea Gherzan off = offsetof(struct thread_info, cpu); 785ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_A, r_scratch, off), ctx); 786ddecdfceSMircea Gherzan break; 787ddecdfceSMircea Gherzan case BPF_S_ANC_IFINDEX: 788ddecdfceSMircea Gherzan /* A = skb->dev->ifindex */ 789ddecdfceSMircea Gherzan ctx->seen |= SEEN_SKB; 790ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, dev); 791ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_scratch, r_skb, off), ctx); 792ddecdfceSMircea Gherzan 793ddecdfceSMircea Gherzan emit(ARM_CMP_I(r_scratch, 0), ctx); 794ddecdfceSMircea Gherzan emit_err_ret(ARM_COND_EQ, ctx); 795ddecdfceSMircea Gherzan 796ddecdfceSMircea Gherzan BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, 797ddecdfceSMircea Gherzan ifindex) != 4); 798ddecdfceSMircea Gherzan off = offsetof(struct net_device, ifindex); 799ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_A, r_scratch, off), ctx); 800ddecdfceSMircea Gherzan break; 801ddecdfceSMircea Gherzan case BPF_S_ANC_MARK: 802ddecdfceSMircea Gherzan ctx->seen |= SEEN_SKB; 803ddecdfceSMircea Gherzan BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); 804ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, mark); 805ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_A, r_skb, off), ctx); 806ddecdfceSMircea Gherzan break; 807ddecdfceSMircea Gherzan case BPF_S_ANC_RXHASH: 808ddecdfceSMircea Gherzan ctx->seen |= SEEN_SKB; 809ddecdfceSMircea Gherzan BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, rxhash) != 4); 810ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, rxhash); 811ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_A, r_skb, off), ctx); 812ddecdfceSMircea Gherzan break; 813ddecdfceSMircea Gherzan case BPF_S_ANC_QUEUE: 814ddecdfceSMircea Gherzan ctx->seen |= SEEN_SKB; 815ddecdfceSMircea Gherzan BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, 816ddecdfceSMircea Gherzan queue_mapping) != 2); 817ddecdfceSMircea Gherzan BUILD_BUG_ON(offsetof(struct sk_buff, 818ddecdfceSMircea Gherzan queue_mapping) > 0xff); 819ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, queue_mapping); 820ddecdfceSMircea Gherzan emit(ARM_LDRH_I(r_A, r_skb, off), ctx); 821ddecdfceSMircea Gherzan break; 822ddecdfceSMircea Gherzan default: 823ddecdfceSMircea Gherzan return -1; 824ddecdfceSMircea Gherzan } 825ddecdfceSMircea Gherzan } 826ddecdfceSMircea Gherzan 827ddecdfceSMircea Gherzan /* compute offsets only during the first pass */ 828ddecdfceSMircea Gherzan if (ctx->target == NULL) 829ddecdfceSMircea Gherzan ctx->offsets[i] = ctx->idx * 4; 830ddecdfceSMircea Gherzan 831ddecdfceSMircea Gherzan return 0; 832ddecdfceSMircea Gherzan } 833ddecdfceSMircea Gherzan 834ddecdfceSMircea Gherzan 835ddecdfceSMircea Gherzan void bpf_jit_compile(struct sk_filter *fp) 836ddecdfceSMircea Gherzan { 837ddecdfceSMircea Gherzan struct jit_ctx ctx; 838ddecdfceSMircea Gherzan unsigned tmp_idx; 839ddecdfceSMircea Gherzan unsigned alloc_size; 840ddecdfceSMircea Gherzan 841ddecdfceSMircea Gherzan if (!bpf_jit_enable) 842ddecdfceSMircea Gherzan return; 843ddecdfceSMircea Gherzan 844ddecdfceSMircea Gherzan memset(&ctx, 0, sizeof(ctx)); 845ddecdfceSMircea Gherzan ctx.skf = fp; 846ddecdfceSMircea Gherzan ctx.ret0_fp_idx = -1; 847ddecdfceSMircea Gherzan 84889c2e009SSchichan Nicolas ctx.offsets = kzalloc(4 * (ctx.skf->len + 1), GFP_KERNEL); 849ddecdfceSMircea Gherzan if (ctx.offsets == NULL) 850ddecdfceSMircea Gherzan return; 851ddecdfceSMircea Gherzan 852ddecdfceSMircea Gherzan /* fake pass to fill in the ctx->seen */ 853ddecdfceSMircea Gherzan if (unlikely(build_body(&ctx))) 854ddecdfceSMircea Gherzan goto out; 855ddecdfceSMircea Gherzan 856ddecdfceSMircea Gherzan tmp_idx = ctx.idx; 857ddecdfceSMircea Gherzan build_prologue(&ctx); 858ddecdfceSMircea Gherzan ctx.prologue_bytes = (ctx.idx - tmp_idx) * 4; 859ddecdfceSMircea Gherzan 860ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ < 7 861ddecdfceSMircea Gherzan tmp_idx = ctx.idx; 862ddecdfceSMircea Gherzan build_epilogue(&ctx); 863ddecdfceSMircea Gherzan ctx.epilogue_bytes = (ctx.idx - tmp_idx) * 4; 864ddecdfceSMircea Gherzan 865ddecdfceSMircea Gherzan ctx.idx += ctx.imm_count; 866ddecdfceSMircea Gherzan if (ctx.imm_count) { 86789c2e009SSchichan Nicolas ctx.imms = kzalloc(4 * ctx.imm_count, GFP_KERNEL); 868ddecdfceSMircea Gherzan if (ctx.imms == NULL) 869ddecdfceSMircea Gherzan goto out; 870ddecdfceSMircea Gherzan } 871ddecdfceSMircea Gherzan #else 872ddecdfceSMircea Gherzan /* there's nothing after the epilogue on ARMv7 */ 873ddecdfceSMircea Gherzan build_epilogue(&ctx); 874ddecdfceSMircea Gherzan #endif 875ddecdfceSMircea Gherzan 876ddecdfceSMircea Gherzan alloc_size = 4 * ctx.idx; 877ddecdfceSMircea Gherzan ctx.target = module_alloc(max(sizeof(struct work_struct), 878ddecdfceSMircea Gherzan alloc_size)); 879ddecdfceSMircea Gherzan if (unlikely(ctx.target == NULL)) 880ddecdfceSMircea Gherzan goto out; 881ddecdfceSMircea Gherzan 882ddecdfceSMircea Gherzan ctx.idx = 0; 883ddecdfceSMircea Gherzan build_prologue(&ctx); 884ddecdfceSMircea Gherzan build_body(&ctx); 885ddecdfceSMircea Gherzan build_epilogue(&ctx); 886ddecdfceSMircea Gherzan 887ddecdfceSMircea Gherzan flush_icache_range((u32)ctx.target, (u32)(ctx.target + ctx.idx)); 888ddecdfceSMircea Gherzan 889ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ < 7 890ddecdfceSMircea Gherzan if (ctx.imm_count) 891ddecdfceSMircea Gherzan kfree(ctx.imms); 892ddecdfceSMircea Gherzan #endif 893ddecdfceSMircea Gherzan 894ddecdfceSMircea Gherzan if (bpf_jit_enable > 1) 895ddecdfceSMircea Gherzan print_hex_dump(KERN_INFO, "BPF JIT code: ", 896ddecdfceSMircea Gherzan DUMP_PREFIX_ADDRESS, 16, 4, ctx.target, 897ddecdfceSMircea Gherzan alloc_size, false); 898ddecdfceSMircea Gherzan 899ddecdfceSMircea Gherzan fp->bpf_func = (void *)ctx.target; 900ddecdfceSMircea Gherzan out: 901ddecdfceSMircea Gherzan kfree(ctx.offsets); 902ddecdfceSMircea Gherzan return; 903ddecdfceSMircea Gherzan } 904ddecdfceSMircea Gherzan 905ddecdfceSMircea Gherzan static void bpf_jit_free_worker(struct work_struct *work) 906ddecdfceSMircea Gherzan { 907ddecdfceSMircea Gherzan module_free(NULL, work); 908ddecdfceSMircea Gherzan } 909ddecdfceSMircea Gherzan 910ddecdfceSMircea Gherzan void bpf_jit_free(struct sk_filter *fp) 911ddecdfceSMircea Gherzan { 912ddecdfceSMircea Gherzan struct work_struct *work; 913ddecdfceSMircea Gherzan 914ddecdfceSMircea Gherzan if (fp->bpf_func != sk_run_filter) { 915ddecdfceSMircea Gherzan work = (struct work_struct *)fp->bpf_func; 916ddecdfceSMircea Gherzan 917ddecdfceSMircea Gherzan INIT_WORK(work, bpf_jit_free_worker); 918ddecdfceSMircea Gherzan schedule_work(work); 919ddecdfceSMircea Gherzan } 920ddecdfceSMircea Gherzan } 921