18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Just-In-Time compiler for BPF filters on MIPS 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2014 Imagination Technologies Ltd. 58c2ecf20Sopenharmony_ci * Author: Markos Chandras <markos.chandras@imgtec.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 88c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the 98c2ecf20Sopenharmony_ci * Free Software Foundation; version 2 of the License. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/bitops.h> 138c2ecf20Sopenharmony_ci#include <linux/compiler.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/filter.h> 168c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 178c2ecf20Sopenharmony_ci#include <linux/moduleloader.h> 188c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 198c2ecf20Sopenharmony_ci#include <linux/string.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/types.h> 228c2ecf20Sopenharmony_ci#include <asm/asm.h> 238c2ecf20Sopenharmony_ci#include <asm/bitops.h> 248c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 258c2ecf20Sopenharmony_ci#include <asm/cpu-features.h> 268c2ecf20Sopenharmony_ci#include <asm/uasm.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "bpf_jit.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* ABI 318c2ecf20Sopenharmony_ci * r_skb_hl SKB header length 328c2ecf20Sopenharmony_ci * r_data SKB data pointer 338c2ecf20Sopenharmony_ci * r_off Offset 348c2ecf20Sopenharmony_ci * r_A BPF register A 358c2ecf20Sopenharmony_ci * r_X BPF register X 368c2ecf20Sopenharmony_ci * r_skb *skb 378c2ecf20Sopenharmony_ci * r_M *scratch memory 388c2ecf20Sopenharmony_ci * r_skb_len SKB length 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * On entry (*bpf_func)(*skb, *filter) 418c2ecf20Sopenharmony_ci * a0 = MIPS_R_A0 = skb; 428c2ecf20Sopenharmony_ci * a1 = MIPS_R_A1 = filter; 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * Stack 458c2ecf20Sopenharmony_ci * ... 468c2ecf20Sopenharmony_ci * M[15] 478c2ecf20Sopenharmony_ci * M[14] 488c2ecf20Sopenharmony_ci * M[13] 498c2ecf20Sopenharmony_ci * ... 508c2ecf20Sopenharmony_ci * M[0] <-- r_M 518c2ecf20Sopenharmony_ci * saved reg k-1 528c2ecf20Sopenharmony_ci * saved reg k-2 538c2ecf20Sopenharmony_ci * ... 548c2ecf20Sopenharmony_ci * saved reg 0 <-- r_sp 558c2ecf20Sopenharmony_ci * <no argument area> 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * Packet layout 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * <--------------------- len ------------------------> 608c2ecf20Sopenharmony_ci * <--skb-len(r_skb_hl)-->< ----- skb->data_len ------> 618c2ecf20Sopenharmony_ci * ---------------------------------------------------- 628c2ecf20Sopenharmony_ci * | skb->data | 638c2ecf20Sopenharmony_ci * ---------------------------------------------------- 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define ptr typeof(unsigned long) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define SCRATCH_OFF(k) (4 * (k)) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* JIT flags */ 718c2ecf20Sopenharmony_ci#define SEEN_CALL (1 << BPF_MEMWORDS) 728c2ecf20Sopenharmony_ci#define SEEN_SREG_SFT (BPF_MEMWORDS + 1) 738c2ecf20Sopenharmony_ci#define SEEN_SREG_BASE (1 << SEEN_SREG_SFT) 748c2ecf20Sopenharmony_ci#define SEEN_SREG(x) (SEEN_SREG_BASE << (x)) 758c2ecf20Sopenharmony_ci#define SEEN_OFF SEEN_SREG(2) 768c2ecf20Sopenharmony_ci#define SEEN_A SEEN_SREG(3) 778c2ecf20Sopenharmony_ci#define SEEN_X SEEN_SREG(4) 788c2ecf20Sopenharmony_ci#define SEEN_SKB SEEN_SREG(5) 798c2ecf20Sopenharmony_ci#define SEEN_MEM SEEN_SREG(6) 808c2ecf20Sopenharmony_ci/* SEEN_SK_DATA also implies skb_hl an skb_len */ 818c2ecf20Sopenharmony_ci#define SEEN_SKB_DATA (SEEN_SREG(7) | SEEN_SREG(1) | SEEN_SREG(0)) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* Arguments used by JIT */ 848c2ecf20Sopenharmony_ci#define ARGS_USED_BY_JIT 2 /* only applicable to 64-bit */ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define SBIT(x) (1 << (x)) /* Signed version of BIT() */ 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/** 898c2ecf20Sopenharmony_ci * struct jit_ctx - JIT context 908c2ecf20Sopenharmony_ci * @skf: The sk_filter 918c2ecf20Sopenharmony_ci * @prologue_bytes: Number of bytes for prologue 928c2ecf20Sopenharmony_ci * @idx: Instruction index 938c2ecf20Sopenharmony_ci * @flags: JIT flags 948c2ecf20Sopenharmony_ci * @offsets: Instruction offsets 958c2ecf20Sopenharmony_ci * @target: Memory location for the compiled filter 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistruct jit_ctx { 988c2ecf20Sopenharmony_ci const struct bpf_prog *skf; 998c2ecf20Sopenharmony_ci unsigned int prologue_bytes; 1008c2ecf20Sopenharmony_ci u32 idx; 1018c2ecf20Sopenharmony_ci u32 flags; 1028c2ecf20Sopenharmony_ci u32 *offsets; 1038c2ecf20Sopenharmony_ci u32 *target; 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic inline int optimize_div(u32 *k) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci /* power of 2 divides can be implemented with right shift */ 1108c2ecf20Sopenharmony_ci if (!(*k & (*k-1))) { 1118c2ecf20Sopenharmony_ci *k = ilog2(*k); 1128c2ecf20Sopenharmony_ci return 1; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic inline void emit_jit_reg_move(ptr dst, ptr src, struct jit_ctx *ctx); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* Simply emit the instruction if the JIT memory space has been allocated */ 1218c2ecf20Sopenharmony_ci#define emit_instr(ctx, func, ...) \ 1228c2ecf20Sopenharmony_cido { \ 1238c2ecf20Sopenharmony_ci if ((ctx)->target != NULL) { \ 1248c2ecf20Sopenharmony_ci u32 *p = &(ctx)->target[ctx->idx]; \ 1258c2ecf20Sopenharmony_ci uasm_i_##func(&p, ##__VA_ARGS__); \ 1268c2ecf20Sopenharmony_ci } \ 1278c2ecf20Sopenharmony_ci (ctx)->idx++; \ 1288c2ecf20Sopenharmony_ci} while (0) 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* 1318c2ecf20Sopenharmony_ci * Similar to emit_instr but it must be used when we need to emit 1328c2ecf20Sopenharmony_ci * 32-bit or 64-bit instructions 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci#define emit_long_instr(ctx, func, ...) \ 1358c2ecf20Sopenharmony_cido { \ 1368c2ecf20Sopenharmony_ci if ((ctx)->target != NULL) { \ 1378c2ecf20Sopenharmony_ci u32 *p = &(ctx)->target[ctx->idx]; \ 1388c2ecf20Sopenharmony_ci UASM_i_##func(&p, ##__VA_ARGS__); \ 1398c2ecf20Sopenharmony_ci } \ 1408c2ecf20Sopenharmony_ci (ctx)->idx++; \ 1418c2ecf20Sopenharmony_ci} while (0) 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* Determine if immediate is within the 16-bit signed range */ 1448c2ecf20Sopenharmony_cistatic inline bool is_range16(s32 imm) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci return !(imm >= SBIT(15) || imm < -SBIT(15)); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic inline void emit_addu(unsigned int dst, unsigned int src1, 1508c2ecf20Sopenharmony_ci unsigned int src2, struct jit_ctx *ctx) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci emit_instr(ctx, addu, dst, src1, src2); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic inline void emit_nop(struct jit_ctx *ctx) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci emit_instr(ctx, nop); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* Load a u32 immediate to a register */ 1618c2ecf20Sopenharmony_cistatic inline void emit_load_imm(unsigned int dst, u32 imm, struct jit_ctx *ctx) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci if (ctx->target != NULL) { 1648c2ecf20Sopenharmony_ci /* addiu can only handle s16 */ 1658c2ecf20Sopenharmony_ci if (!is_range16(imm)) { 1668c2ecf20Sopenharmony_ci u32 *p = &ctx->target[ctx->idx]; 1678c2ecf20Sopenharmony_ci uasm_i_lui(&p, r_tmp_imm, (s32)imm >> 16); 1688c2ecf20Sopenharmony_ci p = &ctx->target[ctx->idx + 1]; 1698c2ecf20Sopenharmony_ci uasm_i_ori(&p, dst, r_tmp_imm, imm & 0xffff); 1708c2ecf20Sopenharmony_ci } else { 1718c2ecf20Sopenharmony_ci u32 *p = &ctx->target[ctx->idx]; 1728c2ecf20Sopenharmony_ci uasm_i_addiu(&p, dst, r_zero, imm); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci ctx->idx++; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (!is_range16(imm)) 1788c2ecf20Sopenharmony_ci ctx->idx++; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic inline void emit_or(unsigned int dst, unsigned int src1, 1828c2ecf20Sopenharmony_ci unsigned int src2, struct jit_ctx *ctx) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci emit_instr(ctx, or, dst, src1, src2); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic inline void emit_ori(unsigned int dst, unsigned src, u32 imm, 1888c2ecf20Sopenharmony_ci struct jit_ctx *ctx) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci if (imm >= BIT(16)) { 1918c2ecf20Sopenharmony_ci emit_load_imm(r_tmp, imm, ctx); 1928c2ecf20Sopenharmony_ci emit_or(dst, src, r_tmp, ctx); 1938c2ecf20Sopenharmony_ci } else { 1948c2ecf20Sopenharmony_ci emit_instr(ctx, ori, dst, src, imm); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic inline void emit_daddiu(unsigned int dst, unsigned int src, 1998c2ecf20Sopenharmony_ci int imm, struct jit_ctx *ctx) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci /* 2028c2ecf20Sopenharmony_ci * Only used for stack, so the imm is relatively small 2038c2ecf20Sopenharmony_ci * and it fits in 15-bits 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ci emit_instr(ctx, daddiu, dst, src, imm); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic inline void emit_addiu(unsigned int dst, unsigned int src, 2098c2ecf20Sopenharmony_ci u32 imm, struct jit_ctx *ctx) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci if (!is_range16(imm)) { 2128c2ecf20Sopenharmony_ci emit_load_imm(r_tmp, imm, ctx); 2138c2ecf20Sopenharmony_ci emit_addu(dst, r_tmp, src, ctx); 2148c2ecf20Sopenharmony_ci } else { 2158c2ecf20Sopenharmony_ci emit_instr(ctx, addiu, dst, src, imm); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic inline void emit_and(unsigned int dst, unsigned int src1, 2208c2ecf20Sopenharmony_ci unsigned int src2, struct jit_ctx *ctx) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci emit_instr(ctx, and, dst, src1, src2); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic inline void emit_andi(unsigned int dst, unsigned int src, 2268c2ecf20Sopenharmony_ci u32 imm, struct jit_ctx *ctx) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci /* If imm does not fit in u16 then load it to register */ 2298c2ecf20Sopenharmony_ci if (imm >= BIT(16)) { 2308c2ecf20Sopenharmony_ci emit_load_imm(r_tmp, imm, ctx); 2318c2ecf20Sopenharmony_ci emit_and(dst, src, r_tmp, ctx); 2328c2ecf20Sopenharmony_ci } else { 2338c2ecf20Sopenharmony_ci emit_instr(ctx, andi, dst, src, imm); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic inline void emit_xor(unsigned int dst, unsigned int src1, 2388c2ecf20Sopenharmony_ci unsigned int src2, struct jit_ctx *ctx) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci emit_instr(ctx, xor, dst, src1, src2); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic inline void emit_xori(ptr dst, ptr src, u32 imm, struct jit_ctx *ctx) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci /* If imm does not fit in u16 then load it to register */ 2468c2ecf20Sopenharmony_ci if (imm >= BIT(16)) { 2478c2ecf20Sopenharmony_ci emit_load_imm(r_tmp, imm, ctx); 2488c2ecf20Sopenharmony_ci emit_xor(dst, src, r_tmp, ctx); 2498c2ecf20Sopenharmony_ci } else { 2508c2ecf20Sopenharmony_ci emit_instr(ctx, xori, dst, src, imm); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic inline void emit_stack_offset(int offset, struct jit_ctx *ctx) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci emit_long_instr(ctx, ADDIU, r_sp, r_sp, offset); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic inline void emit_subu(unsigned int dst, unsigned int src1, 2608c2ecf20Sopenharmony_ci unsigned int src2, struct jit_ctx *ctx) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci emit_instr(ctx, subu, dst, src1, src2); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic inline void emit_neg(unsigned int reg, struct jit_ctx *ctx) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci emit_subu(reg, r_zero, reg, ctx); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic inline void emit_sllv(unsigned int dst, unsigned int src, 2718c2ecf20Sopenharmony_ci unsigned int sa, struct jit_ctx *ctx) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci emit_instr(ctx, sllv, dst, src, sa); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic inline void emit_sll(unsigned int dst, unsigned int src, 2778c2ecf20Sopenharmony_ci unsigned int sa, struct jit_ctx *ctx) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci /* sa is 5-bits long */ 2808c2ecf20Sopenharmony_ci if (sa >= BIT(5)) 2818c2ecf20Sopenharmony_ci /* Shifting >= 32 results in zero */ 2828c2ecf20Sopenharmony_ci emit_jit_reg_move(dst, r_zero, ctx); 2838c2ecf20Sopenharmony_ci else 2848c2ecf20Sopenharmony_ci emit_instr(ctx, sll, dst, src, sa); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic inline void emit_srlv(unsigned int dst, unsigned int src, 2888c2ecf20Sopenharmony_ci unsigned int sa, struct jit_ctx *ctx) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci emit_instr(ctx, srlv, dst, src, sa); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic inline void emit_srl(unsigned int dst, unsigned int src, 2948c2ecf20Sopenharmony_ci unsigned int sa, struct jit_ctx *ctx) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci /* sa is 5-bits long */ 2978c2ecf20Sopenharmony_ci if (sa >= BIT(5)) 2988c2ecf20Sopenharmony_ci /* Shifting >= 32 results in zero */ 2998c2ecf20Sopenharmony_ci emit_jit_reg_move(dst, r_zero, ctx); 3008c2ecf20Sopenharmony_ci else 3018c2ecf20Sopenharmony_ci emit_instr(ctx, srl, dst, src, sa); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic inline void emit_slt(unsigned int dst, unsigned int src1, 3058c2ecf20Sopenharmony_ci unsigned int src2, struct jit_ctx *ctx) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci emit_instr(ctx, slt, dst, src1, src2); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic inline void emit_sltu(unsigned int dst, unsigned int src1, 3118c2ecf20Sopenharmony_ci unsigned int src2, struct jit_ctx *ctx) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci emit_instr(ctx, sltu, dst, src1, src2); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic inline void emit_sltiu(unsigned dst, unsigned int src, 3178c2ecf20Sopenharmony_ci unsigned int imm, struct jit_ctx *ctx) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci /* 16 bit immediate */ 3208c2ecf20Sopenharmony_ci if (!is_range16((s32)imm)) { 3218c2ecf20Sopenharmony_ci emit_load_imm(r_tmp, imm, ctx); 3228c2ecf20Sopenharmony_ci emit_sltu(dst, src, r_tmp, ctx); 3238c2ecf20Sopenharmony_ci } else { 3248c2ecf20Sopenharmony_ci emit_instr(ctx, sltiu, dst, src, imm); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci/* Store register on the stack */ 3308c2ecf20Sopenharmony_cistatic inline void emit_store_stack_reg(ptr reg, ptr base, 3318c2ecf20Sopenharmony_ci unsigned int offset, 3328c2ecf20Sopenharmony_ci struct jit_ctx *ctx) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci emit_long_instr(ctx, SW, reg, offset, base); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic inline void emit_store(ptr reg, ptr base, unsigned int offset, 3388c2ecf20Sopenharmony_ci struct jit_ctx *ctx) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci emit_instr(ctx, sw, reg, offset, base); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic inline void emit_load_stack_reg(ptr reg, ptr base, 3448c2ecf20Sopenharmony_ci unsigned int offset, 3458c2ecf20Sopenharmony_ci struct jit_ctx *ctx) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci emit_long_instr(ctx, LW, reg, offset, base); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic inline void emit_load(unsigned int reg, unsigned int base, 3518c2ecf20Sopenharmony_ci unsigned int offset, struct jit_ctx *ctx) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci emit_instr(ctx, lw, reg, offset, base); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic inline void emit_load_byte(unsigned int reg, unsigned int base, 3578c2ecf20Sopenharmony_ci unsigned int offset, struct jit_ctx *ctx) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci emit_instr(ctx, lb, reg, offset, base); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic inline void emit_half_load(unsigned int reg, unsigned int base, 3638c2ecf20Sopenharmony_ci unsigned int offset, struct jit_ctx *ctx) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci emit_instr(ctx, lh, reg, offset, base); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic inline void emit_half_load_unsigned(unsigned int reg, unsigned int base, 3698c2ecf20Sopenharmony_ci unsigned int offset, struct jit_ctx *ctx) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci emit_instr(ctx, lhu, reg, offset, base); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic inline void emit_mul(unsigned int dst, unsigned int src1, 3758c2ecf20Sopenharmony_ci unsigned int src2, struct jit_ctx *ctx) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci emit_instr(ctx, mul, dst, src1, src2); 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic inline void emit_div(unsigned int dst, unsigned int src, 3818c2ecf20Sopenharmony_ci struct jit_ctx *ctx) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci if (ctx->target != NULL) { 3848c2ecf20Sopenharmony_ci u32 *p = &ctx->target[ctx->idx]; 3858c2ecf20Sopenharmony_ci uasm_i_divu(&p, dst, src); 3868c2ecf20Sopenharmony_ci p = &ctx->target[ctx->idx + 1]; 3878c2ecf20Sopenharmony_ci uasm_i_mflo(&p, dst); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci ctx->idx += 2; /* 2 insts */ 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic inline void emit_mod(unsigned int dst, unsigned int src, 3938c2ecf20Sopenharmony_ci struct jit_ctx *ctx) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci if (ctx->target != NULL) { 3968c2ecf20Sopenharmony_ci u32 *p = &ctx->target[ctx->idx]; 3978c2ecf20Sopenharmony_ci uasm_i_divu(&p, dst, src); 3988c2ecf20Sopenharmony_ci p = &ctx->target[ctx->idx + 1]; 3998c2ecf20Sopenharmony_ci uasm_i_mfhi(&p, dst); 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci ctx->idx += 2; /* 2 insts */ 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic inline void emit_dsll(unsigned int dst, unsigned int src, 4058c2ecf20Sopenharmony_ci unsigned int sa, struct jit_ctx *ctx) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci emit_instr(ctx, dsll, dst, src, sa); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic inline void emit_dsrl32(unsigned int dst, unsigned int src, 4118c2ecf20Sopenharmony_ci unsigned int sa, struct jit_ctx *ctx) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci emit_instr(ctx, dsrl32, dst, src, sa); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic inline void emit_wsbh(unsigned int dst, unsigned int src, 4178c2ecf20Sopenharmony_ci struct jit_ctx *ctx) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci emit_instr(ctx, wsbh, dst, src); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci/* load pointer to register */ 4238c2ecf20Sopenharmony_cistatic inline void emit_load_ptr(unsigned int dst, unsigned int src, 4248c2ecf20Sopenharmony_ci int imm, struct jit_ctx *ctx) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci /* src contains the base addr of the 32/64-pointer */ 4278c2ecf20Sopenharmony_ci emit_long_instr(ctx, LW, dst, imm, src); 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci/* load a function pointer to register */ 4318c2ecf20Sopenharmony_cistatic inline void emit_load_func(unsigned int reg, ptr imm, 4328c2ecf20Sopenharmony_ci struct jit_ctx *ctx) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_64BIT)) { 4358c2ecf20Sopenharmony_ci /* At this point imm is always 64-bit */ 4368c2ecf20Sopenharmony_ci emit_load_imm(r_tmp, (u64)imm >> 32, ctx); 4378c2ecf20Sopenharmony_ci emit_dsll(r_tmp_imm, r_tmp, 16, ctx); /* left shift by 16 */ 4388c2ecf20Sopenharmony_ci emit_ori(r_tmp, r_tmp_imm, (imm >> 16) & 0xffff, ctx); 4398c2ecf20Sopenharmony_ci emit_dsll(r_tmp_imm, r_tmp, 16, ctx); /* left shift by 16 */ 4408c2ecf20Sopenharmony_ci emit_ori(reg, r_tmp_imm, imm & 0xffff, ctx); 4418c2ecf20Sopenharmony_ci } else { 4428c2ecf20Sopenharmony_ci emit_load_imm(reg, imm, ctx); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci/* Move to real MIPS register */ 4478c2ecf20Sopenharmony_cistatic inline void emit_reg_move(ptr dst, ptr src, struct jit_ctx *ctx) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci emit_long_instr(ctx, ADDU, dst, src, r_zero); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/* Move to JIT (32-bit) register */ 4538c2ecf20Sopenharmony_cistatic inline void emit_jit_reg_move(ptr dst, ptr src, struct jit_ctx *ctx) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci emit_addu(dst, src, r_zero, ctx); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci/* Compute the immediate value for PC-relative branches. */ 4598c2ecf20Sopenharmony_cistatic inline u32 b_imm(unsigned int tgt, struct jit_ctx *ctx) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci if (ctx->target == NULL) 4628c2ecf20Sopenharmony_ci return 0; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci /* 4658c2ecf20Sopenharmony_ci * We want a pc-relative branch. We only do forward branches 4668c2ecf20Sopenharmony_ci * so tgt is always after pc. tgt is the instruction offset 4678c2ecf20Sopenharmony_ci * we want to jump to. 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci * Branch on MIPS: 4708c2ecf20Sopenharmony_ci * I: target_offset <- sign_extend(offset) 4718c2ecf20Sopenharmony_ci * I+1: PC += target_offset (delay slot) 4728c2ecf20Sopenharmony_ci * 4738c2ecf20Sopenharmony_ci * ctx->idx currently points to the branch instruction 4748c2ecf20Sopenharmony_ci * but the offset is added to the delay slot so we need 4758c2ecf20Sopenharmony_ci * to subtract 4. 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_ci return ctx->offsets[tgt] - 4788c2ecf20Sopenharmony_ci (ctx->idx * 4 - ctx->prologue_bytes) - 4; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic inline void emit_bcond(int cond, unsigned int reg1, unsigned int reg2, 4828c2ecf20Sopenharmony_ci unsigned int imm, struct jit_ctx *ctx) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci if (ctx->target != NULL) { 4858c2ecf20Sopenharmony_ci u32 *p = &ctx->target[ctx->idx]; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci switch (cond) { 4888c2ecf20Sopenharmony_ci case MIPS_COND_EQ: 4898c2ecf20Sopenharmony_ci uasm_i_beq(&p, reg1, reg2, imm); 4908c2ecf20Sopenharmony_ci break; 4918c2ecf20Sopenharmony_ci case MIPS_COND_NE: 4928c2ecf20Sopenharmony_ci uasm_i_bne(&p, reg1, reg2, imm); 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci case MIPS_COND_ALL: 4958c2ecf20Sopenharmony_ci uasm_i_b(&p, imm); 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci default: 4988c2ecf20Sopenharmony_ci pr_warn("%s: Unhandled branch conditional: %d\n", 4998c2ecf20Sopenharmony_ci __func__, cond); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci ctx->idx++; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic inline void emit_b(unsigned int imm, struct jit_ctx *ctx) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_ALL, r_zero, r_zero, imm, ctx); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic inline void emit_jalr(unsigned int link, unsigned int reg, 5118c2ecf20Sopenharmony_ci struct jit_ctx *ctx) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci emit_instr(ctx, jalr, link, reg); 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic inline void emit_jr(unsigned int reg, struct jit_ctx *ctx) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci emit_instr(ctx, jr, reg); 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic inline u16 align_sp(unsigned int num) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci /* Double word alignment for 32-bit, quadword for 64-bit */ 5248c2ecf20Sopenharmony_ci unsigned int align = IS_ENABLED(CONFIG_64BIT) ? 16 : 8; 5258c2ecf20Sopenharmony_ci num = (num + (align - 1)) & -align; 5268c2ecf20Sopenharmony_ci return num; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic void save_bpf_jit_regs(struct jit_ctx *ctx, unsigned offset) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci int i = 0, real_off = 0; 5328c2ecf20Sopenharmony_ci u32 sflags, tmp_flags; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* Adjust the stack pointer */ 5358c2ecf20Sopenharmony_ci if (offset) 5368c2ecf20Sopenharmony_ci emit_stack_offset(-align_sp(offset), ctx); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci tmp_flags = sflags = ctx->flags >> SEEN_SREG_SFT; 5398c2ecf20Sopenharmony_ci /* sflags is essentially a bitmap */ 5408c2ecf20Sopenharmony_ci while (tmp_flags) { 5418c2ecf20Sopenharmony_ci if ((sflags >> i) & 0x1) { 5428c2ecf20Sopenharmony_ci emit_store_stack_reg(MIPS_R_S0 + i, r_sp, real_off, 5438c2ecf20Sopenharmony_ci ctx); 5448c2ecf20Sopenharmony_ci real_off += SZREG; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci i++; 5478c2ecf20Sopenharmony_ci tmp_flags >>= 1; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* save return address */ 5518c2ecf20Sopenharmony_ci if (ctx->flags & SEEN_CALL) { 5528c2ecf20Sopenharmony_ci emit_store_stack_reg(r_ra, r_sp, real_off, ctx); 5538c2ecf20Sopenharmony_ci real_off += SZREG; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* Setup r_M leaving the alignment gap if necessary */ 5578c2ecf20Sopenharmony_ci if (ctx->flags & SEEN_MEM) { 5588c2ecf20Sopenharmony_ci if (real_off % (SZREG * 2)) 5598c2ecf20Sopenharmony_ci real_off += SZREG; 5608c2ecf20Sopenharmony_ci emit_long_instr(ctx, ADDIU, r_M, r_sp, real_off); 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic void restore_bpf_jit_regs(struct jit_ctx *ctx, 5658c2ecf20Sopenharmony_ci unsigned int offset) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci int i, real_off = 0; 5688c2ecf20Sopenharmony_ci u32 sflags, tmp_flags; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci tmp_flags = sflags = ctx->flags >> SEEN_SREG_SFT; 5718c2ecf20Sopenharmony_ci /* sflags is a bitmap */ 5728c2ecf20Sopenharmony_ci i = 0; 5738c2ecf20Sopenharmony_ci while (tmp_flags) { 5748c2ecf20Sopenharmony_ci if ((sflags >> i) & 0x1) { 5758c2ecf20Sopenharmony_ci emit_load_stack_reg(MIPS_R_S0 + i, r_sp, real_off, 5768c2ecf20Sopenharmony_ci ctx); 5778c2ecf20Sopenharmony_ci real_off += SZREG; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci i++; 5808c2ecf20Sopenharmony_ci tmp_flags >>= 1; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* restore return address */ 5848c2ecf20Sopenharmony_ci if (ctx->flags & SEEN_CALL) 5858c2ecf20Sopenharmony_ci emit_load_stack_reg(r_ra, r_sp, real_off, ctx); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* Restore the sp and discard the scrach memory */ 5888c2ecf20Sopenharmony_ci if (offset) 5898c2ecf20Sopenharmony_ci emit_stack_offset(align_sp(offset), ctx); 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic unsigned int get_stack_depth(struct jit_ctx *ctx) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci int sp_off = 0; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* How may s* regs do we need to preserved? */ 5988c2ecf20Sopenharmony_ci sp_off += hweight32(ctx->flags >> SEEN_SREG_SFT) * SZREG; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (ctx->flags & SEEN_MEM) 6018c2ecf20Sopenharmony_ci sp_off += 4 * BPF_MEMWORDS; /* BPF_MEMWORDS are 32-bit */ 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (ctx->flags & SEEN_CALL) 6048c2ecf20Sopenharmony_ci sp_off += SZREG; /* Space for our ra register */ 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci return sp_off; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic void build_prologue(struct jit_ctx *ctx) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci int sp_off; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* Calculate the total offset for the stack pointer */ 6148c2ecf20Sopenharmony_ci sp_off = get_stack_depth(ctx); 6158c2ecf20Sopenharmony_ci save_bpf_jit_regs(ctx, sp_off); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (ctx->flags & SEEN_SKB) 6188c2ecf20Sopenharmony_ci emit_reg_move(r_skb, MIPS_R_A0, ctx); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (ctx->flags & SEEN_SKB_DATA) { 6218c2ecf20Sopenharmony_ci /* Load packet length */ 6228c2ecf20Sopenharmony_ci emit_load(r_skb_len, r_skb, offsetof(struct sk_buff, len), 6238c2ecf20Sopenharmony_ci ctx); 6248c2ecf20Sopenharmony_ci emit_load(r_tmp, r_skb, offsetof(struct sk_buff, data_len), 6258c2ecf20Sopenharmony_ci ctx); 6268c2ecf20Sopenharmony_ci /* Load the data pointer */ 6278c2ecf20Sopenharmony_ci emit_load_ptr(r_skb_data, r_skb, 6288c2ecf20Sopenharmony_ci offsetof(struct sk_buff, data), ctx); 6298c2ecf20Sopenharmony_ci /* Load the header length */ 6308c2ecf20Sopenharmony_ci emit_subu(r_skb_hl, r_skb_len, r_tmp, ctx); 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (ctx->flags & SEEN_X) 6348c2ecf20Sopenharmony_ci emit_jit_reg_move(r_X, r_zero, ctx); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* 6378c2ecf20Sopenharmony_ci * Do not leak kernel data to userspace, we only need to clear 6388c2ecf20Sopenharmony_ci * r_A if it is ever used. In fact if it is never used, we 6398c2ecf20Sopenharmony_ci * will not save/restore it, so clearing it in this case would 6408c2ecf20Sopenharmony_ci * corrupt the state of the caller. 6418c2ecf20Sopenharmony_ci */ 6428c2ecf20Sopenharmony_ci if (bpf_needs_clear_a(&ctx->skf->insns[0]) && 6438c2ecf20Sopenharmony_ci (ctx->flags & SEEN_A)) 6448c2ecf20Sopenharmony_ci emit_jit_reg_move(r_A, r_zero, ctx); 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic void build_epilogue(struct jit_ctx *ctx) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci unsigned int sp_off; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* Calculate the total offset for the stack pointer */ 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci sp_off = get_stack_depth(ctx); 6548c2ecf20Sopenharmony_ci restore_bpf_jit_regs(ctx, sp_off); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* Return */ 6578c2ecf20Sopenharmony_ci emit_jr(r_ra, ctx); 6588c2ecf20Sopenharmony_ci emit_nop(ctx); 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci#define CHOOSE_LOAD_FUNC(K, func) \ 6628c2ecf20Sopenharmony_ci ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative : func) : \ 6638c2ecf20Sopenharmony_ci func##_positive) 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic bool is_bad_offset(int b_off) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci return b_off > 0x1ffff || b_off < -0x20000; 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic int build_body(struct jit_ctx *ctx) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci const struct bpf_prog *prog = ctx->skf; 6738c2ecf20Sopenharmony_ci const struct sock_filter *inst; 6748c2ecf20Sopenharmony_ci unsigned int i, off, condt; 6758c2ecf20Sopenharmony_ci u32 k, b_off __maybe_unused; 6768c2ecf20Sopenharmony_ci u8 (*sk_load_func)(unsigned long *skb, int offset); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci for (i = 0; i < prog->len; i++) { 6798c2ecf20Sopenharmony_ci u16 code; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci inst = &(prog->insns[i]); 6828c2ecf20Sopenharmony_ci pr_debug("%s: code->0x%02x, jt->0x%x, jf->0x%x, k->0x%x\n", 6838c2ecf20Sopenharmony_ci __func__, inst->code, inst->jt, inst->jf, inst->k); 6848c2ecf20Sopenharmony_ci k = inst->k; 6858c2ecf20Sopenharmony_ci code = bpf_anc_helper(inst); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (ctx->target == NULL) 6888c2ecf20Sopenharmony_ci ctx->offsets[i] = ctx->idx * 4; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci switch (code) { 6918c2ecf20Sopenharmony_ci case BPF_LD | BPF_IMM: 6928c2ecf20Sopenharmony_ci /* A <- k ==> li r_A, k */ 6938c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 6948c2ecf20Sopenharmony_ci emit_load_imm(r_A, k, ctx); 6958c2ecf20Sopenharmony_ci break; 6968c2ecf20Sopenharmony_ci case BPF_LD | BPF_W | BPF_LEN: 6978c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct sk_buff, len) != 4); 6988c2ecf20Sopenharmony_ci /* A <- len ==> lw r_A, offset(skb) */ 6998c2ecf20Sopenharmony_ci ctx->flags |= SEEN_SKB | SEEN_A; 7008c2ecf20Sopenharmony_ci off = offsetof(struct sk_buff, len); 7018c2ecf20Sopenharmony_ci emit_load(r_A, r_skb, off, ctx); 7028c2ecf20Sopenharmony_ci break; 7038c2ecf20Sopenharmony_ci case BPF_LD | BPF_MEM: 7048c2ecf20Sopenharmony_ci /* A <- M[k] ==> lw r_A, offset(M) */ 7058c2ecf20Sopenharmony_ci ctx->flags |= SEEN_MEM | SEEN_A; 7068c2ecf20Sopenharmony_ci emit_load(r_A, r_M, SCRATCH_OFF(k), ctx); 7078c2ecf20Sopenharmony_ci break; 7088c2ecf20Sopenharmony_ci case BPF_LD | BPF_W | BPF_ABS: 7098c2ecf20Sopenharmony_ci /* A <- P[k:4] */ 7108c2ecf20Sopenharmony_ci sk_load_func = CHOOSE_LOAD_FUNC(k, sk_load_word); 7118c2ecf20Sopenharmony_ci goto load; 7128c2ecf20Sopenharmony_ci case BPF_LD | BPF_H | BPF_ABS: 7138c2ecf20Sopenharmony_ci /* A <- P[k:2] */ 7148c2ecf20Sopenharmony_ci sk_load_func = CHOOSE_LOAD_FUNC(k, sk_load_half); 7158c2ecf20Sopenharmony_ci goto load; 7168c2ecf20Sopenharmony_ci case BPF_LD | BPF_B | BPF_ABS: 7178c2ecf20Sopenharmony_ci /* A <- P[k:1] */ 7188c2ecf20Sopenharmony_ci sk_load_func = CHOOSE_LOAD_FUNC(k, sk_load_byte); 7198c2ecf20Sopenharmony_ciload: 7208c2ecf20Sopenharmony_ci emit_load_imm(r_off, k, ctx); 7218c2ecf20Sopenharmony_ciload_common: 7228c2ecf20Sopenharmony_ci ctx->flags |= SEEN_CALL | SEEN_OFF | 7238c2ecf20Sopenharmony_ci SEEN_SKB | SEEN_A | SEEN_SKB_DATA; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci emit_load_func(r_s0, (ptr)sk_load_func, ctx); 7268c2ecf20Sopenharmony_ci emit_reg_move(MIPS_R_A0, r_skb, ctx); 7278c2ecf20Sopenharmony_ci emit_jalr(MIPS_R_RA, r_s0, ctx); 7288c2ecf20Sopenharmony_ci /* Load second argument to delay slot */ 7298c2ecf20Sopenharmony_ci emit_reg_move(MIPS_R_A1, r_off, ctx); 7308c2ecf20Sopenharmony_ci /* Check the error value */ 7318c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_EQ, r_ret, 0, b_imm(i + 1, ctx), 7328c2ecf20Sopenharmony_ci ctx); 7338c2ecf20Sopenharmony_ci /* Load return register on DS for failures */ 7348c2ecf20Sopenharmony_ci emit_reg_move(r_ret, r_zero, ctx); 7358c2ecf20Sopenharmony_ci /* Return with error */ 7368c2ecf20Sopenharmony_ci b_off = b_imm(prog->len, ctx); 7378c2ecf20Sopenharmony_ci if (is_bad_offset(b_off)) 7388c2ecf20Sopenharmony_ci return -E2BIG; 7398c2ecf20Sopenharmony_ci emit_b(b_off, ctx); 7408c2ecf20Sopenharmony_ci emit_nop(ctx); 7418c2ecf20Sopenharmony_ci break; 7428c2ecf20Sopenharmony_ci case BPF_LD | BPF_W | BPF_IND: 7438c2ecf20Sopenharmony_ci /* A <- P[X + k:4] */ 7448c2ecf20Sopenharmony_ci sk_load_func = sk_load_word; 7458c2ecf20Sopenharmony_ci goto load_ind; 7468c2ecf20Sopenharmony_ci case BPF_LD | BPF_H | BPF_IND: 7478c2ecf20Sopenharmony_ci /* A <- P[X + k:2] */ 7488c2ecf20Sopenharmony_ci sk_load_func = sk_load_half; 7498c2ecf20Sopenharmony_ci goto load_ind; 7508c2ecf20Sopenharmony_ci case BPF_LD | BPF_B | BPF_IND: 7518c2ecf20Sopenharmony_ci /* A <- P[X + k:1] */ 7528c2ecf20Sopenharmony_ci sk_load_func = sk_load_byte; 7538c2ecf20Sopenharmony_ciload_ind: 7548c2ecf20Sopenharmony_ci ctx->flags |= SEEN_OFF | SEEN_X; 7558c2ecf20Sopenharmony_ci emit_addiu(r_off, r_X, k, ctx); 7568c2ecf20Sopenharmony_ci goto load_common; 7578c2ecf20Sopenharmony_ci case BPF_LDX | BPF_IMM: 7588c2ecf20Sopenharmony_ci /* X <- k */ 7598c2ecf20Sopenharmony_ci ctx->flags |= SEEN_X; 7608c2ecf20Sopenharmony_ci emit_load_imm(r_X, k, ctx); 7618c2ecf20Sopenharmony_ci break; 7628c2ecf20Sopenharmony_ci case BPF_LDX | BPF_MEM: 7638c2ecf20Sopenharmony_ci /* X <- M[k] */ 7648c2ecf20Sopenharmony_ci ctx->flags |= SEEN_X | SEEN_MEM; 7658c2ecf20Sopenharmony_ci emit_load(r_X, r_M, SCRATCH_OFF(k), ctx); 7668c2ecf20Sopenharmony_ci break; 7678c2ecf20Sopenharmony_ci case BPF_LDX | BPF_W | BPF_LEN: 7688c2ecf20Sopenharmony_ci /* X <- len */ 7698c2ecf20Sopenharmony_ci ctx->flags |= SEEN_X | SEEN_SKB; 7708c2ecf20Sopenharmony_ci off = offsetof(struct sk_buff, len); 7718c2ecf20Sopenharmony_ci emit_load(r_X, r_skb, off, ctx); 7728c2ecf20Sopenharmony_ci break; 7738c2ecf20Sopenharmony_ci case BPF_LDX | BPF_B | BPF_MSH: 7748c2ecf20Sopenharmony_ci /* X <- 4 * (P[k:1] & 0xf) */ 7758c2ecf20Sopenharmony_ci ctx->flags |= SEEN_X | SEEN_CALL | SEEN_SKB; 7768c2ecf20Sopenharmony_ci /* Load offset to a1 */ 7778c2ecf20Sopenharmony_ci emit_load_func(r_s0, (ptr)sk_load_byte, ctx); 7788c2ecf20Sopenharmony_ci /* 7798c2ecf20Sopenharmony_ci * This may emit two instructions so it may not fit 7808c2ecf20Sopenharmony_ci * in the delay slot. So use a0 in the delay slot. 7818c2ecf20Sopenharmony_ci */ 7828c2ecf20Sopenharmony_ci emit_load_imm(MIPS_R_A1, k, ctx); 7838c2ecf20Sopenharmony_ci emit_jalr(MIPS_R_RA, r_s0, ctx); 7848c2ecf20Sopenharmony_ci emit_reg_move(MIPS_R_A0, r_skb, ctx); /* delay slot */ 7858c2ecf20Sopenharmony_ci /* Check the error value */ 7868c2ecf20Sopenharmony_ci b_off = b_imm(prog->len, ctx); 7878c2ecf20Sopenharmony_ci if (is_bad_offset(b_off)) 7888c2ecf20Sopenharmony_ci return -E2BIG; 7898c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_NE, r_ret, 0, b_off, ctx); 7908c2ecf20Sopenharmony_ci emit_reg_move(r_ret, r_zero, ctx); 7918c2ecf20Sopenharmony_ci /* We are good */ 7928c2ecf20Sopenharmony_ci /* X <- P[1:K] & 0xf */ 7938c2ecf20Sopenharmony_ci emit_andi(r_X, r_A, 0xf, ctx); 7948c2ecf20Sopenharmony_ci /* X << 2 */ 7958c2ecf20Sopenharmony_ci emit_b(b_imm(i + 1, ctx), ctx); 7968c2ecf20Sopenharmony_ci emit_sll(r_X, r_X, 2, ctx); /* delay slot */ 7978c2ecf20Sopenharmony_ci break; 7988c2ecf20Sopenharmony_ci case BPF_ST: 7998c2ecf20Sopenharmony_ci /* M[k] <- A */ 8008c2ecf20Sopenharmony_ci ctx->flags |= SEEN_MEM | SEEN_A; 8018c2ecf20Sopenharmony_ci emit_store(r_A, r_M, SCRATCH_OFF(k), ctx); 8028c2ecf20Sopenharmony_ci break; 8038c2ecf20Sopenharmony_ci case BPF_STX: 8048c2ecf20Sopenharmony_ci /* M[k] <- X */ 8058c2ecf20Sopenharmony_ci ctx->flags |= SEEN_MEM | SEEN_X; 8068c2ecf20Sopenharmony_ci emit_store(r_X, r_M, SCRATCH_OFF(k), ctx); 8078c2ecf20Sopenharmony_ci break; 8088c2ecf20Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_K: 8098c2ecf20Sopenharmony_ci /* A += K */ 8108c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 8118c2ecf20Sopenharmony_ci emit_addiu(r_A, r_A, k, ctx); 8128c2ecf20Sopenharmony_ci break; 8138c2ecf20Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_X: 8148c2ecf20Sopenharmony_ci /* A += X */ 8158c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A | SEEN_X; 8168c2ecf20Sopenharmony_ci emit_addu(r_A, r_A, r_X, ctx); 8178c2ecf20Sopenharmony_ci break; 8188c2ecf20Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_K: 8198c2ecf20Sopenharmony_ci /* A -= K */ 8208c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 8218c2ecf20Sopenharmony_ci emit_addiu(r_A, r_A, -k, ctx); 8228c2ecf20Sopenharmony_ci break; 8238c2ecf20Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_X: 8248c2ecf20Sopenharmony_ci /* A -= X */ 8258c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A | SEEN_X; 8268c2ecf20Sopenharmony_ci emit_subu(r_A, r_A, r_X, ctx); 8278c2ecf20Sopenharmony_ci break; 8288c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_K: 8298c2ecf20Sopenharmony_ci /* A *= K */ 8308c2ecf20Sopenharmony_ci /* Load K to scratch register before MUL */ 8318c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 8328c2ecf20Sopenharmony_ci emit_load_imm(r_s0, k, ctx); 8338c2ecf20Sopenharmony_ci emit_mul(r_A, r_A, r_s0, ctx); 8348c2ecf20Sopenharmony_ci break; 8358c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_X: 8368c2ecf20Sopenharmony_ci /* A *= X */ 8378c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A | SEEN_X; 8388c2ecf20Sopenharmony_ci emit_mul(r_A, r_A, r_X, ctx); 8398c2ecf20Sopenharmony_ci break; 8408c2ecf20Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_K: 8418c2ecf20Sopenharmony_ci /* A /= k */ 8428c2ecf20Sopenharmony_ci if (k == 1) 8438c2ecf20Sopenharmony_ci break; 8448c2ecf20Sopenharmony_ci if (optimize_div(&k)) { 8458c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 8468c2ecf20Sopenharmony_ci emit_srl(r_A, r_A, k, ctx); 8478c2ecf20Sopenharmony_ci break; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 8508c2ecf20Sopenharmony_ci emit_load_imm(r_s0, k, ctx); 8518c2ecf20Sopenharmony_ci emit_div(r_A, r_s0, ctx); 8528c2ecf20Sopenharmony_ci break; 8538c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_K: 8548c2ecf20Sopenharmony_ci /* A %= k */ 8558c2ecf20Sopenharmony_ci if (k == 1) { 8568c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 8578c2ecf20Sopenharmony_ci emit_jit_reg_move(r_A, r_zero, ctx); 8588c2ecf20Sopenharmony_ci } else { 8598c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 8608c2ecf20Sopenharmony_ci emit_load_imm(r_s0, k, ctx); 8618c2ecf20Sopenharmony_ci emit_mod(r_A, r_s0, ctx); 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci break; 8648c2ecf20Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_X: 8658c2ecf20Sopenharmony_ci /* A /= X */ 8668c2ecf20Sopenharmony_ci ctx->flags |= SEEN_X | SEEN_A; 8678c2ecf20Sopenharmony_ci /* Check if r_X is zero */ 8688c2ecf20Sopenharmony_ci b_off = b_imm(prog->len, ctx); 8698c2ecf20Sopenharmony_ci if (is_bad_offset(b_off)) 8708c2ecf20Sopenharmony_ci return -E2BIG; 8718c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_EQ, r_X, r_zero, b_off, ctx); 8728c2ecf20Sopenharmony_ci emit_load_imm(r_ret, 0, ctx); /* delay slot */ 8738c2ecf20Sopenharmony_ci emit_div(r_A, r_X, ctx); 8748c2ecf20Sopenharmony_ci break; 8758c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_X: 8768c2ecf20Sopenharmony_ci /* A %= X */ 8778c2ecf20Sopenharmony_ci ctx->flags |= SEEN_X | SEEN_A; 8788c2ecf20Sopenharmony_ci /* Check if r_X is zero */ 8798c2ecf20Sopenharmony_ci b_off = b_imm(prog->len, ctx); 8808c2ecf20Sopenharmony_ci if (is_bad_offset(b_off)) 8818c2ecf20Sopenharmony_ci return -E2BIG; 8828c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_EQ, r_X, r_zero, b_off, ctx); 8838c2ecf20Sopenharmony_ci emit_load_imm(r_ret, 0, ctx); /* delay slot */ 8848c2ecf20Sopenharmony_ci emit_mod(r_A, r_X, ctx); 8858c2ecf20Sopenharmony_ci break; 8868c2ecf20Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_K: 8878c2ecf20Sopenharmony_ci /* A |= K */ 8888c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 8898c2ecf20Sopenharmony_ci emit_ori(r_A, r_A, k, ctx); 8908c2ecf20Sopenharmony_ci break; 8918c2ecf20Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_X: 8928c2ecf20Sopenharmony_ci /* A |= X */ 8938c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 8948c2ecf20Sopenharmony_ci emit_ori(r_A, r_A, r_X, ctx); 8958c2ecf20Sopenharmony_ci break; 8968c2ecf20Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_K: 8978c2ecf20Sopenharmony_ci /* A ^= k */ 8988c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 8998c2ecf20Sopenharmony_ci emit_xori(r_A, r_A, k, ctx); 9008c2ecf20Sopenharmony_ci break; 9018c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_ALU_XOR_X: 9028c2ecf20Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_X: 9038c2ecf20Sopenharmony_ci /* A ^= X */ 9048c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 9058c2ecf20Sopenharmony_ci emit_xor(r_A, r_A, r_X, ctx); 9068c2ecf20Sopenharmony_ci break; 9078c2ecf20Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_K: 9088c2ecf20Sopenharmony_ci /* A &= K */ 9098c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 9108c2ecf20Sopenharmony_ci emit_andi(r_A, r_A, k, ctx); 9118c2ecf20Sopenharmony_ci break; 9128c2ecf20Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_X: 9138c2ecf20Sopenharmony_ci /* A &= X */ 9148c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A | SEEN_X; 9158c2ecf20Sopenharmony_ci emit_and(r_A, r_A, r_X, ctx); 9168c2ecf20Sopenharmony_ci break; 9178c2ecf20Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_K: 9188c2ecf20Sopenharmony_ci /* A <<= K */ 9198c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 9208c2ecf20Sopenharmony_ci emit_sll(r_A, r_A, k, ctx); 9218c2ecf20Sopenharmony_ci break; 9228c2ecf20Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_X: 9238c2ecf20Sopenharmony_ci /* A <<= X */ 9248c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A | SEEN_X; 9258c2ecf20Sopenharmony_ci emit_sllv(r_A, r_A, r_X, ctx); 9268c2ecf20Sopenharmony_ci break; 9278c2ecf20Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_K: 9288c2ecf20Sopenharmony_ci /* A >>= K */ 9298c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 9308c2ecf20Sopenharmony_ci emit_srl(r_A, r_A, k, ctx); 9318c2ecf20Sopenharmony_ci break; 9328c2ecf20Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_X: 9338c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A | SEEN_X; 9348c2ecf20Sopenharmony_ci emit_srlv(r_A, r_A, r_X, ctx); 9358c2ecf20Sopenharmony_ci break; 9368c2ecf20Sopenharmony_ci case BPF_ALU | BPF_NEG: 9378c2ecf20Sopenharmony_ci /* A = -A */ 9388c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 9398c2ecf20Sopenharmony_ci emit_neg(r_A, ctx); 9408c2ecf20Sopenharmony_ci break; 9418c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JA: 9428c2ecf20Sopenharmony_ci /* pc += K */ 9438c2ecf20Sopenharmony_ci b_off = b_imm(i + k + 1, ctx); 9448c2ecf20Sopenharmony_ci if (is_bad_offset(b_off)) 9458c2ecf20Sopenharmony_ci return -E2BIG; 9468c2ecf20Sopenharmony_ci emit_b(b_off, ctx); 9478c2ecf20Sopenharmony_ci emit_nop(ctx); 9488c2ecf20Sopenharmony_ci break; 9498c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_K: 9508c2ecf20Sopenharmony_ci /* pc += ( A == K ) ? pc->jt : pc->jf */ 9518c2ecf20Sopenharmony_ci condt = MIPS_COND_EQ | MIPS_COND_K; 9528c2ecf20Sopenharmony_ci goto jmp_cmp; 9538c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_X: 9548c2ecf20Sopenharmony_ci ctx->flags |= SEEN_X; 9558c2ecf20Sopenharmony_ci /* pc += ( A == X ) ? pc->jt : pc->jf */ 9568c2ecf20Sopenharmony_ci condt = MIPS_COND_EQ | MIPS_COND_X; 9578c2ecf20Sopenharmony_ci goto jmp_cmp; 9588c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_K: 9598c2ecf20Sopenharmony_ci /* pc += ( A >= K ) ? pc->jt : pc->jf */ 9608c2ecf20Sopenharmony_ci condt = MIPS_COND_GE | MIPS_COND_K; 9618c2ecf20Sopenharmony_ci goto jmp_cmp; 9628c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_X: 9638c2ecf20Sopenharmony_ci ctx->flags |= SEEN_X; 9648c2ecf20Sopenharmony_ci /* pc += ( A >= X ) ? pc->jt : pc->jf */ 9658c2ecf20Sopenharmony_ci condt = MIPS_COND_GE | MIPS_COND_X; 9668c2ecf20Sopenharmony_ci goto jmp_cmp; 9678c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_K: 9688c2ecf20Sopenharmony_ci /* pc += ( A > K ) ? pc->jt : pc->jf */ 9698c2ecf20Sopenharmony_ci condt = MIPS_COND_GT | MIPS_COND_K; 9708c2ecf20Sopenharmony_ci goto jmp_cmp; 9718c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_X: 9728c2ecf20Sopenharmony_ci ctx->flags |= SEEN_X; 9738c2ecf20Sopenharmony_ci /* pc += ( A > X ) ? pc->jt : pc->jf */ 9748c2ecf20Sopenharmony_ci condt = MIPS_COND_GT | MIPS_COND_X; 9758c2ecf20Sopenharmony_cijmp_cmp: 9768c2ecf20Sopenharmony_ci /* Greater or Equal */ 9778c2ecf20Sopenharmony_ci if ((condt & MIPS_COND_GE) || 9788c2ecf20Sopenharmony_ci (condt & MIPS_COND_GT)) { 9798c2ecf20Sopenharmony_ci if (condt & MIPS_COND_K) { /* K */ 9808c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 9818c2ecf20Sopenharmony_ci emit_sltiu(r_s0, r_A, k, ctx); 9828c2ecf20Sopenharmony_ci } else { /* X */ 9838c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A | 9848c2ecf20Sopenharmony_ci SEEN_X; 9858c2ecf20Sopenharmony_ci emit_sltu(r_s0, r_A, r_X, ctx); 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci /* A < (K|X) ? r_scrach = 1 */ 9888c2ecf20Sopenharmony_ci b_off = b_imm(i + inst->jf + 1, ctx); 9898c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_NE, r_s0, r_zero, b_off, 9908c2ecf20Sopenharmony_ci ctx); 9918c2ecf20Sopenharmony_ci emit_nop(ctx); 9928c2ecf20Sopenharmony_ci /* A > (K|X) ? scratch = 0 */ 9938c2ecf20Sopenharmony_ci if (condt & MIPS_COND_GT) { 9948c2ecf20Sopenharmony_ci /* Checking for equality */ 9958c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A | SEEN_X; 9968c2ecf20Sopenharmony_ci if (condt & MIPS_COND_K) 9978c2ecf20Sopenharmony_ci emit_load_imm(r_s0, k, ctx); 9988c2ecf20Sopenharmony_ci else 9998c2ecf20Sopenharmony_ci emit_jit_reg_move(r_s0, r_X, 10008c2ecf20Sopenharmony_ci ctx); 10018c2ecf20Sopenharmony_ci b_off = b_imm(i + inst->jf + 1, ctx); 10028c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_EQ, r_A, r_s0, 10038c2ecf20Sopenharmony_ci b_off, ctx); 10048c2ecf20Sopenharmony_ci emit_nop(ctx); 10058c2ecf20Sopenharmony_ci /* Finally, A > K|X */ 10068c2ecf20Sopenharmony_ci b_off = b_imm(i + inst->jt + 1, ctx); 10078c2ecf20Sopenharmony_ci emit_b(b_off, ctx); 10088c2ecf20Sopenharmony_ci emit_nop(ctx); 10098c2ecf20Sopenharmony_ci } else { 10108c2ecf20Sopenharmony_ci /* A >= (K|X) so jump */ 10118c2ecf20Sopenharmony_ci b_off = b_imm(i + inst->jt + 1, ctx); 10128c2ecf20Sopenharmony_ci emit_b(b_off, ctx); 10138c2ecf20Sopenharmony_ci emit_nop(ctx); 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci } else { 10168c2ecf20Sopenharmony_ci /* A == K|X */ 10178c2ecf20Sopenharmony_ci if (condt & MIPS_COND_K) { /* K */ 10188c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 10198c2ecf20Sopenharmony_ci emit_load_imm(r_s0, k, ctx); 10208c2ecf20Sopenharmony_ci /* jump true */ 10218c2ecf20Sopenharmony_ci b_off = b_imm(i + inst->jt + 1, ctx); 10228c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_EQ, r_A, r_s0, 10238c2ecf20Sopenharmony_ci b_off, ctx); 10248c2ecf20Sopenharmony_ci emit_nop(ctx); 10258c2ecf20Sopenharmony_ci /* jump false */ 10268c2ecf20Sopenharmony_ci b_off = b_imm(i + inst->jf + 1, 10278c2ecf20Sopenharmony_ci ctx); 10288c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_NE, r_A, r_s0, 10298c2ecf20Sopenharmony_ci b_off, ctx); 10308c2ecf20Sopenharmony_ci emit_nop(ctx); 10318c2ecf20Sopenharmony_ci } else { /* X */ 10328c2ecf20Sopenharmony_ci /* jump true */ 10338c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A | SEEN_X; 10348c2ecf20Sopenharmony_ci b_off = b_imm(i + inst->jt + 1, 10358c2ecf20Sopenharmony_ci ctx); 10368c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_EQ, r_A, r_X, 10378c2ecf20Sopenharmony_ci b_off, ctx); 10388c2ecf20Sopenharmony_ci emit_nop(ctx); 10398c2ecf20Sopenharmony_ci /* jump false */ 10408c2ecf20Sopenharmony_ci b_off = b_imm(i + inst->jf + 1, ctx); 10418c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_NE, r_A, r_X, 10428c2ecf20Sopenharmony_ci b_off, ctx); 10438c2ecf20Sopenharmony_ci emit_nop(ctx); 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci break; 10478c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_K: 10488c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 10498c2ecf20Sopenharmony_ci /* pc += (A & K) ? pc -> jt : pc -> jf */ 10508c2ecf20Sopenharmony_ci emit_load_imm(r_s1, k, ctx); 10518c2ecf20Sopenharmony_ci emit_and(r_s0, r_A, r_s1, ctx); 10528c2ecf20Sopenharmony_ci /* jump true */ 10538c2ecf20Sopenharmony_ci b_off = b_imm(i + inst->jt + 1, ctx); 10548c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_NE, r_s0, r_zero, b_off, ctx); 10558c2ecf20Sopenharmony_ci emit_nop(ctx); 10568c2ecf20Sopenharmony_ci /* jump false */ 10578c2ecf20Sopenharmony_ci b_off = b_imm(i + inst->jf + 1, ctx); 10588c2ecf20Sopenharmony_ci emit_b(b_off, ctx); 10598c2ecf20Sopenharmony_ci emit_nop(ctx); 10608c2ecf20Sopenharmony_ci break; 10618c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_X: 10628c2ecf20Sopenharmony_ci ctx->flags |= SEEN_X | SEEN_A; 10638c2ecf20Sopenharmony_ci /* pc += (A & X) ? pc -> jt : pc -> jf */ 10648c2ecf20Sopenharmony_ci emit_and(r_s0, r_A, r_X, ctx); 10658c2ecf20Sopenharmony_ci /* jump true */ 10668c2ecf20Sopenharmony_ci b_off = b_imm(i + inst->jt + 1, ctx); 10678c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_NE, r_s0, r_zero, b_off, ctx); 10688c2ecf20Sopenharmony_ci emit_nop(ctx); 10698c2ecf20Sopenharmony_ci /* jump false */ 10708c2ecf20Sopenharmony_ci b_off = b_imm(i + inst->jf + 1, ctx); 10718c2ecf20Sopenharmony_ci emit_b(b_off, ctx); 10728c2ecf20Sopenharmony_ci emit_nop(ctx); 10738c2ecf20Sopenharmony_ci break; 10748c2ecf20Sopenharmony_ci case BPF_RET | BPF_A: 10758c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A; 10768c2ecf20Sopenharmony_ci if (i != prog->len - 1) { 10778c2ecf20Sopenharmony_ci /* 10788c2ecf20Sopenharmony_ci * If this is not the last instruction 10798c2ecf20Sopenharmony_ci * then jump to the epilogue 10808c2ecf20Sopenharmony_ci */ 10818c2ecf20Sopenharmony_ci b_off = b_imm(prog->len, ctx); 10828c2ecf20Sopenharmony_ci if (is_bad_offset(b_off)) 10838c2ecf20Sopenharmony_ci return -E2BIG; 10848c2ecf20Sopenharmony_ci emit_b(b_off, ctx); 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci emit_reg_move(r_ret, r_A, ctx); /* delay slot */ 10878c2ecf20Sopenharmony_ci break; 10888c2ecf20Sopenharmony_ci case BPF_RET | BPF_K: 10898c2ecf20Sopenharmony_ci /* 10908c2ecf20Sopenharmony_ci * It can emit two instructions so it does not fit on 10918c2ecf20Sopenharmony_ci * the delay slot. 10928c2ecf20Sopenharmony_ci */ 10938c2ecf20Sopenharmony_ci emit_load_imm(r_ret, k, ctx); 10948c2ecf20Sopenharmony_ci if (i != prog->len - 1) { 10958c2ecf20Sopenharmony_ci /* 10968c2ecf20Sopenharmony_ci * If this is not the last instruction 10978c2ecf20Sopenharmony_ci * then jump to the epilogue 10988c2ecf20Sopenharmony_ci */ 10998c2ecf20Sopenharmony_ci b_off = b_imm(prog->len, ctx); 11008c2ecf20Sopenharmony_ci if (is_bad_offset(b_off)) 11018c2ecf20Sopenharmony_ci return -E2BIG; 11028c2ecf20Sopenharmony_ci emit_b(b_off, ctx); 11038c2ecf20Sopenharmony_ci emit_nop(ctx); 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci break; 11068c2ecf20Sopenharmony_ci case BPF_MISC | BPF_TAX: 11078c2ecf20Sopenharmony_ci /* X = A */ 11088c2ecf20Sopenharmony_ci ctx->flags |= SEEN_X | SEEN_A; 11098c2ecf20Sopenharmony_ci emit_jit_reg_move(r_X, r_A, ctx); 11108c2ecf20Sopenharmony_ci break; 11118c2ecf20Sopenharmony_ci case BPF_MISC | BPF_TXA: 11128c2ecf20Sopenharmony_ci /* A = X */ 11138c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A | SEEN_X; 11148c2ecf20Sopenharmony_ci emit_jit_reg_move(r_A, r_X, ctx); 11158c2ecf20Sopenharmony_ci break; 11168c2ecf20Sopenharmony_ci /* AUX */ 11178c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_PROTOCOL: 11188c2ecf20Sopenharmony_ci /* A = ntohs(skb->protocol */ 11198c2ecf20Sopenharmony_ci ctx->flags |= SEEN_SKB | SEEN_OFF | SEEN_A; 11208c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct sk_buff, 11218c2ecf20Sopenharmony_ci protocol) != 2); 11228c2ecf20Sopenharmony_ci off = offsetof(struct sk_buff, protocol); 11238c2ecf20Sopenharmony_ci emit_half_load(r_A, r_skb, off, ctx); 11248c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_LITTLE_ENDIAN 11258c2ecf20Sopenharmony_ci /* This needs little endian fixup */ 11268c2ecf20Sopenharmony_ci if (cpu_has_wsbh) { 11278c2ecf20Sopenharmony_ci /* R2 and later have the wsbh instruction */ 11288c2ecf20Sopenharmony_ci emit_wsbh(r_A, r_A, ctx); 11298c2ecf20Sopenharmony_ci } else { 11308c2ecf20Sopenharmony_ci /* Get first byte */ 11318c2ecf20Sopenharmony_ci emit_andi(r_tmp_imm, r_A, 0xff, ctx); 11328c2ecf20Sopenharmony_ci /* Shift it */ 11338c2ecf20Sopenharmony_ci emit_sll(r_tmp, r_tmp_imm, 8, ctx); 11348c2ecf20Sopenharmony_ci /* Get second byte */ 11358c2ecf20Sopenharmony_ci emit_srl(r_tmp_imm, r_A, 8, ctx); 11368c2ecf20Sopenharmony_ci emit_andi(r_tmp_imm, r_tmp_imm, 0xff, ctx); 11378c2ecf20Sopenharmony_ci /* Put everyting together in r_A */ 11388c2ecf20Sopenharmony_ci emit_or(r_A, r_tmp, r_tmp_imm, ctx); 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci#endif 11418c2ecf20Sopenharmony_ci break; 11428c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_CPU: 11438c2ecf20Sopenharmony_ci ctx->flags |= SEEN_A | SEEN_OFF; 11448c2ecf20Sopenharmony_ci /* A = current_thread_info()->cpu */ 11458c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct thread_info, 11468c2ecf20Sopenharmony_ci cpu) != 4); 11478c2ecf20Sopenharmony_ci off = offsetof(struct thread_info, cpu); 11488c2ecf20Sopenharmony_ci /* $28/gp points to the thread_info struct */ 11498c2ecf20Sopenharmony_ci emit_load(r_A, 28, off, ctx); 11508c2ecf20Sopenharmony_ci break; 11518c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_IFINDEX: 11528c2ecf20Sopenharmony_ci /* A = skb->dev->ifindex */ 11538c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_HATYPE: 11548c2ecf20Sopenharmony_ci /* A = skb->dev->type */ 11558c2ecf20Sopenharmony_ci ctx->flags |= SEEN_SKB | SEEN_A; 11568c2ecf20Sopenharmony_ci off = offsetof(struct sk_buff, dev); 11578c2ecf20Sopenharmony_ci /* Load *dev pointer */ 11588c2ecf20Sopenharmony_ci emit_load_ptr(r_s0, r_skb, off, ctx); 11598c2ecf20Sopenharmony_ci /* error (0) in the delay slot */ 11608c2ecf20Sopenharmony_ci b_off = b_imm(prog->len, ctx); 11618c2ecf20Sopenharmony_ci if (is_bad_offset(b_off)) 11628c2ecf20Sopenharmony_ci return -E2BIG; 11638c2ecf20Sopenharmony_ci emit_bcond(MIPS_COND_EQ, r_s0, r_zero, b_off, ctx); 11648c2ecf20Sopenharmony_ci emit_reg_move(r_ret, r_zero, ctx); 11658c2ecf20Sopenharmony_ci if (code == (BPF_ANC | SKF_AD_IFINDEX)) { 11668c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct net_device, ifindex) != 4); 11678c2ecf20Sopenharmony_ci off = offsetof(struct net_device, ifindex); 11688c2ecf20Sopenharmony_ci emit_load(r_A, r_s0, off, ctx); 11698c2ecf20Sopenharmony_ci } else { /* (code == (BPF_ANC | SKF_AD_HATYPE) */ 11708c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct net_device, type) != 2); 11718c2ecf20Sopenharmony_ci off = offsetof(struct net_device, type); 11728c2ecf20Sopenharmony_ci emit_half_load_unsigned(r_A, r_s0, off, ctx); 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci break; 11758c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_MARK: 11768c2ecf20Sopenharmony_ci ctx->flags |= SEEN_SKB | SEEN_A; 11778c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct sk_buff, mark) != 4); 11788c2ecf20Sopenharmony_ci off = offsetof(struct sk_buff, mark); 11798c2ecf20Sopenharmony_ci emit_load(r_A, r_skb, off, ctx); 11808c2ecf20Sopenharmony_ci break; 11818c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_RXHASH: 11828c2ecf20Sopenharmony_ci ctx->flags |= SEEN_SKB | SEEN_A; 11838c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct sk_buff, hash) != 4); 11848c2ecf20Sopenharmony_ci off = offsetof(struct sk_buff, hash); 11858c2ecf20Sopenharmony_ci emit_load(r_A, r_skb, off, ctx); 11868c2ecf20Sopenharmony_ci break; 11878c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_VLAN_TAG: 11888c2ecf20Sopenharmony_ci ctx->flags |= SEEN_SKB | SEEN_A; 11898c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct sk_buff, 11908c2ecf20Sopenharmony_ci vlan_tci) != 2); 11918c2ecf20Sopenharmony_ci off = offsetof(struct sk_buff, vlan_tci); 11928c2ecf20Sopenharmony_ci emit_half_load_unsigned(r_A, r_skb, off, ctx); 11938c2ecf20Sopenharmony_ci break; 11948c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT: 11958c2ecf20Sopenharmony_ci ctx->flags |= SEEN_SKB | SEEN_A; 11968c2ecf20Sopenharmony_ci emit_load_byte(r_A, r_skb, PKT_VLAN_PRESENT_OFFSET(), ctx); 11978c2ecf20Sopenharmony_ci if (PKT_VLAN_PRESENT_BIT) 11988c2ecf20Sopenharmony_ci emit_srl(r_A, r_A, PKT_VLAN_PRESENT_BIT, ctx); 11998c2ecf20Sopenharmony_ci if (PKT_VLAN_PRESENT_BIT < 7) 12008c2ecf20Sopenharmony_ci emit_andi(r_A, r_A, 1, ctx); 12018c2ecf20Sopenharmony_ci break; 12028c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_PKTTYPE: 12038c2ecf20Sopenharmony_ci ctx->flags |= SEEN_SKB; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci emit_load_byte(r_tmp, r_skb, PKT_TYPE_OFFSET(), ctx); 12068c2ecf20Sopenharmony_ci /* Keep only the last 3 bits */ 12078c2ecf20Sopenharmony_ci emit_andi(r_A, r_tmp, PKT_TYPE_MAX, ctx); 12088c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD 12098c2ecf20Sopenharmony_ci /* Get the actual packet type to the lower 3 bits */ 12108c2ecf20Sopenharmony_ci emit_srl(r_A, r_A, 5, ctx); 12118c2ecf20Sopenharmony_ci#endif 12128c2ecf20Sopenharmony_ci break; 12138c2ecf20Sopenharmony_ci case BPF_ANC | SKF_AD_QUEUE: 12148c2ecf20Sopenharmony_ci ctx->flags |= SEEN_SKB | SEEN_A; 12158c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct sk_buff, 12168c2ecf20Sopenharmony_ci queue_mapping) != 2); 12178c2ecf20Sopenharmony_ci BUILD_BUG_ON(offsetof(struct sk_buff, 12188c2ecf20Sopenharmony_ci queue_mapping) > 0xff); 12198c2ecf20Sopenharmony_ci off = offsetof(struct sk_buff, queue_mapping); 12208c2ecf20Sopenharmony_ci emit_half_load_unsigned(r_A, r_skb, off, ctx); 12218c2ecf20Sopenharmony_ci break; 12228c2ecf20Sopenharmony_ci default: 12238c2ecf20Sopenharmony_ci pr_debug("%s: Unhandled opcode: 0x%02x\n", __FILE__, 12248c2ecf20Sopenharmony_ci inst->code); 12258c2ecf20Sopenharmony_ci return -1; 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci /* compute offsets only during the first pass */ 12308c2ecf20Sopenharmony_ci if (ctx->target == NULL) 12318c2ecf20Sopenharmony_ci ctx->offsets[i] = ctx->idx * 4; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci return 0; 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_civoid bpf_jit_compile(struct bpf_prog *fp) 12378c2ecf20Sopenharmony_ci{ 12388c2ecf20Sopenharmony_ci struct jit_ctx ctx; 12398c2ecf20Sopenharmony_ci unsigned int alloc_size, tmp_idx; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci if (!bpf_jit_enable) 12428c2ecf20Sopenharmony_ci return; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci memset(&ctx, 0, sizeof(ctx)); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci ctx.offsets = kcalloc(fp->len + 1, sizeof(*ctx.offsets), GFP_KERNEL); 12478c2ecf20Sopenharmony_ci if (ctx.offsets == NULL) 12488c2ecf20Sopenharmony_ci return; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci ctx.skf = fp; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if (build_body(&ctx)) 12538c2ecf20Sopenharmony_ci goto out; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci tmp_idx = ctx.idx; 12568c2ecf20Sopenharmony_ci build_prologue(&ctx); 12578c2ecf20Sopenharmony_ci ctx.prologue_bytes = (ctx.idx - tmp_idx) * 4; 12588c2ecf20Sopenharmony_ci /* just to complete the ctx.idx count */ 12598c2ecf20Sopenharmony_ci build_epilogue(&ctx); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci alloc_size = 4 * ctx.idx; 12628c2ecf20Sopenharmony_ci ctx.target = module_alloc(alloc_size); 12638c2ecf20Sopenharmony_ci if (ctx.target == NULL) 12648c2ecf20Sopenharmony_ci goto out; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* Clean it */ 12678c2ecf20Sopenharmony_ci memset(ctx.target, 0, alloc_size); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci ctx.idx = 0; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci /* Generate the actual JIT code */ 12728c2ecf20Sopenharmony_ci build_prologue(&ctx); 12738c2ecf20Sopenharmony_ci if (build_body(&ctx)) { 12748c2ecf20Sopenharmony_ci module_memfree(ctx.target); 12758c2ecf20Sopenharmony_ci goto out; 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci build_epilogue(&ctx); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci /* Update the icache */ 12808c2ecf20Sopenharmony_ci flush_icache_range((ptr)ctx.target, (ptr)(ctx.target + ctx.idx)); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci if (bpf_jit_enable > 1) 12838c2ecf20Sopenharmony_ci /* Dump JIT code */ 12848c2ecf20Sopenharmony_ci bpf_jit_dump(fp->len, alloc_size, 2, ctx.target); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci fp->bpf_func = (void *)ctx.target; 12878c2ecf20Sopenharmony_ci fp->jited = 1; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ciout: 12908c2ecf20Sopenharmony_ci kfree(ctx.offsets); 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_civoid bpf_jit_free(struct bpf_prog *fp) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci if (fp->jited) 12968c2ecf20Sopenharmony_ci module_memfree(fp->bpf_func); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci bpf_prog_unlock_free(fp); 12998c2ecf20Sopenharmony_ci} 1300