18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* bpf_jit_comp.c: BPF JIT compiler 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2011 Matt Evans <matt@ozlabs.org>, IBM Corporation 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on the x86 BPF compiler, by Eric Dumazet (eric.dumazet@gmail.com) 78c2ecf20Sopenharmony_ci * Ported to ppc32 by Denis Kirjanov <kda@linux-powerpc.org> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/moduleloader.h> 108c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 118c2ecf20Sopenharmony_ci#include <asm/asm-compat.h> 128c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/filter.h> 148c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "bpf_jit32.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic inline void bpf_flush_icache(void *start, void *end) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci smp_wmb(); 218c2ecf20Sopenharmony_ci flush_icache_range((unsigned long)start, (unsigned long)end); 228c2ecf20Sopenharmony_ci} 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic void bpf_jit_build_prologue(struct bpf_prog *fp, u32 *image, 258c2ecf20Sopenharmony_ci struct codegen_context *ctx) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci int i; 288c2ecf20Sopenharmony_ci const struct sock_filter *filter = fp->insns; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (ctx->seen & (SEEN_MEM | SEEN_DATAREF)) { 318c2ecf20Sopenharmony_ci /* Make stackframe */ 328c2ecf20Sopenharmony_ci if (ctx->seen & SEEN_DATAREF) { 338c2ecf20Sopenharmony_ci /* If we call any helpers (for loads), save LR */ 348c2ecf20Sopenharmony_ci EMIT(PPC_INST_MFLR | __PPC_RT(R0)); 358c2ecf20Sopenharmony_ci PPC_BPF_STL(0, 1, PPC_LR_STKOFF); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci /* Back up non-volatile regs. */ 388c2ecf20Sopenharmony_ci PPC_BPF_STL(r_D, 1, -(REG_SZ*(32-r_D))); 398c2ecf20Sopenharmony_ci PPC_BPF_STL(r_HL, 1, -(REG_SZ*(32-r_HL))); 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci if (ctx->seen & SEEN_MEM) { 428c2ecf20Sopenharmony_ci /* 438c2ecf20Sopenharmony_ci * Conditionally save regs r15-r31 as some will be used 448c2ecf20Sopenharmony_ci * for M[] data. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ci for (i = r_M; i < (r_M+16); i++) { 478c2ecf20Sopenharmony_ci if (ctx->seen & (1 << (i-r_M))) 488c2ecf20Sopenharmony_ci PPC_BPF_STL(i, 1, -(REG_SZ*(32-i))); 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci PPC_BPF_STLU(1, 1, -BPF_PPC_STACKFRAME); 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (ctx->seen & SEEN_DATAREF) { 558c2ecf20Sopenharmony_ci /* 568c2ecf20Sopenharmony_ci * If this filter needs to access skb data, 578c2ecf20Sopenharmony_ci * prepare r_D and r_HL: 588c2ecf20Sopenharmony_ci * r_HL = skb->len - skb->data_len 598c2ecf20Sopenharmony_ci * r_D = skb->data 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci PPC_LWZ_OFFS(r_scratch1, r_skb, offsetof(struct sk_buff, 628c2ecf20Sopenharmony_ci data_len)); 638c2ecf20Sopenharmony_ci PPC_LWZ_OFFS(r_HL, r_skb, offsetof(struct sk_buff, len)); 648c2ecf20Sopenharmony_ci EMIT(PPC_RAW_SUB(r_HL, r_HL, r_scratch1)); 658c2ecf20Sopenharmony_ci PPC_LL_OFFS(r_D, r_skb, offsetof(struct sk_buff, data)); 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (ctx->seen & SEEN_XREG) { 698c2ecf20Sopenharmony_ci /* 708c2ecf20Sopenharmony_ci * TODO: Could also detect whether first instr. sets X and 718c2ecf20Sopenharmony_ci * avoid this (as below, with A). 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci EMIT(PPC_RAW_LI(r_X, 0)); 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* make sure we dont leak kernel information to user */ 778c2ecf20Sopenharmony_ci if (bpf_needs_clear_a(&filter[0])) 788c2ecf20Sopenharmony_ci EMIT(PPC_RAW_LI(r_A, 0)); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci int i; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (ctx->seen & (SEEN_MEM | SEEN_DATAREF)) { 868c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ADDI(1, 1, BPF_PPC_STACKFRAME)); 878c2ecf20Sopenharmony_ci if (ctx->seen & SEEN_DATAREF) { 888c2ecf20Sopenharmony_ci PPC_BPF_LL(0, 1, PPC_LR_STKOFF); 898c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MTLR(0)); 908c2ecf20Sopenharmony_ci PPC_BPF_LL(r_D, 1, -(REG_SZ*(32-r_D))); 918c2ecf20Sopenharmony_ci PPC_BPF_LL(r_HL, 1, -(REG_SZ*(32-r_HL))); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci if (ctx->seen & SEEN_MEM) { 948c2ecf20Sopenharmony_ci /* Restore any saved non-vol registers */ 958c2ecf20Sopenharmony_ci for (i = r_M; i < (r_M+16); i++) { 968c2ecf20Sopenharmony_ci if (ctx->seen & (1 << (i-r_M))) 978c2ecf20Sopenharmony_ci PPC_BPF_LL(i, 1, -(REG_SZ*(32-i))); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci /* The RETs have left a return value in R3. */ 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci EMIT(PPC_RAW_BLR()); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define CHOOSE_LOAD_FUNC(K, func) \ 1078c2ecf20Sopenharmony_ci ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset) 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* Assemble the body code between the prologue & epilogue. */ 1108c2ecf20Sopenharmony_cistatic int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, 1118c2ecf20Sopenharmony_ci struct codegen_context *ctx, 1128c2ecf20Sopenharmony_ci unsigned int *addrs) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci const struct sock_filter *filter = fp->insns; 1158c2ecf20Sopenharmony_ci int flen = fp->len; 1168c2ecf20Sopenharmony_ci u8 *func; 1178c2ecf20Sopenharmony_ci unsigned int true_cond; 1188c2ecf20Sopenharmony_ci int i; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Start of epilogue code */ 1218c2ecf20Sopenharmony_ci unsigned int exit_addr = addrs[flen]; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci for (i = 0; i < flen; i++) { 1248c2ecf20Sopenharmony_ci unsigned int K = filter[i].k; 1258c2ecf20Sopenharmony_ci u16 code = bpf_anc_helper(&filter[i]); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* 1288c2ecf20Sopenharmony_ci * addrs[] maps a BPF bytecode address into a real offset from 1298c2ecf20Sopenharmony_ci * the start of the body code. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci addrs[i] = ctx->idx * 4; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci switch (code) { 1348c2ecf20Sopenharmony_ci /*** ALU ops ***/ 1358c2ecf20Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_X: /* A += X; */ 1368c2ecf20Sopenharmony_ci ctx->seen |= SEEN_XREG; 1378c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ADD(r_A, r_A, r_X)); 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_K: /* A += K; */ 1408c2ecf20Sopenharmony_ci if (!K) 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ADDI(r_A, r_A, IMM_L(K))); 1438c2ecf20Sopenharmony_ci if (K >= 32768) 1448c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ADDIS(r_A, r_A, IMM_HA(K))); 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_X: /* A -= X; */ 1478c2ecf20Sopenharmony_ci ctx->seen |= SEEN_XREG; 1488c2ecf20Sopenharmony_ci EMIT(PPC_RAW_SUB(r_A, r_A, r_X)); 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */ 1518c2ecf20Sopenharmony_ci if (!K) 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ADDI(r_A, r_A, IMM_L(-K))); 1548c2ecf20Sopenharmony_ci if (K >= 32768) 1558c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ADDIS(r_A, r_A, IMM_HA(-K))); 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_X: /* A *= X; */ 1588c2ecf20Sopenharmony_ci ctx->seen |= SEEN_XREG; 1598c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MULW(r_A, r_A, r_X)); 1608c2ecf20Sopenharmony_ci break; 1618c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */ 1628c2ecf20Sopenharmony_ci if (K < 32768) 1638c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MULI(r_A, r_A, K)); 1648c2ecf20Sopenharmony_ci else { 1658c2ecf20Sopenharmony_ci PPC_LI32(r_scratch1, K); 1668c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MULW(r_A, r_A, r_scratch1)); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_X: /* A %= X; */ 1708c2ecf20Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_X: /* A /= X; */ 1718c2ecf20Sopenharmony_ci ctx->seen |= SEEN_XREG; 1728c2ecf20Sopenharmony_ci EMIT(PPC_RAW_CMPWI(r_X, 0)); 1738c2ecf20Sopenharmony_ci if (ctx->pc_ret0 != -1) { 1748c2ecf20Sopenharmony_ci PPC_BCC(COND_EQ, addrs[ctx->pc_ret0]); 1758c2ecf20Sopenharmony_ci } else { 1768c2ecf20Sopenharmony_ci PPC_BCC_SHORT(COND_NE, (ctx->idx*4)+12); 1778c2ecf20Sopenharmony_ci EMIT(PPC_RAW_LI(r_ret, 0)); 1788c2ecf20Sopenharmony_ci PPC_JMP(exit_addr); 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci if (code == (BPF_ALU | BPF_MOD | BPF_X)) { 1818c2ecf20Sopenharmony_ci EMIT(PPC_RAW_DIVWU(r_scratch1, r_A, r_X)); 1828c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MULW(r_scratch1, r_X, r_scratch1)); 1838c2ecf20Sopenharmony_ci EMIT(PPC_RAW_SUB(r_A, r_A, r_scratch1)); 1848c2ecf20Sopenharmony_ci } else { 1858c2ecf20Sopenharmony_ci EMIT(PPC_RAW_DIVWU(r_A, r_A, r_X)); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_K: /* A %= K; */ 1898c2ecf20Sopenharmony_ci PPC_LI32(r_scratch2, K); 1908c2ecf20Sopenharmony_ci EMIT(PPC_RAW_DIVWU(r_scratch1, r_A, r_scratch2)); 1918c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MULW(r_scratch1, r_scratch2, r_scratch1)); 1928c2ecf20Sopenharmony_ci EMIT(PPC_RAW_SUB(r_A, r_A, r_scratch1)); 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_K: /* A /= K */ 1958c2ecf20Sopenharmony_ci if (K == 1) 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci PPC_LI32(r_scratch1, K); 1988c2ecf20Sopenharmony_ci EMIT(PPC_RAW_DIVWU(r_A, r_A, r_scratch1)); 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_X: 2018c2ecf20Sopenharmony_ci ctx->seen |= SEEN_XREG; 2028c2ecf20Sopenharmony_ci EMIT(PPC_RAW_AND(r_A, r_A, r_X)); 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_K: 2058c2ecf20Sopenharmony_ci if (!IMM_H(K)) 2068c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ANDI(r_A, r_A, K)); 2078c2ecf20Sopenharmony_ci else { 2088c2ecf20Sopenharmony_ci PPC_LI32(r_scratch1, K); 2098c2ecf20Sopenharmony_ci EMIT(PPC_RAW_AND(r_A, r_A, r_scratch1)); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_X: 2138c2ecf20Sopenharmony_ci ctx->seen |= SEEN_XREG; 2148c2ecf20Sopenharmony_ci EMIT(PPC_RAW_OR(r_A, r_A, r_X)); 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_K: 2178c2ecf20Sopenharmony_ci if (IMM_L(K)) 2188c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ORI(r_A, r_A, IMM_L(K))); 2198c2ecf20Sopenharmony_ci if (K >= 65536) 2208c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ORIS(r_A, r_A, IMM_H(K))); 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_ALU_XOR_X: 2238c2ecf20Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_X: /* A ^= X */ 2248c2ecf20Sopenharmony_ci ctx->seen |= SEEN_XREG; 2258c2ecf20Sopenharmony_ci EMIT(PPC_RAW_XOR(r_A, r_A, r_X)); 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K */ 2288c2ecf20Sopenharmony_ci if (IMM_L(K)) 2298c2ecf20Sopenharmony_ci EMIT(PPC_RAW_XORI(r_A, r_A, IMM_L(K))); 2308c2ecf20Sopenharmony_ci if (K >= 65536) 2318c2ecf20Sopenharmony_ci EMIT(PPC_RAW_XORIS(r_A, r_A, IMM_H(K))); 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_X: /* A <<= X; */ 2348c2ecf20Sopenharmony_ci ctx->seen |= SEEN_XREG; 2358c2ecf20Sopenharmony_ci EMIT(PPC_RAW_SLW(r_A, r_A, r_X)); 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_K: 2388c2ecf20Sopenharmony_ci if (K == 0) 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci else 2418c2ecf20Sopenharmony_ci EMIT(PPC_RAW_SLWI(r_A, r_A, K)); 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_X: /* A >>= X; */ 2448c2ecf20Sopenharmony_ci ctx->seen |= SEEN_XREG; 2458c2ecf20Sopenharmony_ci EMIT(PPC_RAW_SRW(r_A, r_A, r_X)); 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_K: /* A >>= K; */ 2488c2ecf20Sopenharmony_ci if (K == 0) 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci else 2518c2ecf20Sopenharmony_ci EMIT(PPC_RAW_SRWI(r_A, r_A, K)); 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci case BPF_ALU | BPF_NEG: 2548c2ecf20Sopenharmony_ci EMIT(PPC_RAW_NEG(r_A, r_A)); 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci case BPF_RET | BPF_K: 2578c2ecf20Sopenharmony_ci PPC_LI32(r_ret, K); 2588c2ecf20Sopenharmony_ci if (!K) { 2598c2ecf20Sopenharmony_ci if (ctx->pc_ret0 == -1) 2608c2ecf20Sopenharmony_ci ctx->pc_ret0 = i; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci /* 2638c2ecf20Sopenharmony_ci * If this isn't the very last instruction, branch to 2648c2ecf20Sopenharmony_ci * the epilogue if we've stuff to clean up. Otherwise, 2658c2ecf20Sopenharmony_ci * if there's nothing to tidy, just return. If we /are/ 2668c2ecf20Sopenharmony_ci * the last instruction, we're about to fall through to 2678c2ecf20Sopenharmony_ci * the epilogue to return. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ci if (i != flen - 1) { 2708c2ecf20Sopenharmony_ci /* 2718c2ecf20Sopenharmony_ci * Note: 'seen' is properly valid only on pass 2728c2ecf20Sopenharmony_ci * #2. Both parts of this conditional are the 2738c2ecf20Sopenharmony_ci * same instruction size though, meaning the 2748c2ecf20Sopenharmony_ci * first pass will still correctly determine the 2758c2ecf20Sopenharmony_ci * code size/addresses. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci if (ctx->seen) 2788c2ecf20Sopenharmony_ci PPC_JMP(exit_addr); 2798c2ecf20Sopenharmony_ci else 2808c2ecf20Sopenharmony_ci EMIT(PPC_RAW_BLR()); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci case BPF_RET | BPF_A: 2848c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MR(r_ret, r_A)); 2858c2ecf20Sopenharmony_ci if (i != flen - 1) { 2868c2ecf20Sopenharmony_ci if (ctx->seen) 2878c2ecf20Sopenharmony_ci PPC_JMP(exit_addr); 2888c2ecf20Sopenharmony_ci else 2898c2ecf20Sopenharmony_ci EMIT(PPC_RAW_BLR()); 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci case BPF_MISC | BPF_TAX: /* X = A */ 2938c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MR(r_X, r_A)); 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci case BPF_MISC | BPF_TXA: /* A = X */ 2968c2ecf20Sopenharmony_ci ctx->seen |= SEEN_XREG; 2978c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MR(r_A, r_X)); 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /*** Constant loads/M[] access ***/ 3018c2ecf20Sopenharmony_ci case BPF_LD | BPF_IMM: /* A = K */ 3028c2ecf20Sopenharmony_ci PPC_LI32(r_A, K); 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci case BPF_LDX | BPF_IMM: /* X = K */ 3058c2ecf20Sopenharmony_ci PPC_LI32(r_X, K); 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci case BPF_LD | BPF_MEM: /* A = mem[K] */ 3088c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MR(r_A, r_M + (K & 0xf))); 3098c2ecf20Sopenharmony_ci ctx->seen |= SEEN_MEM | (1<<(K & 0xf)); 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci case BPF_LDX | BPF_MEM: /* X = mem[K] */ 3128c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MR(r_X, r_M + (K & 0xf))); 3138c2ecf20Sopenharmony_ci ctx->seen |= SEEN_MEM | (1<<(K & 0xf)); 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci case BPF_ST: /* mem[K] = A */ 3168c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MR(r_M + (K & 0xf), r_A)); 3178c2ecf20Sopenharmony_ci ctx->seen |= SEEN_MEM | (1<<(K & 0xf)); 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci case BPF_STX: /* mem[K] = X */ 3208c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MR(r_M + (K & 0xf), r_X)); 3218c2ecf20Sopenharmony_ci ctx->seen |= SEEN_XREG | SEEN_MEM | (1<<(K & 0xf)); 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci case BPF_LD | BPF_W | BPF_LEN: /* A = skb->len; */ 3248c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct sk_buff, len) != 4); 3258c2ecf20Sopenharmony_ci PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, len)); 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci case BPF_LDX | BPF_W | BPF_ABS: /* A = *((u32 *)(seccomp_data + K)); */ 3288c2ecf20Sopenharmony_ci PPC_LWZ_OFFS(r_A, r_skb, K); 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci case BPF_LDX | BPF_W | BPF_LEN: /* X = skb->len; */ 3318c2ecf20Sopenharmony_ci PPC_LWZ_OFFS(r_X, r_skb, offsetof(struct sk_buff, len)); 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /*** Ancillary info loads ***/ 3358c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_PROTOCOL: /* A = ntohs(skb->protocol); */ 3368c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct sk_buff, 3378c2ecf20Sopenharmony_ci protocol) != 2); 3388c2ecf20Sopenharmony_ci PPC_NTOHS_OFFS(r_A, r_skb, offsetof(struct sk_buff, 3398c2ecf20Sopenharmony_ci protocol)); 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_IFINDEX: 3428c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_HATYPE: 3438c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct net_device, 3448c2ecf20Sopenharmony_ci ifindex) != 4); 3458c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct net_device, 3468c2ecf20Sopenharmony_ci type) != 2); 3478c2ecf20Sopenharmony_ci PPC_LL_OFFS(r_scratch1, r_skb, offsetof(struct sk_buff, 3488c2ecf20Sopenharmony_ci dev)); 3498c2ecf20Sopenharmony_ci EMIT(PPC_RAW_CMPDI(r_scratch1, 0)); 3508c2ecf20Sopenharmony_ci if (ctx->pc_ret0 != -1) { 3518c2ecf20Sopenharmony_ci PPC_BCC(COND_EQ, addrs[ctx->pc_ret0]); 3528c2ecf20Sopenharmony_ci } else { 3538c2ecf20Sopenharmony_ci /* Exit, returning 0; first pass hits here. */ 3548c2ecf20Sopenharmony_ci PPC_BCC_SHORT(COND_NE, ctx->idx * 4 + 12); 3558c2ecf20Sopenharmony_ci EMIT(PPC_RAW_LI(r_ret, 0)); 3568c2ecf20Sopenharmony_ci PPC_JMP(exit_addr); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci if (code == (BPF_ANC | SKF_AD_IFINDEX)) { 3598c2ecf20Sopenharmony_ci PPC_LWZ_OFFS(r_A, r_scratch1, 3608c2ecf20Sopenharmony_ci offsetof(struct net_device, ifindex)); 3618c2ecf20Sopenharmony_ci } else { 3628c2ecf20Sopenharmony_ci PPC_LHZ_OFFS(r_A, r_scratch1, 3638c2ecf20Sopenharmony_ci offsetof(struct net_device, type)); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_MARK: 3688c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct sk_buff, mark) != 4); 3698c2ecf20Sopenharmony_ci PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, 3708c2ecf20Sopenharmony_ci mark)); 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_RXHASH: 3738c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct sk_buff, hash) != 4); 3748c2ecf20Sopenharmony_ci PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, 3758c2ecf20Sopenharmony_ci hash)); 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_VLAN_TAG: 3788c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct sk_buff, vlan_tci) != 2); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci PPC_LHZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, 3818c2ecf20Sopenharmony_ci vlan_tci)); 3828c2ecf20Sopenharmony_ci break; 3838c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT: 3848c2ecf20Sopenharmony_ci PPC_LBZ_OFFS(r_A, r_skb, PKT_VLAN_PRESENT_OFFSET()); 3858c2ecf20Sopenharmony_ci if (PKT_VLAN_PRESENT_BIT) 3868c2ecf20Sopenharmony_ci EMIT(PPC_RAW_SRWI(r_A, r_A, PKT_VLAN_PRESENT_BIT)); 3878c2ecf20Sopenharmony_ci if (PKT_VLAN_PRESENT_BIT < 7) 3888c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ANDI(r_A, r_A, 1)); 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_QUEUE: 3918c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct sk_buff, 3928c2ecf20Sopenharmony_ci queue_mapping) != 2); 3938c2ecf20Sopenharmony_ci PPC_LHZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, 3948c2ecf20Sopenharmony_ci queue_mapping)); 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_PKTTYPE: 3978c2ecf20Sopenharmony_ci PPC_LBZ_OFFS(r_A, r_skb, PKT_TYPE_OFFSET()); 3988c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ANDI(r_A, r_A, PKT_TYPE_MAX)); 3998c2ecf20Sopenharmony_ci EMIT(PPC_RAW_SRWI(r_A, r_A, 5)); 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_CPU: 4028c2ecf20Sopenharmony_ci PPC_BPF_LOAD_CPU(r_A); 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci /*** Absolute loads from packet header/data ***/ 4058c2ecf20Sopenharmony_ci case BPF_LD | BPF_W | BPF_ABS: 4068c2ecf20Sopenharmony_ci func = CHOOSE_LOAD_FUNC(K, sk_load_word); 4078c2ecf20Sopenharmony_ci goto common_load; 4088c2ecf20Sopenharmony_ci case BPF_LD | BPF_H | BPF_ABS: 4098c2ecf20Sopenharmony_ci func = CHOOSE_LOAD_FUNC(K, sk_load_half); 4108c2ecf20Sopenharmony_ci goto common_load; 4118c2ecf20Sopenharmony_ci case BPF_LD | BPF_B | BPF_ABS: 4128c2ecf20Sopenharmony_ci func = CHOOSE_LOAD_FUNC(K, sk_load_byte); 4138c2ecf20Sopenharmony_ci common_load: 4148c2ecf20Sopenharmony_ci /* Load from [K]. */ 4158c2ecf20Sopenharmony_ci ctx->seen |= SEEN_DATAREF; 4168c2ecf20Sopenharmony_ci PPC_FUNC_ADDR(r_scratch1, func); 4178c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MTLR(r_scratch1)); 4188c2ecf20Sopenharmony_ci PPC_LI32(r_addr, K); 4198c2ecf20Sopenharmony_ci EMIT(PPC_RAW_BLRL()); 4208c2ecf20Sopenharmony_ci /* 4218c2ecf20Sopenharmony_ci * Helper returns 'lt' condition on error, and an 4228c2ecf20Sopenharmony_ci * appropriate return value in r3 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ci PPC_BCC(COND_LT, exit_addr); 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /*** Indirect loads from packet header/data ***/ 4288c2ecf20Sopenharmony_ci case BPF_LD | BPF_W | BPF_IND: 4298c2ecf20Sopenharmony_ci func = sk_load_word; 4308c2ecf20Sopenharmony_ci goto common_load_ind; 4318c2ecf20Sopenharmony_ci case BPF_LD | BPF_H | BPF_IND: 4328c2ecf20Sopenharmony_ci func = sk_load_half; 4338c2ecf20Sopenharmony_ci goto common_load_ind; 4348c2ecf20Sopenharmony_ci case BPF_LD | BPF_B | BPF_IND: 4358c2ecf20Sopenharmony_ci func = sk_load_byte; 4368c2ecf20Sopenharmony_ci common_load_ind: 4378c2ecf20Sopenharmony_ci /* 4388c2ecf20Sopenharmony_ci * Load from [X + K]. Negative offsets are tested for 4398c2ecf20Sopenharmony_ci * in the helper functions. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci ctx->seen |= SEEN_DATAREF | SEEN_XREG; 4428c2ecf20Sopenharmony_ci PPC_FUNC_ADDR(r_scratch1, func); 4438c2ecf20Sopenharmony_ci EMIT(PPC_RAW_MTLR(r_scratch1)); 4448c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ADDI(r_addr, r_X, IMM_L(K))); 4458c2ecf20Sopenharmony_ci if (K >= 32768) 4468c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ADDIS(r_addr, r_addr, IMM_HA(K))); 4478c2ecf20Sopenharmony_ci EMIT(PPC_RAW_BLRL()); 4488c2ecf20Sopenharmony_ci /* If error, cr0.LT set */ 4498c2ecf20Sopenharmony_ci PPC_BCC(COND_LT, exit_addr); 4508c2ecf20Sopenharmony_ci break; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci case BPF_LDX | BPF_B | BPF_MSH: 4538c2ecf20Sopenharmony_ci func = CHOOSE_LOAD_FUNC(K, sk_load_byte_msh); 4548c2ecf20Sopenharmony_ci goto common_load; 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /*** Jump and branches ***/ 4588c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JA: 4598c2ecf20Sopenharmony_ci if (K != 0) 4608c2ecf20Sopenharmony_ci PPC_JMP(addrs[i + 1 + K]); 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_K: 4648c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_X: 4658c2ecf20Sopenharmony_ci true_cond = COND_GT; 4668c2ecf20Sopenharmony_ci goto cond_branch; 4678c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_K: 4688c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_X: 4698c2ecf20Sopenharmony_ci true_cond = COND_GE; 4708c2ecf20Sopenharmony_ci goto cond_branch; 4718c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_K: 4728c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_X: 4738c2ecf20Sopenharmony_ci true_cond = COND_EQ; 4748c2ecf20Sopenharmony_ci goto cond_branch; 4758c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_K: 4768c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_X: 4778c2ecf20Sopenharmony_ci true_cond = COND_NE; 4788c2ecf20Sopenharmony_ci cond_branch: 4798c2ecf20Sopenharmony_ci /* same targets, can avoid doing the test :) */ 4808c2ecf20Sopenharmony_ci if (filter[i].jt == filter[i].jf) { 4818c2ecf20Sopenharmony_ci if (filter[i].jt > 0) 4828c2ecf20Sopenharmony_ci PPC_JMP(addrs[i + 1 + filter[i].jt]); 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci switch (code) { 4878c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_X: 4888c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_X: 4898c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_X: 4908c2ecf20Sopenharmony_ci ctx->seen |= SEEN_XREG; 4918c2ecf20Sopenharmony_ci EMIT(PPC_RAW_CMPLW(r_A, r_X)); 4928c2ecf20Sopenharmony_ci break; 4938c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_X: 4948c2ecf20Sopenharmony_ci ctx->seen |= SEEN_XREG; 4958c2ecf20Sopenharmony_ci EMIT(PPC_RAW_AND_DOT(r_scratch1, r_A, r_X)); 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_K: 4988c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_K: 4998c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_K: 5008c2ecf20Sopenharmony_ci if (K < 32768) 5018c2ecf20Sopenharmony_ci EMIT(PPC_RAW_CMPLWI(r_A, K)); 5028c2ecf20Sopenharmony_ci else { 5038c2ecf20Sopenharmony_ci PPC_LI32(r_scratch1, K); 5048c2ecf20Sopenharmony_ci EMIT(PPC_RAW_CMPLW(r_A, r_scratch1)); 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci break; 5078c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_K: 5088c2ecf20Sopenharmony_ci if (K < 32768) 5098c2ecf20Sopenharmony_ci /* PPC_ANDI is /only/ dot-form */ 5108c2ecf20Sopenharmony_ci EMIT(PPC_RAW_ANDI(r_scratch1, r_A, K)); 5118c2ecf20Sopenharmony_ci else { 5128c2ecf20Sopenharmony_ci PPC_LI32(r_scratch1, K); 5138c2ecf20Sopenharmony_ci EMIT(PPC_RAW_AND_DOT(r_scratch1, r_A, 5148c2ecf20Sopenharmony_ci r_scratch1)); 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci break; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci /* Sometimes branches are constructed "backward", with 5198c2ecf20Sopenharmony_ci * the false path being the branch and true path being 5208c2ecf20Sopenharmony_ci * a fallthrough to the next instruction. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci if (filter[i].jt == 0) 5238c2ecf20Sopenharmony_ci /* Swap the sense of the branch */ 5248c2ecf20Sopenharmony_ci PPC_BCC(true_cond ^ COND_CMP_TRUE, 5258c2ecf20Sopenharmony_ci addrs[i + 1 + filter[i].jf]); 5268c2ecf20Sopenharmony_ci else { 5278c2ecf20Sopenharmony_ci PPC_BCC(true_cond, addrs[i + 1 + filter[i].jt]); 5288c2ecf20Sopenharmony_ci if (filter[i].jf != 0) 5298c2ecf20Sopenharmony_ci PPC_JMP(addrs[i + 1 + filter[i].jf]); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci break; 5328c2ecf20Sopenharmony_ci default: 5338c2ecf20Sopenharmony_ci /* The filter contains something cruel & unusual. 5348c2ecf20Sopenharmony_ci * We don't handle it, but also there shouldn't be 5358c2ecf20Sopenharmony_ci * anything missing from our list. 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_ci if (printk_ratelimit()) 5388c2ecf20Sopenharmony_ci pr_err("BPF filter opcode %04x (@%d) unsupported\n", 5398c2ecf20Sopenharmony_ci filter[i].code, i); 5408c2ecf20Sopenharmony_ci return -ENOTSUPP; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci /* Set end-of-body-code address for exit. */ 5458c2ecf20Sopenharmony_ci addrs[i] = ctx->idx * 4; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_civoid bpf_jit_compile(struct bpf_prog *fp) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci unsigned int proglen; 5538c2ecf20Sopenharmony_ci unsigned int alloclen; 5548c2ecf20Sopenharmony_ci u32 *image = NULL; 5558c2ecf20Sopenharmony_ci u32 *code_base; 5568c2ecf20Sopenharmony_ci unsigned int *addrs; 5578c2ecf20Sopenharmony_ci struct codegen_context cgctx; 5588c2ecf20Sopenharmony_ci int pass; 5598c2ecf20Sopenharmony_ci int flen = fp->len; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (!bpf_jit_enable) 5628c2ecf20Sopenharmony_ci return; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci addrs = kcalloc(flen + 1, sizeof(*addrs), GFP_KERNEL); 5658c2ecf20Sopenharmony_ci if (addrs == NULL) 5668c2ecf20Sopenharmony_ci return; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* 5698c2ecf20Sopenharmony_ci * There are multiple assembly passes as the generated code will change 5708c2ecf20Sopenharmony_ci * size as it settles down, figuring out the max branch offsets/exit 5718c2ecf20Sopenharmony_ci * paths required. 5728c2ecf20Sopenharmony_ci * 5738c2ecf20Sopenharmony_ci * The range of standard conditional branches is +/- 32Kbytes. Since 5748c2ecf20Sopenharmony_ci * BPF_MAXINSNS = 4096, we can only jump from (worst case) start to 5758c2ecf20Sopenharmony_ci * finish with 8 bytes/instruction. Not feasible, so long jumps are 5768c2ecf20Sopenharmony_ci * used, distinct from short branches. 5778c2ecf20Sopenharmony_ci * 5788c2ecf20Sopenharmony_ci * Current: 5798c2ecf20Sopenharmony_ci * 5808c2ecf20Sopenharmony_ci * For now, both branch types assemble to 2 words (short branches padded 5818c2ecf20Sopenharmony_ci * with a NOP); this is less efficient, but assembly will always complete 5828c2ecf20Sopenharmony_ci * after exactly 3 passes: 5838c2ecf20Sopenharmony_ci * 5848c2ecf20Sopenharmony_ci * First pass: No code buffer; Program is "faux-generated" -- no code 5858c2ecf20Sopenharmony_ci * emitted but maximum size of output determined (and addrs[] filled 5868c2ecf20Sopenharmony_ci * in). Also, we note whether we use M[], whether we use skb data, etc. 5878c2ecf20Sopenharmony_ci * All generation choices assumed to be 'worst-case', e.g. branches all 5888c2ecf20Sopenharmony_ci * far (2 instructions), return path code reduction not available, etc. 5898c2ecf20Sopenharmony_ci * 5908c2ecf20Sopenharmony_ci * Second pass: Code buffer allocated with size determined previously. 5918c2ecf20Sopenharmony_ci * Prologue generated to support features we have seen used. Exit paths 5928c2ecf20Sopenharmony_ci * determined and addrs[] is filled in again, as code may be slightly 5938c2ecf20Sopenharmony_ci * smaller as a result. 5948c2ecf20Sopenharmony_ci * 5958c2ecf20Sopenharmony_ci * Third pass: Code generated 'for real', and branch destinations 5968c2ecf20Sopenharmony_ci * determined from now-accurate addrs[] map. 5978c2ecf20Sopenharmony_ci * 5988c2ecf20Sopenharmony_ci * Ideal: 5998c2ecf20Sopenharmony_ci * 6008c2ecf20Sopenharmony_ci * If we optimise this, near branches will be shorter. On the 6018c2ecf20Sopenharmony_ci * first assembly pass, we should err on the side of caution and 6028c2ecf20Sopenharmony_ci * generate the biggest code. On subsequent passes, branches will be 6038c2ecf20Sopenharmony_ci * generated short or long and code size will reduce. With smaller 6048c2ecf20Sopenharmony_ci * code, more branches may fall into the short category, and code will 6058c2ecf20Sopenharmony_ci * reduce more. 6068c2ecf20Sopenharmony_ci * 6078c2ecf20Sopenharmony_ci * Finally, if we see one pass generate code the same size as the 6088c2ecf20Sopenharmony_ci * previous pass we have converged and should now generate code for 6098c2ecf20Sopenharmony_ci * real. Allocating at the end will also save the memory that would 6108c2ecf20Sopenharmony_ci * otherwise be wasted by the (small) current code shrinkage. 6118c2ecf20Sopenharmony_ci * Preferably, we should do a small number of passes (e.g. 5) and if we 6128c2ecf20Sopenharmony_ci * haven't converged by then, get impatient and force code to generate 6138c2ecf20Sopenharmony_ci * as-is, even if the odd branch would be left long. The chances of a 6148c2ecf20Sopenharmony_ci * long jump are tiny with all but the most enormous of BPF filter 6158c2ecf20Sopenharmony_ci * inputs, so we should usually converge on the third pass. 6168c2ecf20Sopenharmony_ci */ 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci cgctx.idx = 0; 6198c2ecf20Sopenharmony_ci cgctx.seen = 0; 6208c2ecf20Sopenharmony_ci cgctx.pc_ret0 = -1; 6218c2ecf20Sopenharmony_ci /* Scouting faux-generate pass 0 */ 6228c2ecf20Sopenharmony_ci if (bpf_jit_build_body(fp, 0, &cgctx, addrs)) 6238c2ecf20Sopenharmony_ci /* We hit something illegal or unsupported. */ 6248c2ecf20Sopenharmony_ci goto out; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* 6278c2ecf20Sopenharmony_ci * Pretend to build prologue, given the features we've seen. This will 6288c2ecf20Sopenharmony_ci * update ctgtx.idx as it pretends to output instructions, then we can 6298c2ecf20Sopenharmony_ci * calculate total size from idx. 6308c2ecf20Sopenharmony_ci */ 6318c2ecf20Sopenharmony_ci bpf_jit_build_prologue(fp, 0, &cgctx); 6328c2ecf20Sopenharmony_ci bpf_jit_build_epilogue(0, &cgctx); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci proglen = cgctx.idx * 4; 6358c2ecf20Sopenharmony_ci alloclen = proglen + FUNCTION_DESCR_SIZE; 6368c2ecf20Sopenharmony_ci image = module_alloc(alloclen); 6378c2ecf20Sopenharmony_ci if (!image) 6388c2ecf20Sopenharmony_ci goto out; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci code_base = image + (FUNCTION_DESCR_SIZE/4); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* Code generation passes 1-2 */ 6438c2ecf20Sopenharmony_ci for (pass = 1; pass < 3; pass++) { 6448c2ecf20Sopenharmony_ci /* Now build the prologue, body code & epilogue for real. */ 6458c2ecf20Sopenharmony_ci cgctx.idx = 0; 6468c2ecf20Sopenharmony_ci bpf_jit_build_prologue(fp, code_base, &cgctx); 6478c2ecf20Sopenharmony_ci bpf_jit_build_body(fp, code_base, &cgctx, addrs); 6488c2ecf20Sopenharmony_ci bpf_jit_build_epilogue(code_base, &cgctx); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (bpf_jit_enable > 1) 6518c2ecf20Sopenharmony_ci pr_info("Pass %d: shrink = %d, seen = 0x%x\n", pass, 6528c2ecf20Sopenharmony_ci proglen - (cgctx.idx * 4), cgctx.seen); 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (bpf_jit_enable > 1) 6568c2ecf20Sopenharmony_ci /* Note that we output the base address of the code_base 6578c2ecf20Sopenharmony_ci * rather than image, since opcodes are in code_base. 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_ci bpf_jit_dump(flen, proglen, pass, code_base); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci bpf_flush_icache(code_base, code_base + (proglen/4)); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64 6648c2ecf20Sopenharmony_ci /* Function descriptor nastiness: Address + TOC */ 6658c2ecf20Sopenharmony_ci ((u64 *)image)[0] = (u64)code_base; 6668c2ecf20Sopenharmony_ci ((u64 *)image)[1] = local_paca->kernel_toc; 6678c2ecf20Sopenharmony_ci#endif 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci fp->bpf_func = (void *)image; 6708c2ecf20Sopenharmony_ci fp->jited = 1; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ciout: 6738c2ecf20Sopenharmony_ci kfree(addrs); 6748c2ecf20Sopenharmony_ci return; 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_civoid bpf_jit_free(struct bpf_prog *fp) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci if (fp->jited) 6808c2ecf20Sopenharmony_ci module_memfree(fp->bpf_func); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci bpf_prog_unlock_free(fp); 6838c2ecf20Sopenharmony_ci} 684