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