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/netdevice.h> 16ddecdfceSMircea Gherzan #include <linux/string.h> 17ddecdfceSMircea Gherzan #include <linux/slab.h> 18bf0098f2SDaniel Borkmann #include <linux/if_vlan.h> 19ddecdfceSMircea Gherzan #include <asm/cacheflush.h> 20ddecdfceSMircea Gherzan #include <asm/hwcap.h> 213460743eSBen Dooks #include <asm/opcodes.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 { 587ae457c1SAlexei Starovoitov const struct bpf_prog *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 { 1163460743eSBen Dooks inst |= (cond << 28); 1173460743eSBen Dooks inst = __opcode_to_mem_arm(inst); 1183460743eSBen Dooks 119ddecdfceSMircea Gherzan if (ctx->target != NULL) 1203460743eSBen Dooks ctx->target[ctx->idx] = inst; 121ddecdfceSMircea Gherzan 122ddecdfceSMircea Gherzan ctx->idx++; 123ddecdfceSMircea Gherzan } 124ddecdfceSMircea Gherzan 125ddecdfceSMircea Gherzan /* 126ddecdfceSMircea Gherzan * Emit an instruction that will be executed unconditionally. 127ddecdfceSMircea Gherzan */ 128ddecdfceSMircea Gherzan static inline void emit(u32 inst, struct jit_ctx *ctx) 129ddecdfceSMircea Gherzan { 130ddecdfceSMircea Gherzan _emit(ARM_COND_AL, inst, ctx); 131ddecdfceSMircea Gherzan } 132ddecdfceSMircea Gherzan 133ddecdfceSMircea Gherzan static u16 saved_regs(struct jit_ctx *ctx) 134ddecdfceSMircea Gherzan { 135ddecdfceSMircea Gherzan u16 ret = 0; 136ddecdfceSMircea Gherzan 137ddecdfceSMircea Gherzan if ((ctx->skf->len > 1) || 13834805931SDaniel Borkmann (ctx->skf->insns[0].code == (BPF_RET | BPF_A))) 139ddecdfceSMircea Gherzan ret |= 1 << r_A; 140ddecdfceSMircea Gherzan 141ddecdfceSMircea Gherzan #ifdef CONFIG_FRAME_POINTER 142ddecdfceSMircea Gherzan ret |= (1 << ARM_FP) | (1 << ARM_IP) | (1 << ARM_LR) | (1 << ARM_PC); 143ddecdfceSMircea Gherzan #else 144ddecdfceSMircea Gherzan if (ctx->seen & SEEN_CALL) 145ddecdfceSMircea Gherzan ret |= 1 << ARM_LR; 146ddecdfceSMircea Gherzan #endif 147ddecdfceSMircea Gherzan if (ctx->seen & (SEEN_DATA | SEEN_SKB)) 148ddecdfceSMircea Gherzan ret |= 1 << r_skb; 149ddecdfceSMircea Gherzan if (ctx->seen & SEEN_DATA) 150ddecdfceSMircea Gherzan ret |= (1 << r_skb_data) | (1 << r_skb_hl); 151ddecdfceSMircea Gherzan if (ctx->seen & SEEN_X) 152ddecdfceSMircea Gherzan ret |= 1 << r_X; 153ddecdfceSMircea Gherzan 154ddecdfceSMircea Gherzan return ret; 155ddecdfceSMircea Gherzan } 156ddecdfceSMircea Gherzan 157ddecdfceSMircea Gherzan static inline int mem_words_used(struct jit_ctx *ctx) 158ddecdfceSMircea Gherzan { 159ddecdfceSMircea Gherzan /* yes, we do waste some stack space IF there are "holes" in the set" */ 160ddecdfceSMircea Gherzan return fls(ctx->seen & SEEN_MEM); 161ddecdfceSMircea Gherzan } 162ddecdfceSMircea Gherzan 163ddecdfceSMircea Gherzan static inline bool is_load_to_a(u16 inst) 164ddecdfceSMircea Gherzan { 165ddecdfceSMircea Gherzan switch (inst) { 16634805931SDaniel Borkmann case BPF_LD | BPF_W | BPF_LEN: 16734805931SDaniel Borkmann case BPF_LD | BPF_W | BPF_ABS: 16834805931SDaniel Borkmann case BPF_LD | BPF_H | BPF_ABS: 16934805931SDaniel Borkmann case BPF_LD | BPF_B | BPF_ABS: 170ddecdfceSMircea Gherzan return true; 171ddecdfceSMircea Gherzan default: 172ddecdfceSMircea Gherzan return false; 173ddecdfceSMircea Gherzan } 174ddecdfceSMircea Gherzan } 175ddecdfceSMircea Gherzan 176*55309dd3SDaniel Borkmann static void jit_fill_hole(void *area, unsigned int size) 177*55309dd3SDaniel Borkmann { 178*55309dd3SDaniel Borkmann /* Insert illegal UND instructions. */ 179*55309dd3SDaniel Borkmann u32 *ptr, fill_ins = 0xe7ffffff; 180*55309dd3SDaniel Borkmann /* We are guaranteed to have aligned memory. */ 181*55309dd3SDaniel Borkmann for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 182*55309dd3SDaniel Borkmann *ptr++ = fill_ins; 183*55309dd3SDaniel Borkmann } 184*55309dd3SDaniel Borkmann 185ddecdfceSMircea Gherzan static void build_prologue(struct jit_ctx *ctx) 186ddecdfceSMircea Gherzan { 187ddecdfceSMircea Gherzan u16 reg_set = saved_regs(ctx); 188ddecdfceSMircea Gherzan u16 first_inst = ctx->skf->insns[0].code; 189ddecdfceSMircea Gherzan u16 off; 190ddecdfceSMircea Gherzan 191ddecdfceSMircea Gherzan #ifdef CONFIG_FRAME_POINTER 192ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_IP, ARM_SP), ctx); 193ddecdfceSMircea Gherzan emit(ARM_PUSH(reg_set), ctx); 194ddecdfceSMircea Gherzan emit(ARM_SUB_I(ARM_FP, ARM_IP, 4), ctx); 195ddecdfceSMircea Gherzan #else 196ddecdfceSMircea Gherzan if (reg_set) 197ddecdfceSMircea Gherzan emit(ARM_PUSH(reg_set), ctx); 198ddecdfceSMircea Gherzan #endif 199ddecdfceSMircea Gherzan 200ddecdfceSMircea Gherzan if (ctx->seen & (SEEN_DATA | SEEN_SKB)) 201ddecdfceSMircea Gherzan emit(ARM_MOV_R(r_skb, ARM_R0), ctx); 202ddecdfceSMircea Gherzan 203ddecdfceSMircea Gherzan if (ctx->seen & SEEN_DATA) { 204ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, data); 205ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_skb_data, r_skb, off), ctx); 206ddecdfceSMircea Gherzan /* headlen = len - data_len */ 207ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, len); 208ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_skb_hl, r_skb, off), ctx); 209ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, data_len); 210ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_scratch, r_skb, off), ctx); 211ddecdfceSMircea Gherzan emit(ARM_SUB_R(r_skb_hl, r_skb_hl, r_scratch), ctx); 212ddecdfceSMircea Gherzan } 213ddecdfceSMircea Gherzan 214ddecdfceSMircea Gherzan if (ctx->flags & FLAG_NEED_X_RESET) 215ddecdfceSMircea Gherzan emit(ARM_MOV_I(r_X, 0), ctx); 216ddecdfceSMircea Gherzan 217ddecdfceSMircea Gherzan /* do not leak kernel data to userspace */ 21834805931SDaniel Borkmann if ((first_inst != (BPF_RET | BPF_K)) && !(is_load_to_a(first_inst))) 219ddecdfceSMircea Gherzan emit(ARM_MOV_I(r_A, 0), ctx); 220ddecdfceSMircea Gherzan 221ddecdfceSMircea Gherzan /* stack space for the BPF_MEM words */ 222ddecdfceSMircea Gherzan if (ctx->seen & SEEN_MEM) 223ddecdfceSMircea Gherzan emit(ARM_SUB_I(ARM_SP, ARM_SP, mem_words_used(ctx) * 4), ctx); 224ddecdfceSMircea Gherzan } 225ddecdfceSMircea Gherzan 226ddecdfceSMircea Gherzan static void build_epilogue(struct jit_ctx *ctx) 227ddecdfceSMircea Gherzan { 228ddecdfceSMircea Gherzan u16 reg_set = saved_regs(ctx); 229ddecdfceSMircea Gherzan 230ddecdfceSMircea Gherzan if (ctx->seen & SEEN_MEM) 231ddecdfceSMircea Gherzan emit(ARM_ADD_I(ARM_SP, ARM_SP, mem_words_used(ctx) * 4), ctx); 232ddecdfceSMircea Gherzan 233ddecdfceSMircea Gherzan reg_set &= ~(1 << ARM_LR); 234ddecdfceSMircea Gherzan 235ddecdfceSMircea Gherzan #ifdef CONFIG_FRAME_POINTER 236ddecdfceSMircea Gherzan /* the first instruction of the prologue was: mov ip, sp */ 237ddecdfceSMircea Gherzan reg_set &= ~(1 << ARM_IP); 238ddecdfceSMircea Gherzan reg_set |= (1 << ARM_SP); 239ddecdfceSMircea Gherzan emit(ARM_LDM(ARM_SP, reg_set), ctx); 240ddecdfceSMircea Gherzan #else 241ddecdfceSMircea Gherzan if (reg_set) { 242ddecdfceSMircea Gherzan if (ctx->seen & SEEN_CALL) 243ddecdfceSMircea Gherzan reg_set |= 1 << ARM_PC; 244ddecdfceSMircea Gherzan emit(ARM_POP(reg_set), ctx); 245ddecdfceSMircea Gherzan } 246ddecdfceSMircea Gherzan 247ddecdfceSMircea Gherzan if (!(ctx->seen & SEEN_CALL)) 248ddecdfceSMircea Gherzan emit(ARM_BX(ARM_LR), ctx); 249ddecdfceSMircea Gherzan #endif 250ddecdfceSMircea Gherzan } 251ddecdfceSMircea Gherzan 252ddecdfceSMircea Gherzan static int16_t imm8m(u32 x) 253ddecdfceSMircea Gherzan { 254ddecdfceSMircea Gherzan u32 rot; 255ddecdfceSMircea Gherzan 256ddecdfceSMircea Gherzan for (rot = 0; rot < 16; rot++) 257ddecdfceSMircea Gherzan if ((x & ~ror32(0xff, 2 * rot)) == 0) 258ddecdfceSMircea Gherzan return rol32(x, 2 * rot) | (rot << 8); 259ddecdfceSMircea Gherzan 260ddecdfceSMircea Gherzan return -1; 261ddecdfceSMircea Gherzan } 262ddecdfceSMircea Gherzan 263ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ < 7 264ddecdfceSMircea Gherzan 265ddecdfceSMircea Gherzan static u16 imm_offset(u32 k, struct jit_ctx *ctx) 266ddecdfceSMircea Gherzan { 267ddecdfceSMircea Gherzan unsigned i = 0, offset; 268ddecdfceSMircea Gherzan u16 imm; 269ddecdfceSMircea Gherzan 270ddecdfceSMircea Gherzan /* on the "fake" run we just count them (duplicates included) */ 271ddecdfceSMircea Gherzan if (ctx->target == NULL) { 272ddecdfceSMircea Gherzan ctx->imm_count++; 273ddecdfceSMircea Gherzan return 0; 274ddecdfceSMircea Gherzan } 275ddecdfceSMircea Gherzan 276ddecdfceSMircea Gherzan while ((i < ctx->imm_count) && ctx->imms[i]) { 277ddecdfceSMircea Gherzan if (ctx->imms[i] == k) 278ddecdfceSMircea Gherzan break; 279ddecdfceSMircea Gherzan i++; 280ddecdfceSMircea Gherzan } 281ddecdfceSMircea Gherzan 282ddecdfceSMircea Gherzan if (ctx->imms[i] == 0) 283ddecdfceSMircea Gherzan ctx->imms[i] = k; 284ddecdfceSMircea Gherzan 285ddecdfceSMircea Gherzan /* constants go just after the epilogue */ 286ddecdfceSMircea Gherzan offset = ctx->offsets[ctx->skf->len]; 287ddecdfceSMircea Gherzan offset += ctx->prologue_bytes; 288ddecdfceSMircea Gherzan offset += ctx->epilogue_bytes; 289ddecdfceSMircea Gherzan offset += i * 4; 290ddecdfceSMircea Gherzan 291ddecdfceSMircea Gherzan ctx->target[offset / 4] = k; 292ddecdfceSMircea Gherzan 293ddecdfceSMircea Gherzan /* PC in ARM mode == address of the instruction + 8 */ 294ddecdfceSMircea Gherzan imm = offset - (8 + ctx->idx * 4); 295ddecdfceSMircea Gherzan 296ddecdfceSMircea Gherzan return imm; 297ddecdfceSMircea Gherzan } 298ddecdfceSMircea Gherzan 299ddecdfceSMircea Gherzan #endif /* __LINUX_ARM_ARCH__ */ 300ddecdfceSMircea Gherzan 301ddecdfceSMircea Gherzan /* 302ddecdfceSMircea Gherzan * Move an immediate that's not an imm8m to a core register. 303ddecdfceSMircea Gherzan */ 304ddecdfceSMircea Gherzan static inline void emit_mov_i_no8m(int rd, u32 val, struct jit_ctx *ctx) 305ddecdfceSMircea Gherzan { 306ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ < 7 307ddecdfceSMircea Gherzan emit(ARM_LDR_I(rd, ARM_PC, imm_offset(val, ctx)), ctx); 308ddecdfceSMircea Gherzan #else 309ddecdfceSMircea Gherzan emit(ARM_MOVW(rd, val & 0xffff), ctx); 310ddecdfceSMircea Gherzan if (val > 0xffff) 311ddecdfceSMircea Gherzan emit(ARM_MOVT(rd, val >> 16), ctx); 312ddecdfceSMircea Gherzan #endif 313ddecdfceSMircea Gherzan } 314ddecdfceSMircea Gherzan 315ddecdfceSMircea Gherzan static inline void emit_mov_i(int rd, u32 val, struct jit_ctx *ctx) 316ddecdfceSMircea Gherzan { 317ddecdfceSMircea Gherzan int imm12 = imm8m(val); 318ddecdfceSMircea Gherzan 319ddecdfceSMircea Gherzan if (imm12 >= 0) 320ddecdfceSMircea Gherzan emit(ARM_MOV_I(rd, imm12), ctx); 321ddecdfceSMircea Gherzan else 322ddecdfceSMircea Gherzan emit_mov_i_no8m(rd, val, ctx); 323ddecdfceSMircea Gherzan } 324ddecdfceSMircea Gherzan 325ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ < 6 326ddecdfceSMircea Gherzan 327ddecdfceSMircea Gherzan static void emit_load_be32(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx) 328ddecdfceSMircea Gherzan { 329ddecdfceSMircea Gherzan _emit(cond, ARM_LDRB_I(ARM_R3, r_addr, 1), ctx); 330ddecdfceSMircea Gherzan _emit(cond, ARM_LDRB_I(ARM_R1, r_addr, 0), ctx); 331ddecdfceSMircea Gherzan _emit(cond, ARM_LDRB_I(ARM_R2, r_addr, 3), ctx); 332ddecdfceSMircea Gherzan _emit(cond, ARM_LSL_I(ARM_R3, ARM_R3, 16), ctx); 333ddecdfceSMircea Gherzan _emit(cond, ARM_LDRB_I(ARM_R0, r_addr, 2), ctx); 334ddecdfceSMircea Gherzan _emit(cond, ARM_ORR_S(ARM_R3, ARM_R3, ARM_R1, SRTYPE_LSL, 24), ctx); 335ddecdfceSMircea Gherzan _emit(cond, ARM_ORR_R(ARM_R3, ARM_R3, ARM_R2), ctx); 336ddecdfceSMircea Gherzan _emit(cond, ARM_ORR_S(r_res, ARM_R3, ARM_R0, SRTYPE_LSL, 8), ctx); 337ddecdfceSMircea Gherzan } 338ddecdfceSMircea Gherzan 339ddecdfceSMircea Gherzan static void emit_load_be16(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx) 340ddecdfceSMircea Gherzan { 341ddecdfceSMircea Gherzan _emit(cond, ARM_LDRB_I(ARM_R1, r_addr, 0), ctx); 342ddecdfceSMircea Gherzan _emit(cond, ARM_LDRB_I(ARM_R2, r_addr, 1), ctx); 343ddecdfceSMircea Gherzan _emit(cond, ARM_ORR_S(r_res, ARM_R2, ARM_R1, SRTYPE_LSL, 8), ctx); 344ddecdfceSMircea Gherzan } 345ddecdfceSMircea Gherzan 346ddecdfceSMircea Gherzan static inline void emit_swap16(u8 r_dst, u8 r_src, struct jit_ctx *ctx) 347ddecdfceSMircea Gherzan { 348462738f4SNicolas Schichan /* r_dst = (r_src << 8) | (r_src >> 8) */ 349462738f4SNicolas Schichan emit(ARM_LSL_I(ARM_R1, r_src, 8), ctx); 350462738f4SNicolas Schichan emit(ARM_ORR_S(r_dst, ARM_R1, r_src, SRTYPE_LSR, 8), ctx); 351462738f4SNicolas Schichan 352462738f4SNicolas Schichan /* 353462738f4SNicolas Schichan * we need to mask out the bits set in r_dst[23:16] due to 354462738f4SNicolas Schichan * the first shift instruction. 355462738f4SNicolas Schichan * 356462738f4SNicolas Schichan * note that 0x8ff is the encoded immediate 0x00ff0000. 357462738f4SNicolas Schichan */ 358462738f4SNicolas Schichan emit(ARM_BIC_I(r_dst, r_dst, 0x8ff), ctx); 359ddecdfceSMircea Gherzan } 360ddecdfceSMircea Gherzan 361ddecdfceSMircea Gherzan #else /* ARMv6+ */ 362ddecdfceSMircea Gherzan 363ddecdfceSMircea Gherzan static void emit_load_be32(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx) 364ddecdfceSMircea Gherzan { 365ddecdfceSMircea Gherzan _emit(cond, ARM_LDR_I(r_res, r_addr, 0), ctx); 366ddecdfceSMircea Gherzan #ifdef __LITTLE_ENDIAN 367ddecdfceSMircea Gherzan _emit(cond, ARM_REV(r_res, r_res), ctx); 368ddecdfceSMircea Gherzan #endif 369ddecdfceSMircea Gherzan } 370ddecdfceSMircea Gherzan 371ddecdfceSMircea Gherzan static void emit_load_be16(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx) 372ddecdfceSMircea Gherzan { 373ddecdfceSMircea Gherzan _emit(cond, ARM_LDRH_I(r_res, r_addr, 0), ctx); 374ddecdfceSMircea Gherzan #ifdef __LITTLE_ENDIAN 375ddecdfceSMircea Gherzan _emit(cond, ARM_REV16(r_res, r_res), ctx); 376ddecdfceSMircea Gherzan #endif 377ddecdfceSMircea Gherzan } 378ddecdfceSMircea Gherzan 379ddecdfceSMircea Gherzan static inline void emit_swap16(u8 r_dst __maybe_unused, 380ddecdfceSMircea Gherzan u8 r_src __maybe_unused, 381ddecdfceSMircea Gherzan struct jit_ctx *ctx __maybe_unused) 382ddecdfceSMircea Gherzan { 383ddecdfceSMircea Gherzan #ifdef __LITTLE_ENDIAN 384ddecdfceSMircea Gherzan emit(ARM_REV16(r_dst, r_src), ctx); 385ddecdfceSMircea Gherzan #endif 386ddecdfceSMircea Gherzan } 387ddecdfceSMircea Gherzan 388ddecdfceSMircea Gherzan #endif /* __LINUX_ARM_ARCH__ < 6 */ 389ddecdfceSMircea Gherzan 390ddecdfceSMircea Gherzan 391ddecdfceSMircea Gherzan /* Compute the immediate value for a PC-relative branch. */ 392ddecdfceSMircea Gherzan static inline u32 b_imm(unsigned tgt, struct jit_ctx *ctx) 393ddecdfceSMircea Gherzan { 394ddecdfceSMircea Gherzan u32 imm; 395ddecdfceSMircea Gherzan 396ddecdfceSMircea Gherzan if (ctx->target == NULL) 397ddecdfceSMircea Gherzan return 0; 398ddecdfceSMircea Gherzan /* 399ddecdfceSMircea Gherzan * BPF allows only forward jumps and the offset of the target is 400ddecdfceSMircea Gherzan * still the one computed during the first pass. 401ddecdfceSMircea Gherzan */ 402ddecdfceSMircea Gherzan imm = ctx->offsets[tgt] + ctx->prologue_bytes - (ctx->idx * 4 + 8); 403ddecdfceSMircea Gherzan 404ddecdfceSMircea Gherzan return imm >> 2; 405ddecdfceSMircea Gherzan } 406ddecdfceSMircea Gherzan 407ddecdfceSMircea Gherzan #define OP_IMM3(op, r1, r2, imm_val, ctx) \ 408ddecdfceSMircea Gherzan do { \ 409ddecdfceSMircea Gherzan imm12 = imm8m(imm_val); \ 410ddecdfceSMircea Gherzan if (imm12 < 0) { \ 411ddecdfceSMircea Gherzan emit_mov_i_no8m(r_scratch, imm_val, ctx); \ 412ddecdfceSMircea Gherzan emit(op ## _R((r1), (r2), r_scratch), ctx); \ 413ddecdfceSMircea Gherzan } else { \ 414ddecdfceSMircea Gherzan emit(op ## _I((r1), (r2), imm12), ctx); \ 415ddecdfceSMircea Gherzan } \ 416ddecdfceSMircea Gherzan } while (0) 417ddecdfceSMircea Gherzan 418ddecdfceSMircea Gherzan static inline void emit_err_ret(u8 cond, struct jit_ctx *ctx) 419ddecdfceSMircea Gherzan { 420ddecdfceSMircea Gherzan if (ctx->ret0_fp_idx >= 0) { 421ddecdfceSMircea Gherzan _emit(cond, ARM_B(b_imm(ctx->ret0_fp_idx, ctx)), ctx); 422ddecdfceSMircea Gherzan /* NOP to keep the size constant between passes */ 423ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_R0, ARM_R0), ctx); 424ddecdfceSMircea Gherzan } else { 425ddecdfceSMircea Gherzan _emit(cond, ARM_MOV_I(ARM_R0, 0), ctx); 426ddecdfceSMircea Gherzan _emit(cond, ARM_B(b_imm(ctx->skf->len, ctx)), ctx); 427ddecdfceSMircea Gherzan } 428ddecdfceSMircea Gherzan } 429ddecdfceSMircea Gherzan 430ddecdfceSMircea Gherzan static inline void emit_blx_r(u8 tgt_reg, struct jit_ctx *ctx) 431ddecdfceSMircea Gherzan { 432ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ < 5 433ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_LR, ARM_PC), ctx); 434ddecdfceSMircea Gherzan 435ddecdfceSMircea Gherzan if (elf_hwcap & HWCAP_THUMB) 436ddecdfceSMircea Gherzan emit(ARM_BX(tgt_reg), ctx); 437ddecdfceSMircea Gherzan else 438ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_PC, tgt_reg), ctx); 439ddecdfceSMircea Gherzan #else 440ddecdfceSMircea Gherzan emit(ARM_BLX_R(tgt_reg), ctx); 441ddecdfceSMircea Gherzan #endif 442ddecdfceSMircea Gherzan } 443ddecdfceSMircea Gherzan 444ddecdfceSMircea Gherzan static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx) 445ddecdfceSMircea Gherzan { 446ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ == 7 447ddecdfceSMircea Gherzan if (elf_hwcap & HWCAP_IDIVA) { 448ddecdfceSMircea Gherzan emit(ARM_UDIV(rd, rm, rn), ctx); 449ddecdfceSMircea Gherzan return; 450ddecdfceSMircea Gherzan } 451ddecdfceSMircea Gherzan #endif 452ddecdfceSMircea Gherzan if (rm != ARM_R0) 453ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_R0, rm), ctx); 454ddecdfceSMircea Gherzan if (rn != ARM_R1) 455ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_R1, rn), ctx); 456ddecdfceSMircea Gherzan 457ddecdfceSMircea Gherzan ctx->seen |= SEEN_CALL; 458ddecdfceSMircea Gherzan emit_mov_i(ARM_R3, (u32)jit_udiv, ctx); 459ddecdfceSMircea Gherzan emit_blx_r(ARM_R3, ctx); 460ddecdfceSMircea Gherzan 461ddecdfceSMircea Gherzan if (rd != ARM_R0) 462ddecdfceSMircea Gherzan emit(ARM_MOV_R(rd, ARM_R0), ctx); 463ddecdfceSMircea Gherzan } 464ddecdfceSMircea Gherzan 465ddecdfceSMircea Gherzan static inline void update_on_xread(struct jit_ctx *ctx) 466ddecdfceSMircea Gherzan { 467ddecdfceSMircea Gherzan if (!(ctx->seen & SEEN_X)) 468ddecdfceSMircea Gherzan ctx->flags |= FLAG_NEED_X_RESET; 469ddecdfceSMircea Gherzan 470ddecdfceSMircea Gherzan ctx->seen |= SEEN_X; 471ddecdfceSMircea Gherzan } 472ddecdfceSMircea Gherzan 473ddecdfceSMircea Gherzan static int build_body(struct jit_ctx *ctx) 474ddecdfceSMircea Gherzan { 475ddecdfceSMircea Gherzan void *load_func[] = {jit_get_skb_b, jit_get_skb_h, jit_get_skb_w}; 4767ae457c1SAlexei Starovoitov const struct bpf_prog *prog = ctx->skf; 477ddecdfceSMircea Gherzan const struct sock_filter *inst; 478ddecdfceSMircea Gherzan unsigned i, load_order, off, condt; 479ddecdfceSMircea Gherzan int imm12; 480ddecdfceSMircea Gherzan u32 k; 481ddecdfceSMircea Gherzan 482ddecdfceSMircea Gherzan for (i = 0; i < prog->len; i++) { 48334805931SDaniel Borkmann u16 code; 48434805931SDaniel Borkmann 485ddecdfceSMircea Gherzan inst = &(prog->insns[i]); 486ddecdfceSMircea Gherzan /* K as an immediate value operand */ 487ddecdfceSMircea Gherzan k = inst->k; 48834805931SDaniel Borkmann code = bpf_anc_helper(inst); 489ddecdfceSMircea Gherzan 490ddecdfceSMircea Gherzan /* compute offsets only in the fake pass */ 491ddecdfceSMircea Gherzan if (ctx->target == NULL) 492ddecdfceSMircea Gherzan ctx->offsets[i] = ctx->idx * 4; 493ddecdfceSMircea Gherzan 49434805931SDaniel Borkmann switch (code) { 49534805931SDaniel Borkmann case BPF_LD | BPF_IMM: 496ddecdfceSMircea Gherzan emit_mov_i(r_A, k, ctx); 497ddecdfceSMircea Gherzan break; 49834805931SDaniel Borkmann case BPF_LD | BPF_W | BPF_LEN: 499ddecdfceSMircea Gherzan ctx->seen |= SEEN_SKB; 500ddecdfceSMircea Gherzan BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4); 501ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_A, r_skb, 502ddecdfceSMircea Gherzan offsetof(struct sk_buff, len)), ctx); 503ddecdfceSMircea Gherzan break; 50434805931SDaniel Borkmann case BPF_LD | BPF_MEM: 505ddecdfceSMircea Gherzan /* A = scratch[k] */ 506ddecdfceSMircea Gherzan ctx->seen |= SEEN_MEM_WORD(k); 507ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx); 508ddecdfceSMircea Gherzan break; 50934805931SDaniel Borkmann case BPF_LD | BPF_W | BPF_ABS: 510ddecdfceSMircea Gherzan load_order = 2; 511ddecdfceSMircea Gherzan goto load; 51234805931SDaniel Borkmann case BPF_LD | BPF_H | BPF_ABS: 513ddecdfceSMircea Gherzan load_order = 1; 514ddecdfceSMircea Gherzan goto load; 51534805931SDaniel Borkmann case BPF_LD | BPF_B | BPF_ABS: 516ddecdfceSMircea Gherzan load_order = 0; 517ddecdfceSMircea Gherzan load: 518ddecdfceSMircea Gherzan /* the interpreter will deal with the negative K */ 519ddecdfceSMircea Gherzan if ((int)k < 0) 520ddecdfceSMircea Gherzan return -ENOTSUPP; 521ddecdfceSMircea Gherzan emit_mov_i(r_off, k, ctx); 522ddecdfceSMircea Gherzan load_common: 523ddecdfceSMircea Gherzan ctx->seen |= SEEN_DATA | SEEN_CALL; 524ddecdfceSMircea Gherzan 525ddecdfceSMircea Gherzan if (load_order > 0) { 526ddecdfceSMircea Gherzan emit(ARM_SUB_I(r_scratch, r_skb_hl, 527ddecdfceSMircea Gherzan 1 << load_order), ctx); 528ddecdfceSMircea Gherzan emit(ARM_CMP_R(r_scratch, r_off), ctx); 529ddecdfceSMircea Gherzan condt = ARM_COND_HS; 530ddecdfceSMircea Gherzan } else { 531ddecdfceSMircea Gherzan emit(ARM_CMP_R(r_skb_hl, r_off), ctx); 532ddecdfceSMircea Gherzan condt = ARM_COND_HI; 533ddecdfceSMircea Gherzan } 534ddecdfceSMircea Gherzan 535ddecdfceSMircea Gherzan _emit(condt, ARM_ADD_R(r_scratch, r_off, r_skb_data), 536ddecdfceSMircea Gherzan ctx); 537ddecdfceSMircea Gherzan 538ddecdfceSMircea Gherzan if (load_order == 0) 539ddecdfceSMircea Gherzan _emit(condt, ARM_LDRB_I(r_A, r_scratch, 0), 540ddecdfceSMircea Gherzan ctx); 541ddecdfceSMircea Gherzan else if (load_order == 1) 542ddecdfceSMircea Gherzan emit_load_be16(condt, r_A, r_scratch, ctx); 543ddecdfceSMircea Gherzan else if (load_order == 2) 544ddecdfceSMircea Gherzan emit_load_be32(condt, r_A, r_scratch, ctx); 545ddecdfceSMircea Gherzan 546ddecdfceSMircea Gherzan _emit(condt, ARM_B(b_imm(i + 1, ctx)), ctx); 547ddecdfceSMircea Gherzan 548ddecdfceSMircea Gherzan /* the slowpath */ 549ddecdfceSMircea Gherzan emit_mov_i(ARM_R3, (u32)load_func[load_order], ctx); 550ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_R0, r_skb), ctx); 551ddecdfceSMircea Gherzan /* the offset is already in R1 */ 552ddecdfceSMircea Gherzan emit_blx_r(ARM_R3, ctx); 553ddecdfceSMircea Gherzan /* check the result of skb_copy_bits */ 554ddecdfceSMircea Gherzan emit(ARM_CMP_I(ARM_R1, 0), ctx); 555ddecdfceSMircea Gherzan emit_err_ret(ARM_COND_NE, ctx); 556ddecdfceSMircea Gherzan emit(ARM_MOV_R(r_A, ARM_R0), ctx); 557ddecdfceSMircea Gherzan break; 55834805931SDaniel Borkmann case BPF_LD | BPF_W | BPF_IND: 559ddecdfceSMircea Gherzan load_order = 2; 560ddecdfceSMircea Gherzan goto load_ind; 56134805931SDaniel Borkmann case BPF_LD | BPF_H | BPF_IND: 562ddecdfceSMircea Gherzan load_order = 1; 563ddecdfceSMircea Gherzan goto load_ind; 56434805931SDaniel Borkmann case BPF_LD | BPF_B | BPF_IND: 565ddecdfceSMircea Gherzan load_order = 0; 566ddecdfceSMircea Gherzan load_ind: 567ddecdfceSMircea Gherzan OP_IMM3(ARM_ADD, r_off, r_X, k, ctx); 568ddecdfceSMircea Gherzan goto load_common; 56934805931SDaniel Borkmann case BPF_LDX | BPF_IMM: 570ddecdfceSMircea Gherzan ctx->seen |= SEEN_X; 571ddecdfceSMircea Gherzan emit_mov_i(r_X, k, ctx); 572ddecdfceSMircea Gherzan break; 57334805931SDaniel Borkmann case BPF_LDX | BPF_W | BPF_LEN: 574ddecdfceSMircea Gherzan ctx->seen |= SEEN_X | SEEN_SKB; 575ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_X, r_skb, 576ddecdfceSMircea Gherzan offsetof(struct sk_buff, len)), ctx); 577ddecdfceSMircea Gherzan break; 57834805931SDaniel Borkmann case BPF_LDX | BPF_MEM: 579ddecdfceSMircea Gherzan ctx->seen |= SEEN_X | SEEN_MEM_WORD(k); 580ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx); 581ddecdfceSMircea Gherzan break; 58234805931SDaniel Borkmann case BPF_LDX | BPF_B | BPF_MSH: 583ddecdfceSMircea Gherzan /* x = ((*(frame + k)) & 0xf) << 2; */ 584ddecdfceSMircea Gherzan ctx->seen |= SEEN_X | SEEN_DATA | SEEN_CALL; 585ddecdfceSMircea Gherzan /* the interpreter should deal with the negative K */ 58645549a68SChen Gang if ((int)k < 0) 587ddecdfceSMircea Gherzan return -1; 588ddecdfceSMircea Gherzan /* offset in r1: we might have to take the slow path */ 589ddecdfceSMircea Gherzan emit_mov_i(r_off, k, ctx); 590ddecdfceSMircea Gherzan emit(ARM_CMP_R(r_skb_hl, r_off), ctx); 591ddecdfceSMircea Gherzan 592ddecdfceSMircea Gherzan /* load in r0: common with the slowpath */ 593ddecdfceSMircea Gherzan _emit(ARM_COND_HI, ARM_LDRB_R(ARM_R0, r_skb_data, 594ddecdfceSMircea Gherzan ARM_R1), ctx); 595ddecdfceSMircea Gherzan /* 596ddecdfceSMircea Gherzan * emit_mov_i() might generate one or two instructions, 597ddecdfceSMircea Gherzan * the same holds for emit_blx_r() 598ddecdfceSMircea Gherzan */ 599ddecdfceSMircea Gherzan _emit(ARM_COND_HI, ARM_B(b_imm(i + 1, ctx) - 2), ctx); 600ddecdfceSMircea Gherzan 601ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_R0, r_skb), ctx); 602ddecdfceSMircea Gherzan /* r_off is r1 */ 603ddecdfceSMircea Gherzan emit_mov_i(ARM_R3, (u32)jit_get_skb_b, ctx); 604ddecdfceSMircea Gherzan emit_blx_r(ARM_R3, ctx); 605ddecdfceSMircea Gherzan /* check the return value of skb_copy_bits */ 606ddecdfceSMircea Gherzan emit(ARM_CMP_I(ARM_R1, 0), ctx); 607ddecdfceSMircea Gherzan emit_err_ret(ARM_COND_NE, ctx); 608ddecdfceSMircea Gherzan 609ddecdfceSMircea Gherzan emit(ARM_AND_I(r_X, ARM_R0, 0x00f), ctx); 610ddecdfceSMircea Gherzan emit(ARM_LSL_I(r_X, r_X, 2), ctx); 611ddecdfceSMircea Gherzan break; 61234805931SDaniel Borkmann case BPF_ST: 613ddecdfceSMircea Gherzan ctx->seen |= SEEN_MEM_WORD(k); 614ddecdfceSMircea Gherzan emit(ARM_STR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx); 615ddecdfceSMircea Gherzan break; 61634805931SDaniel Borkmann case BPF_STX: 617ddecdfceSMircea Gherzan update_on_xread(ctx); 618ddecdfceSMircea Gherzan ctx->seen |= SEEN_MEM_WORD(k); 619ddecdfceSMircea Gherzan emit(ARM_STR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx); 620ddecdfceSMircea Gherzan break; 62134805931SDaniel Borkmann case BPF_ALU | BPF_ADD | BPF_K: 622ddecdfceSMircea Gherzan /* A += K */ 623ddecdfceSMircea Gherzan OP_IMM3(ARM_ADD, r_A, r_A, k, ctx); 624ddecdfceSMircea Gherzan break; 62534805931SDaniel Borkmann case BPF_ALU | BPF_ADD | BPF_X: 626ddecdfceSMircea Gherzan update_on_xread(ctx); 627ddecdfceSMircea Gherzan emit(ARM_ADD_R(r_A, r_A, r_X), ctx); 628ddecdfceSMircea Gherzan break; 62934805931SDaniel Borkmann case BPF_ALU | BPF_SUB | BPF_K: 630ddecdfceSMircea Gherzan /* A -= K */ 631ddecdfceSMircea Gherzan OP_IMM3(ARM_SUB, r_A, r_A, k, ctx); 632ddecdfceSMircea Gherzan break; 63334805931SDaniel Borkmann case BPF_ALU | BPF_SUB | BPF_X: 634ddecdfceSMircea Gherzan update_on_xread(ctx); 635ddecdfceSMircea Gherzan emit(ARM_SUB_R(r_A, r_A, r_X), ctx); 636ddecdfceSMircea Gherzan break; 63734805931SDaniel Borkmann case BPF_ALU | BPF_MUL | BPF_K: 638ddecdfceSMircea Gherzan /* A *= K */ 639ddecdfceSMircea Gherzan emit_mov_i(r_scratch, k, ctx); 640ddecdfceSMircea Gherzan emit(ARM_MUL(r_A, r_A, r_scratch), ctx); 641ddecdfceSMircea Gherzan break; 64234805931SDaniel Borkmann case BPF_ALU | BPF_MUL | BPF_X: 643ddecdfceSMircea Gherzan update_on_xread(ctx); 644ddecdfceSMircea Gherzan emit(ARM_MUL(r_A, r_A, r_X), ctx); 645ddecdfceSMircea Gherzan break; 64634805931SDaniel Borkmann case BPF_ALU | BPF_DIV | BPF_K: 647aee636c4SEric Dumazet if (k == 1) 648aee636c4SEric Dumazet break; 649ddecdfceSMircea Gherzan emit_mov_i(r_scratch, k, ctx); 650aee636c4SEric Dumazet emit_udiv(r_A, r_A, r_scratch, ctx); 651ddecdfceSMircea Gherzan break; 65234805931SDaniel Borkmann case BPF_ALU | BPF_DIV | BPF_X: 653ddecdfceSMircea Gherzan update_on_xread(ctx); 654ddecdfceSMircea Gherzan emit(ARM_CMP_I(r_X, 0), ctx); 655ddecdfceSMircea Gherzan emit_err_ret(ARM_COND_EQ, ctx); 656ddecdfceSMircea Gherzan emit_udiv(r_A, r_A, r_X, ctx); 657ddecdfceSMircea Gherzan break; 65834805931SDaniel Borkmann case BPF_ALU | BPF_OR | BPF_K: 659ddecdfceSMircea Gherzan /* A |= K */ 660ddecdfceSMircea Gherzan OP_IMM3(ARM_ORR, r_A, r_A, k, ctx); 661ddecdfceSMircea Gherzan break; 66234805931SDaniel Borkmann case BPF_ALU | BPF_OR | BPF_X: 663ddecdfceSMircea Gherzan update_on_xread(ctx); 664ddecdfceSMircea Gherzan emit(ARM_ORR_R(r_A, r_A, r_X), ctx); 665ddecdfceSMircea Gherzan break; 66634805931SDaniel Borkmann case BPF_ALU | BPF_XOR | BPF_K: 6673cbe2041SDaniel Borkmann /* A ^= K; */ 6683cbe2041SDaniel Borkmann OP_IMM3(ARM_EOR, r_A, r_A, k, ctx); 6693cbe2041SDaniel Borkmann break; 67034805931SDaniel Borkmann case BPF_ANC | SKF_AD_ALU_XOR_X: 67134805931SDaniel Borkmann case BPF_ALU | BPF_XOR | BPF_X: 6723cbe2041SDaniel Borkmann /* A ^= X */ 6733cbe2041SDaniel Borkmann update_on_xread(ctx); 6743cbe2041SDaniel Borkmann emit(ARM_EOR_R(r_A, r_A, r_X), ctx); 6753cbe2041SDaniel Borkmann break; 67634805931SDaniel Borkmann case BPF_ALU | BPF_AND | BPF_K: 677ddecdfceSMircea Gherzan /* A &= K */ 678ddecdfceSMircea Gherzan OP_IMM3(ARM_AND, r_A, r_A, k, ctx); 679ddecdfceSMircea Gherzan break; 68034805931SDaniel Borkmann case BPF_ALU | BPF_AND | BPF_X: 681ddecdfceSMircea Gherzan update_on_xread(ctx); 682ddecdfceSMircea Gherzan emit(ARM_AND_R(r_A, r_A, r_X), ctx); 683ddecdfceSMircea Gherzan break; 68434805931SDaniel Borkmann case BPF_ALU | BPF_LSH | BPF_K: 685ddecdfceSMircea Gherzan if (unlikely(k > 31)) 686ddecdfceSMircea Gherzan return -1; 687ddecdfceSMircea Gherzan emit(ARM_LSL_I(r_A, r_A, k), ctx); 688ddecdfceSMircea Gherzan break; 68934805931SDaniel Borkmann case BPF_ALU | BPF_LSH | BPF_X: 690ddecdfceSMircea Gherzan update_on_xread(ctx); 691ddecdfceSMircea Gherzan emit(ARM_LSL_R(r_A, r_A, r_X), ctx); 692ddecdfceSMircea Gherzan break; 69334805931SDaniel Borkmann case BPF_ALU | BPF_RSH | BPF_K: 694ddecdfceSMircea Gherzan if (unlikely(k > 31)) 695ddecdfceSMircea Gherzan return -1; 696ddecdfceSMircea Gherzan emit(ARM_LSR_I(r_A, r_A, k), ctx); 697ddecdfceSMircea Gherzan break; 69834805931SDaniel Borkmann case BPF_ALU | BPF_RSH | BPF_X: 699ddecdfceSMircea Gherzan update_on_xread(ctx); 700ddecdfceSMircea Gherzan emit(ARM_LSR_R(r_A, r_A, r_X), ctx); 701ddecdfceSMircea Gherzan break; 70234805931SDaniel Borkmann case BPF_ALU | BPF_NEG: 703ddecdfceSMircea Gherzan /* A = -A */ 704ddecdfceSMircea Gherzan emit(ARM_RSB_I(r_A, r_A, 0), ctx); 705ddecdfceSMircea Gherzan break; 70634805931SDaniel Borkmann case BPF_JMP | BPF_JA: 707ddecdfceSMircea Gherzan /* pc += K */ 708ddecdfceSMircea Gherzan emit(ARM_B(b_imm(i + k + 1, ctx)), ctx); 709ddecdfceSMircea Gherzan break; 71034805931SDaniel Borkmann case BPF_JMP | BPF_JEQ | BPF_K: 711ddecdfceSMircea Gherzan /* pc += (A == K) ? pc->jt : pc->jf */ 712ddecdfceSMircea Gherzan condt = ARM_COND_EQ; 713ddecdfceSMircea Gherzan goto cmp_imm; 71434805931SDaniel Borkmann case BPF_JMP | BPF_JGT | BPF_K: 715ddecdfceSMircea Gherzan /* pc += (A > K) ? pc->jt : pc->jf */ 716ddecdfceSMircea Gherzan condt = ARM_COND_HI; 717ddecdfceSMircea Gherzan goto cmp_imm; 71834805931SDaniel Borkmann case BPF_JMP | BPF_JGE | BPF_K: 719ddecdfceSMircea Gherzan /* pc += (A >= K) ? pc->jt : pc->jf */ 720ddecdfceSMircea Gherzan condt = ARM_COND_HS; 721ddecdfceSMircea Gherzan cmp_imm: 722ddecdfceSMircea Gherzan imm12 = imm8m(k); 723ddecdfceSMircea Gherzan if (imm12 < 0) { 724ddecdfceSMircea Gherzan emit_mov_i_no8m(r_scratch, k, ctx); 725ddecdfceSMircea Gherzan emit(ARM_CMP_R(r_A, r_scratch), ctx); 726ddecdfceSMircea Gherzan } else { 727ddecdfceSMircea Gherzan emit(ARM_CMP_I(r_A, imm12), ctx); 728ddecdfceSMircea Gherzan } 729ddecdfceSMircea Gherzan cond_jump: 730ddecdfceSMircea Gherzan if (inst->jt) 731ddecdfceSMircea Gherzan _emit(condt, ARM_B(b_imm(i + inst->jt + 1, 732ddecdfceSMircea Gherzan ctx)), ctx); 733ddecdfceSMircea Gherzan if (inst->jf) 734ddecdfceSMircea Gherzan _emit(condt ^ 1, ARM_B(b_imm(i + inst->jf + 1, 735ddecdfceSMircea Gherzan ctx)), ctx); 736ddecdfceSMircea Gherzan break; 73734805931SDaniel Borkmann case BPF_JMP | BPF_JEQ | BPF_X: 738ddecdfceSMircea Gherzan /* pc += (A == X) ? pc->jt : pc->jf */ 739ddecdfceSMircea Gherzan condt = ARM_COND_EQ; 740ddecdfceSMircea Gherzan goto cmp_x; 74134805931SDaniel Borkmann case BPF_JMP | BPF_JGT | BPF_X: 742ddecdfceSMircea Gherzan /* pc += (A > X) ? pc->jt : pc->jf */ 743ddecdfceSMircea Gherzan condt = ARM_COND_HI; 744ddecdfceSMircea Gherzan goto cmp_x; 74534805931SDaniel Borkmann case BPF_JMP | BPF_JGE | BPF_X: 746ddecdfceSMircea Gherzan /* pc += (A >= X) ? pc->jt : pc->jf */ 747ddecdfceSMircea Gherzan condt = ARM_COND_CS; 748ddecdfceSMircea Gherzan cmp_x: 749ddecdfceSMircea Gherzan update_on_xread(ctx); 750ddecdfceSMircea Gherzan emit(ARM_CMP_R(r_A, r_X), ctx); 751ddecdfceSMircea Gherzan goto cond_jump; 75234805931SDaniel Borkmann case BPF_JMP | BPF_JSET | BPF_K: 753ddecdfceSMircea Gherzan /* pc += (A & K) ? pc->jt : pc->jf */ 754ddecdfceSMircea Gherzan condt = ARM_COND_NE; 755ddecdfceSMircea Gherzan /* not set iff all zeroes iff Z==1 iff EQ */ 756ddecdfceSMircea Gherzan 757ddecdfceSMircea Gherzan imm12 = imm8m(k); 758ddecdfceSMircea Gherzan if (imm12 < 0) { 759ddecdfceSMircea Gherzan emit_mov_i_no8m(r_scratch, k, ctx); 760ddecdfceSMircea Gherzan emit(ARM_TST_R(r_A, r_scratch), ctx); 761ddecdfceSMircea Gherzan } else { 762ddecdfceSMircea Gherzan emit(ARM_TST_I(r_A, imm12), ctx); 763ddecdfceSMircea Gherzan } 764ddecdfceSMircea Gherzan goto cond_jump; 76534805931SDaniel Borkmann case BPF_JMP | BPF_JSET | BPF_X: 766ddecdfceSMircea Gherzan /* pc += (A & X) ? pc->jt : pc->jf */ 767ddecdfceSMircea Gherzan update_on_xread(ctx); 768ddecdfceSMircea Gherzan condt = ARM_COND_NE; 769ddecdfceSMircea Gherzan emit(ARM_TST_R(r_A, r_X), ctx); 770ddecdfceSMircea Gherzan goto cond_jump; 77134805931SDaniel Borkmann case BPF_RET | BPF_A: 772ddecdfceSMircea Gherzan emit(ARM_MOV_R(ARM_R0, r_A), ctx); 773ddecdfceSMircea Gherzan goto b_epilogue; 77434805931SDaniel Borkmann case BPF_RET | BPF_K: 775ddecdfceSMircea Gherzan if ((k == 0) && (ctx->ret0_fp_idx < 0)) 776ddecdfceSMircea Gherzan ctx->ret0_fp_idx = i; 777ddecdfceSMircea Gherzan emit_mov_i(ARM_R0, k, ctx); 778ddecdfceSMircea Gherzan b_epilogue: 779ddecdfceSMircea Gherzan if (i != ctx->skf->len - 1) 780ddecdfceSMircea Gherzan emit(ARM_B(b_imm(prog->len, ctx)), ctx); 781ddecdfceSMircea Gherzan break; 78234805931SDaniel Borkmann case BPF_MISC | BPF_TAX: 783ddecdfceSMircea Gherzan /* X = A */ 784ddecdfceSMircea Gherzan ctx->seen |= SEEN_X; 785ddecdfceSMircea Gherzan emit(ARM_MOV_R(r_X, r_A), ctx); 786ddecdfceSMircea Gherzan break; 78734805931SDaniel Borkmann case BPF_MISC | BPF_TXA: 788ddecdfceSMircea Gherzan /* A = X */ 789ddecdfceSMircea Gherzan update_on_xread(ctx); 790ddecdfceSMircea Gherzan emit(ARM_MOV_R(r_A, r_X), ctx); 791ddecdfceSMircea Gherzan break; 79234805931SDaniel Borkmann case BPF_ANC | SKF_AD_PROTOCOL: 793ddecdfceSMircea Gherzan /* A = ntohs(skb->protocol) */ 794ddecdfceSMircea Gherzan ctx->seen |= SEEN_SKB; 795ddecdfceSMircea Gherzan BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, 796ddecdfceSMircea Gherzan protocol) != 2); 797ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, protocol); 798ddecdfceSMircea Gherzan emit(ARM_LDRH_I(r_scratch, r_skb, off), ctx); 799ddecdfceSMircea Gherzan emit_swap16(r_A, r_scratch, ctx); 800ddecdfceSMircea Gherzan break; 80134805931SDaniel Borkmann case BPF_ANC | SKF_AD_CPU: 802ddecdfceSMircea Gherzan /* r_scratch = current_thread_info() */ 803ddecdfceSMircea Gherzan OP_IMM3(ARM_BIC, r_scratch, ARM_SP, THREAD_SIZE - 1, ctx); 804ddecdfceSMircea Gherzan /* A = current_thread_info()->cpu */ 805ddecdfceSMircea Gherzan BUILD_BUG_ON(FIELD_SIZEOF(struct thread_info, cpu) != 4); 806ddecdfceSMircea Gherzan off = offsetof(struct thread_info, cpu); 807ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_A, r_scratch, off), ctx); 808ddecdfceSMircea Gherzan break; 80934805931SDaniel Borkmann case BPF_ANC | SKF_AD_IFINDEX: 810ddecdfceSMircea Gherzan /* A = skb->dev->ifindex */ 811ddecdfceSMircea Gherzan ctx->seen |= SEEN_SKB; 812ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, dev); 813ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_scratch, r_skb, off), ctx); 814ddecdfceSMircea Gherzan 815ddecdfceSMircea Gherzan emit(ARM_CMP_I(r_scratch, 0), ctx); 816ddecdfceSMircea Gherzan emit_err_ret(ARM_COND_EQ, ctx); 817ddecdfceSMircea Gherzan 818ddecdfceSMircea Gherzan BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, 819ddecdfceSMircea Gherzan ifindex) != 4); 820ddecdfceSMircea Gherzan off = offsetof(struct net_device, ifindex); 821ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_A, r_scratch, off), ctx); 822ddecdfceSMircea Gherzan break; 82334805931SDaniel Borkmann case BPF_ANC | SKF_AD_MARK: 824ddecdfceSMircea Gherzan ctx->seen |= SEEN_SKB; 825ddecdfceSMircea Gherzan BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); 826ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, mark); 827ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_A, r_skb, off), ctx); 828ddecdfceSMircea Gherzan break; 82934805931SDaniel Borkmann case BPF_ANC | SKF_AD_RXHASH: 830ddecdfceSMircea Gherzan ctx->seen |= SEEN_SKB; 83161b905daSTom Herbert BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); 83261b905daSTom Herbert off = offsetof(struct sk_buff, hash); 833ddecdfceSMircea Gherzan emit(ARM_LDR_I(r_A, r_skb, off), ctx); 834ddecdfceSMircea Gherzan break; 83534805931SDaniel Borkmann case BPF_ANC | SKF_AD_VLAN_TAG: 83634805931SDaniel Borkmann case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT: 837bf0098f2SDaniel Borkmann ctx->seen |= SEEN_SKB; 838bf0098f2SDaniel Borkmann BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); 839bf0098f2SDaniel Borkmann off = offsetof(struct sk_buff, vlan_tci); 840bf0098f2SDaniel Borkmann emit(ARM_LDRH_I(r_A, r_skb, off), ctx); 84134805931SDaniel Borkmann if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) 842bf0098f2SDaniel Borkmann OP_IMM3(ARM_AND, r_A, r_A, VLAN_VID_MASK, ctx); 843bf0098f2SDaniel Borkmann else 844bf0098f2SDaniel Borkmann OP_IMM3(ARM_AND, r_A, r_A, VLAN_TAG_PRESENT, ctx); 845bf0098f2SDaniel Borkmann break; 84634805931SDaniel Borkmann case BPF_ANC | SKF_AD_QUEUE: 847ddecdfceSMircea Gherzan ctx->seen |= SEEN_SKB; 848ddecdfceSMircea Gherzan BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, 849ddecdfceSMircea Gherzan queue_mapping) != 2); 850ddecdfceSMircea Gherzan BUILD_BUG_ON(offsetof(struct sk_buff, 851ddecdfceSMircea Gherzan queue_mapping) > 0xff); 852ddecdfceSMircea Gherzan off = offsetof(struct sk_buff, queue_mapping); 853ddecdfceSMircea Gherzan emit(ARM_LDRH_I(r_A, r_skb, off), ctx); 854ddecdfceSMircea Gherzan break; 855ddecdfceSMircea Gherzan default: 856ddecdfceSMircea Gherzan return -1; 857ddecdfceSMircea Gherzan } 858ddecdfceSMircea Gherzan } 859ddecdfceSMircea Gherzan 860ddecdfceSMircea Gherzan /* compute offsets only during the first pass */ 861ddecdfceSMircea Gherzan if (ctx->target == NULL) 862ddecdfceSMircea Gherzan ctx->offsets[i] = ctx->idx * 4; 863ddecdfceSMircea Gherzan 864ddecdfceSMircea Gherzan return 0; 865ddecdfceSMircea Gherzan } 866ddecdfceSMircea Gherzan 867ddecdfceSMircea Gherzan 8687ae457c1SAlexei Starovoitov void bpf_jit_compile(struct bpf_prog *fp) 869ddecdfceSMircea Gherzan { 870*55309dd3SDaniel Borkmann struct bpf_binary_header *header; 871ddecdfceSMircea Gherzan struct jit_ctx ctx; 872ddecdfceSMircea Gherzan unsigned tmp_idx; 873ddecdfceSMircea Gherzan unsigned alloc_size; 874*55309dd3SDaniel Borkmann u8 *target_ptr; 875ddecdfceSMircea Gherzan 876ddecdfceSMircea Gherzan if (!bpf_jit_enable) 877ddecdfceSMircea Gherzan return; 878ddecdfceSMircea Gherzan 879ddecdfceSMircea Gherzan memset(&ctx, 0, sizeof(ctx)); 880ddecdfceSMircea Gherzan ctx.skf = fp; 881ddecdfceSMircea Gherzan ctx.ret0_fp_idx = -1; 882ddecdfceSMircea Gherzan 88389c2e009SSchichan Nicolas ctx.offsets = kzalloc(4 * (ctx.skf->len + 1), GFP_KERNEL); 884ddecdfceSMircea Gherzan if (ctx.offsets == NULL) 885ddecdfceSMircea Gherzan return; 886ddecdfceSMircea Gherzan 887ddecdfceSMircea Gherzan /* fake pass to fill in the ctx->seen */ 888ddecdfceSMircea Gherzan if (unlikely(build_body(&ctx))) 889ddecdfceSMircea Gherzan goto out; 890ddecdfceSMircea Gherzan 891ddecdfceSMircea Gherzan tmp_idx = ctx.idx; 892ddecdfceSMircea Gherzan build_prologue(&ctx); 893ddecdfceSMircea Gherzan ctx.prologue_bytes = (ctx.idx - tmp_idx) * 4; 894ddecdfceSMircea Gherzan 895ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ < 7 896ddecdfceSMircea Gherzan tmp_idx = ctx.idx; 897ddecdfceSMircea Gherzan build_epilogue(&ctx); 898ddecdfceSMircea Gherzan ctx.epilogue_bytes = (ctx.idx - tmp_idx) * 4; 899ddecdfceSMircea Gherzan 900ddecdfceSMircea Gherzan ctx.idx += ctx.imm_count; 901ddecdfceSMircea Gherzan if (ctx.imm_count) { 90289c2e009SSchichan Nicolas ctx.imms = kzalloc(4 * ctx.imm_count, GFP_KERNEL); 903ddecdfceSMircea Gherzan if (ctx.imms == NULL) 904ddecdfceSMircea Gherzan goto out; 905ddecdfceSMircea Gherzan } 906ddecdfceSMircea Gherzan #else 907ddecdfceSMircea Gherzan /* there's nothing after the epilogue on ARMv7 */ 908ddecdfceSMircea Gherzan build_epilogue(&ctx); 909ddecdfceSMircea Gherzan #endif 910ddecdfceSMircea Gherzan alloc_size = 4 * ctx.idx; 911*55309dd3SDaniel Borkmann header = bpf_jit_binary_alloc(alloc_size, &target_ptr, 912*55309dd3SDaniel Borkmann 4, jit_fill_hole); 913*55309dd3SDaniel Borkmann if (header == NULL) 914ddecdfceSMircea Gherzan goto out; 915ddecdfceSMircea Gherzan 916*55309dd3SDaniel Borkmann ctx.target = (u32 *) target_ptr; 917ddecdfceSMircea Gherzan ctx.idx = 0; 918*55309dd3SDaniel Borkmann 919ddecdfceSMircea Gherzan build_prologue(&ctx); 920ddecdfceSMircea Gherzan build_body(&ctx); 921ddecdfceSMircea Gherzan build_epilogue(&ctx); 922ddecdfceSMircea Gherzan 923ddecdfceSMircea Gherzan flush_icache_range((u32)ctx.target, (u32)(ctx.target + ctx.idx)); 924ddecdfceSMircea Gherzan 925ddecdfceSMircea Gherzan #if __LINUX_ARM_ARCH__ < 7 926ddecdfceSMircea Gherzan if (ctx.imm_count) 927ddecdfceSMircea Gherzan kfree(ctx.imms); 928ddecdfceSMircea Gherzan #endif 929ddecdfceSMircea Gherzan 930ddecdfceSMircea Gherzan if (bpf_jit_enable > 1) 93179617801SDaniel Borkmann /* there are 2 passes here */ 93279617801SDaniel Borkmann bpf_jit_dump(fp->len, alloc_size, 2, ctx.target); 933ddecdfceSMircea Gherzan 934*55309dd3SDaniel Borkmann set_memory_ro((unsigned long)header, header->pages); 935ddecdfceSMircea Gherzan fp->bpf_func = (void *)ctx.target; 936f8bbbfc3SDaniel Borkmann fp->jited = 1; 937ddecdfceSMircea Gherzan out: 938ddecdfceSMircea Gherzan kfree(ctx.offsets); 939ddecdfceSMircea Gherzan return; 940ddecdfceSMircea Gherzan } 941ddecdfceSMircea Gherzan 9427ae457c1SAlexei Starovoitov void bpf_jit_free(struct bpf_prog *fp) 943ddecdfceSMircea Gherzan { 944*55309dd3SDaniel Borkmann unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK; 945*55309dd3SDaniel Borkmann struct bpf_binary_header *header = (void *)addr; 94660a3b225SDaniel Borkmann 947*55309dd3SDaniel Borkmann if (!fp->jited) 948*55309dd3SDaniel Borkmann goto free_filter; 949*55309dd3SDaniel Borkmann 950*55309dd3SDaniel Borkmann set_memory_rw(addr, header->pages); 951*55309dd3SDaniel Borkmann bpf_jit_binary_free(header); 952*55309dd3SDaniel Borkmann 953*55309dd3SDaniel Borkmann free_filter: 95460a3b225SDaniel Borkmann bpf_prog_unlock_free(fp); 955ddecdfceSMircea Gherzan } 956