18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 28c2ecf20Sopenharmony_ci/* Copyright (C) 2016-2018 Netronome Systems, Inc. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "NFP net bpf: " fmt 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/bug.h> 78c2ecf20Sopenharmony_ci#include <linux/bpf.h> 88c2ecf20Sopenharmony_ci#include <linux/filter.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/pkt_cls.h> 118c2ecf20Sopenharmony_ci#include <linux/reciprocal_div.h> 128c2ecf20Sopenharmony_ci#include <linux/unistd.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "main.h" 158c2ecf20Sopenharmony_ci#include "../nfp_asm.h" 168c2ecf20Sopenharmony_ci#include "../nfp_net_ctrl.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* --- NFP prog --- */ 198c2ecf20Sopenharmony_ci/* Foreach "multiple" entries macros provide pos and next<n> pointers. 208c2ecf20Sopenharmony_ci * It's safe to modify the next pointers (but not pos). 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci#define nfp_for_each_insn_walk2(nfp_prog, pos, next) \ 238c2ecf20Sopenharmony_ci for (pos = list_first_entry(&(nfp_prog)->insns, typeof(*pos), l), \ 248c2ecf20Sopenharmony_ci next = list_next_entry(pos, l); \ 258c2ecf20Sopenharmony_ci &(nfp_prog)->insns != &pos->l && \ 268c2ecf20Sopenharmony_ci &(nfp_prog)->insns != &next->l; \ 278c2ecf20Sopenharmony_ci pos = nfp_meta_next(pos), \ 288c2ecf20Sopenharmony_ci next = nfp_meta_next(pos)) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define nfp_for_each_insn_walk3(nfp_prog, pos, next, next2) \ 318c2ecf20Sopenharmony_ci for (pos = list_first_entry(&(nfp_prog)->insns, typeof(*pos), l), \ 328c2ecf20Sopenharmony_ci next = list_next_entry(pos, l), \ 338c2ecf20Sopenharmony_ci next2 = list_next_entry(next, l); \ 348c2ecf20Sopenharmony_ci &(nfp_prog)->insns != &pos->l && \ 358c2ecf20Sopenharmony_ci &(nfp_prog)->insns != &next->l && \ 368c2ecf20Sopenharmony_ci &(nfp_prog)->insns != &next2->l; \ 378c2ecf20Sopenharmony_ci pos = nfp_meta_next(pos), \ 388c2ecf20Sopenharmony_ci next = nfp_meta_next(pos), \ 398c2ecf20Sopenharmony_ci next2 = nfp_meta_next(next)) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic bool 428c2ecf20Sopenharmony_cinfp_meta_has_prev(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci return meta->l.prev != &nfp_prog->insns; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic void nfp_prog_push(struct nfp_prog *nfp_prog, u64 insn) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci if (nfp_prog->__prog_alloc_len / sizeof(u64) == nfp_prog->prog_len) { 508c2ecf20Sopenharmony_ci pr_warn("instruction limit reached (%u NFP instructions)\n", 518c2ecf20Sopenharmony_ci nfp_prog->prog_len); 528c2ecf20Sopenharmony_ci nfp_prog->error = -ENOSPC; 538c2ecf20Sopenharmony_ci return; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci nfp_prog->prog[nfp_prog->prog_len] = insn; 578c2ecf20Sopenharmony_ci nfp_prog->prog_len++; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic unsigned int nfp_prog_current_offset(struct nfp_prog *nfp_prog) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci return nfp_prog->prog_len; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic bool 668c2ecf20Sopenharmony_cinfp_prog_confirm_current_offset(struct nfp_prog *nfp_prog, unsigned int off) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci /* If there is a recorded error we may have dropped instructions; 698c2ecf20Sopenharmony_ci * that doesn't have to be due to translator bug, and the translation 708c2ecf20Sopenharmony_ci * will fail anyway, so just return OK. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci if (nfp_prog->error) 738c2ecf20Sopenharmony_ci return true; 748c2ecf20Sopenharmony_ci return !WARN_ON_ONCE(nfp_prog_current_offset(nfp_prog) != off); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* --- Emitters --- */ 788c2ecf20Sopenharmony_cistatic void 798c2ecf20Sopenharmony_ci__emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, 808c2ecf20Sopenharmony_ci u8 mode, u8 xfer, u8 areg, u8 breg, u8 size, enum cmd_ctx_swap ctx, 818c2ecf20Sopenharmony_ci bool indir) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci u64 insn; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci insn = FIELD_PREP(OP_CMD_A_SRC, areg) | 868c2ecf20Sopenharmony_ci FIELD_PREP(OP_CMD_CTX, ctx) | 878c2ecf20Sopenharmony_ci FIELD_PREP(OP_CMD_B_SRC, breg) | 888c2ecf20Sopenharmony_ci FIELD_PREP(OP_CMD_TOKEN, cmd_tgt_act[op].token) | 898c2ecf20Sopenharmony_ci FIELD_PREP(OP_CMD_XFER, xfer) | 908c2ecf20Sopenharmony_ci FIELD_PREP(OP_CMD_CNT, size) | 918c2ecf20Sopenharmony_ci FIELD_PREP(OP_CMD_SIG, ctx != CMD_CTX_NO_SWAP) | 928c2ecf20Sopenharmony_ci FIELD_PREP(OP_CMD_TGT_CMD, cmd_tgt_act[op].tgt_cmd) | 938c2ecf20Sopenharmony_ci FIELD_PREP(OP_CMD_INDIR, indir) | 948c2ecf20Sopenharmony_ci FIELD_PREP(OP_CMD_MODE, mode); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci nfp_prog_push(nfp_prog, insn); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void 1008c2ecf20Sopenharmony_ciemit_cmd_any(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, u8 mode, u8 xfer, 1018c2ecf20Sopenharmony_ci swreg lreg, swreg rreg, u8 size, enum cmd_ctx_swap ctx, bool indir) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct nfp_insn_re_regs reg; 1048c2ecf20Sopenharmony_ci int err; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci err = swreg_to_restricted(reg_none(), lreg, rreg, ®, false); 1078c2ecf20Sopenharmony_ci if (err) { 1088c2ecf20Sopenharmony_ci nfp_prog->error = err; 1098c2ecf20Sopenharmony_ci return; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci if (reg.swap) { 1128c2ecf20Sopenharmony_ci pr_err("cmd can't swap arguments\n"); 1138c2ecf20Sopenharmony_ci nfp_prog->error = -EFAULT; 1148c2ecf20Sopenharmony_ci return; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci if (reg.dst_lmextn || reg.src_lmextn) { 1178c2ecf20Sopenharmony_ci pr_err("cmd can't use LMextn\n"); 1188c2ecf20Sopenharmony_ci nfp_prog->error = -EFAULT; 1198c2ecf20Sopenharmony_ci return; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci __emit_cmd(nfp_prog, op, mode, xfer, reg.areg, reg.breg, size, ctx, 1238c2ecf20Sopenharmony_ci indir); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void 1278c2ecf20Sopenharmony_ciemit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, u8 mode, u8 xfer, 1288c2ecf20Sopenharmony_ci swreg lreg, swreg rreg, u8 size, enum cmd_ctx_swap ctx) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci emit_cmd_any(nfp_prog, op, mode, xfer, lreg, rreg, size, ctx, false); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void 1348c2ecf20Sopenharmony_ciemit_cmd_indir(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, u8 mode, u8 xfer, 1358c2ecf20Sopenharmony_ci swreg lreg, swreg rreg, u8 size, enum cmd_ctx_swap ctx) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci emit_cmd_any(nfp_prog, op, mode, xfer, lreg, rreg, size, ctx, true); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void 1418c2ecf20Sopenharmony_ci__emit_br(struct nfp_prog *nfp_prog, enum br_mask mask, enum br_ev_pip ev_pip, 1428c2ecf20Sopenharmony_ci enum br_ctx_signal_state css, u16 addr, u8 defer) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci u16 addr_lo, addr_hi; 1458c2ecf20Sopenharmony_ci u64 insn; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci addr_lo = addr & (OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO)); 1488c2ecf20Sopenharmony_ci addr_hi = addr != addr_lo; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci insn = OP_BR_BASE | 1518c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_MASK, mask) | 1528c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_EV_PIP, ev_pip) | 1538c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_CSS, css) | 1548c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_DEFBR, defer) | 1558c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_ADDR_LO, addr_lo) | 1568c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_ADDR_HI, addr_hi); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci nfp_prog_push(nfp_prog, insn); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void 1628c2ecf20Sopenharmony_ciemit_br_relo(struct nfp_prog *nfp_prog, enum br_mask mask, u16 addr, u8 defer, 1638c2ecf20Sopenharmony_ci enum nfp_relo_type relo) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci if (mask == BR_UNC && defer > 2) { 1668c2ecf20Sopenharmony_ci pr_err("BUG: branch defer out of bounds %d\n", defer); 1678c2ecf20Sopenharmony_ci nfp_prog->error = -EFAULT; 1688c2ecf20Sopenharmony_ci return; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci __emit_br(nfp_prog, mask, 1728c2ecf20Sopenharmony_ci mask != BR_UNC ? BR_EV_PIP_COND : BR_EV_PIP_UNCOND, 1738c2ecf20Sopenharmony_ci BR_CSS_NONE, addr, defer); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci nfp_prog->prog[nfp_prog->prog_len - 1] |= 1768c2ecf20Sopenharmony_ci FIELD_PREP(OP_RELO_TYPE, relo); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void 1808c2ecf20Sopenharmony_ciemit_br(struct nfp_prog *nfp_prog, enum br_mask mask, u16 addr, u8 defer) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci emit_br_relo(nfp_prog, mask, addr, defer, RELO_BR_REL); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void 1868c2ecf20Sopenharmony_ci__emit_br_bit(struct nfp_prog *nfp_prog, u16 areg, u16 breg, u16 addr, u8 defer, 1878c2ecf20Sopenharmony_ci bool set, bool src_lmextn) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci u16 addr_lo, addr_hi; 1908c2ecf20Sopenharmony_ci u64 insn; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci addr_lo = addr & (OP_BR_BIT_ADDR_LO >> __bf_shf(OP_BR_BIT_ADDR_LO)); 1938c2ecf20Sopenharmony_ci addr_hi = addr != addr_lo; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci insn = OP_BR_BIT_BASE | 1968c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_BIT_A_SRC, areg) | 1978c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_BIT_B_SRC, breg) | 1988c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_BIT_BV, set) | 1998c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_BIT_DEFBR, defer) | 2008c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_BIT_ADDR_LO, addr_lo) | 2018c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_BIT_ADDR_HI, addr_hi) | 2028c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_BIT_SRC_LMEXTN, src_lmextn); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci nfp_prog_push(nfp_prog, insn); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void 2088c2ecf20Sopenharmony_ciemit_br_bit_relo(struct nfp_prog *nfp_prog, swreg src, u8 bit, u16 addr, 2098c2ecf20Sopenharmony_ci u8 defer, bool set, enum nfp_relo_type relo) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct nfp_insn_re_regs reg; 2128c2ecf20Sopenharmony_ci int err; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* NOTE: The bit to test is specified as an rotation amount, such that 2158c2ecf20Sopenharmony_ci * the bit to test will be placed on the MSB of the result when 2168c2ecf20Sopenharmony_ci * doing a rotate right. For bit X, we need right rotate X + 1. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci bit += 1; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci err = swreg_to_restricted(reg_none(), src, reg_imm(bit), ®, false); 2218c2ecf20Sopenharmony_ci if (err) { 2228c2ecf20Sopenharmony_ci nfp_prog->error = err; 2238c2ecf20Sopenharmony_ci return; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci __emit_br_bit(nfp_prog, reg.areg, reg.breg, addr, defer, set, 2278c2ecf20Sopenharmony_ci reg.src_lmextn); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci nfp_prog->prog[nfp_prog->prog_len - 1] |= 2308c2ecf20Sopenharmony_ci FIELD_PREP(OP_RELO_TYPE, relo); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic void 2348c2ecf20Sopenharmony_ciemit_br_bset(struct nfp_prog *nfp_prog, swreg src, u8 bit, u16 addr, u8 defer) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci emit_br_bit_relo(nfp_prog, src, bit, addr, defer, true, RELO_BR_REL); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic void 2408c2ecf20Sopenharmony_ci__emit_br_alu(struct nfp_prog *nfp_prog, u16 areg, u16 breg, u16 imm_hi, 2418c2ecf20Sopenharmony_ci u8 defer, bool dst_lmextn, bool src_lmextn) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci u64 insn; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci insn = OP_BR_ALU_BASE | 2468c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_ALU_A_SRC, areg) | 2478c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_ALU_B_SRC, breg) | 2488c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_ALU_DEFBR, defer) | 2498c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_ALU_IMM_HI, imm_hi) | 2508c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_ALU_SRC_LMEXTN, src_lmextn) | 2518c2ecf20Sopenharmony_ci FIELD_PREP(OP_BR_ALU_DST_LMEXTN, dst_lmextn); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci nfp_prog_push(nfp_prog, insn); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic void emit_rtn(struct nfp_prog *nfp_prog, swreg base, u8 defer) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct nfp_insn_ur_regs reg; 2598c2ecf20Sopenharmony_ci int err; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci err = swreg_to_unrestricted(reg_none(), base, reg_imm(0), ®); 2628c2ecf20Sopenharmony_ci if (err) { 2638c2ecf20Sopenharmony_ci nfp_prog->error = err; 2648c2ecf20Sopenharmony_ci return; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci __emit_br_alu(nfp_prog, reg.areg, reg.breg, 0, defer, reg.dst_lmextn, 2688c2ecf20Sopenharmony_ci reg.src_lmextn); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void 2728c2ecf20Sopenharmony_ci__emit_immed(struct nfp_prog *nfp_prog, u16 areg, u16 breg, u16 imm_hi, 2738c2ecf20Sopenharmony_ci enum immed_width width, bool invert, 2748c2ecf20Sopenharmony_ci enum immed_shift shift, bool wr_both, 2758c2ecf20Sopenharmony_ci bool dst_lmextn, bool src_lmextn) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci u64 insn; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci insn = OP_IMMED_BASE | 2808c2ecf20Sopenharmony_ci FIELD_PREP(OP_IMMED_A_SRC, areg) | 2818c2ecf20Sopenharmony_ci FIELD_PREP(OP_IMMED_B_SRC, breg) | 2828c2ecf20Sopenharmony_ci FIELD_PREP(OP_IMMED_IMM, imm_hi) | 2838c2ecf20Sopenharmony_ci FIELD_PREP(OP_IMMED_WIDTH, width) | 2848c2ecf20Sopenharmony_ci FIELD_PREP(OP_IMMED_INV, invert) | 2858c2ecf20Sopenharmony_ci FIELD_PREP(OP_IMMED_SHIFT, shift) | 2868c2ecf20Sopenharmony_ci FIELD_PREP(OP_IMMED_WR_AB, wr_both) | 2878c2ecf20Sopenharmony_ci FIELD_PREP(OP_IMMED_SRC_LMEXTN, src_lmextn) | 2888c2ecf20Sopenharmony_ci FIELD_PREP(OP_IMMED_DST_LMEXTN, dst_lmextn); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci nfp_prog_push(nfp_prog, insn); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic void 2948c2ecf20Sopenharmony_ciemit_immed(struct nfp_prog *nfp_prog, swreg dst, u16 imm, 2958c2ecf20Sopenharmony_ci enum immed_width width, bool invert, enum immed_shift shift) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct nfp_insn_ur_regs reg; 2988c2ecf20Sopenharmony_ci int err; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (swreg_type(dst) == NN_REG_IMM) { 3018c2ecf20Sopenharmony_ci nfp_prog->error = -EFAULT; 3028c2ecf20Sopenharmony_ci return; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci err = swreg_to_unrestricted(dst, dst, reg_imm(imm & 0xff), ®); 3068c2ecf20Sopenharmony_ci if (err) { 3078c2ecf20Sopenharmony_ci nfp_prog->error = err; 3088c2ecf20Sopenharmony_ci return; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* Use reg.dst when destination is No-Dest. */ 3128c2ecf20Sopenharmony_ci __emit_immed(nfp_prog, 3138c2ecf20Sopenharmony_ci swreg_type(dst) == NN_REG_NONE ? reg.dst : reg.areg, 3148c2ecf20Sopenharmony_ci reg.breg, imm >> 8, width, invert, shift, 3158c2ecf20Sopenharmony_ci reg.wr_both, reg.dst_lmextn, reg.src_lmextn); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic void 3198c2ecf20Sopenharmony_ci__emit_shf(struct nfp_prog *nfp_prog, u16 dst, enum alu_dst_ab dst_ab, 3208c2ecf20Sopenharmony_ci enum shf_sc sc, u8 shift, 3218c2ecf20Sopenharmony_ci u16 areg, enum shf_op op, u16 breg, bool i8, bool sw, bool wr_both, 3228c2ecf20Sopenharmony_ci bool dst_lmextn, bool src_lmextn) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci u64 insn; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!FIELD_FIT(OP_SHF_SHIFT, shift)) { 3278c2ecf20Sopenharmony_ci nfp_prog->error = -EFAULT; 3288c2ecf20Sopenharmony_ci return; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* NFP shift instruction has something special. If shift direction is 3328c2ecf20Sopenharmony_ci * left then shift amount of 1 to 31 is specified as 32 minus the amount 3338c2ecf20Sopenharmony_ci * to shift. 3348c2ecf20Sopenharmony_ci * 3358c2ecf20Sopenharmony_ci * But no need to do this for indirect shift which has shift amount be 3368c2ecf20Sopenharmony_ci * 0. Even after we do this subtraction, shift amount 0 will be turned 3378c2ecf20Sopenharmony_ci * into 32 which will eventually be encoded the same as 0 because only 3388c2ecf20Sopenharmony_ci * low 5 bits are encoded, but shift amount be 32 will fail the 3398c2ecf20Sopenharmony_ci * FIELD_PREP check done later on shift mask (0x1f), due to 32 is out of 3408c2ecf20Sopenharmony_ci * mask range. 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_ci if (sc == SHF_SC_L_SHF && shift) 3438c2ecf20Sopenharmony_ci shift = 32 - shift; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci insn = OP_SHF_BASE | 3468c2ecf20Sopenharmony_ci FIELD_PREP(OP_SHF_A_SRC, areg) | 3478c2ecf20Sopenharmony_ci FIELD_PREP(OP_SHF_SC, sc) | 3488c2ecf20Sopenharmony_ci FIELD_PREP(OP_SHF_B_SRC, breg) | 3498c2ecf20Sopenharmony_ci FIELD_PREP(OP_SHF_I8, i8) | 3508c2ecf20Sopenharmony_ci FIELD_PREP(OP_SHF_SW, sw) | 3518c2ecf20Sopenharmony_ci FIELD_PREP(OP_SHF_DST, dst) | 3528c2ecf20Sopenharmony_ci FIELD_PREP(OP_SHF_SHIFT, shift) | 3538c2ecf20Sopenharmony_ci FIELD_PREP(OP_SHF_OP, op) | 3548c2ecf20Sopenharmony_ci FIELD_PREP(OP_SHF_DST_AB, dst_ab) | 3558c2ecf20Sopenharmony_ci FIELD_PREP(OP_SHF_WR_AB, wr_both) | 3568c2ecf20Sopenharmony_ci FIELD_PREP(OP_SHF_SRC_LMEXTN, src_lmextn) | 3578c2ecf20Sopenharmony_ci FIELD_PREP(OP_SHF_DST_LMEXTN, dst_lmextn); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci nfp_prog_push(nfp_prog, insn); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic void 3638c2ecf20Sopenharmony_ciemit_shf(struct nfp_prog *nfp_prog, swreg dst, 3648c2ecf20Sopenharmony_ci swreg lreg, enum shf_op op, swreg rreg, enum shf_sc sc, u8 shift) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct nfp_insn_re_regs reg; 3678c2ecf20Sopenharmony_ci int err; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci err = swreg_to_restricted(dst, lreg, rreg, ®, true); 3708c2ecf20Sopenharmony_ci if (err) { 3718c2ecf20Sopenharmony_ci nfp_prog->error = err; 3728c2ecf20Sopenharmony_ci return; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci __emit_shf(nfp_prog, reg.dst, reg.dst_ab, sc, shift, 3768c2ecf20Sopenharmony_ci reg.areg, op, reg.breg, reg.i8, reg.swap, reg.wr_both, 3778c2ecf20Sopenharmony_ci reg.dst_lmextn, reg.src_lmextn); 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic void 3818c2ecf20Sopenharmony_ciemit_shf_indir(struct nfp_prog *nfp_prog, swreg dst, 3828c2ecf20Sopenharmony_ci swreg lreg, enum shf_op op, swreg rreg, enum shf_sc sc) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci if (sc == SHF_SC_R_ROT) { 3858c2ecf20Sopenharmony_ci pr_err("indirect shift is not allowed on rotation\n"); 3868c2ecf20Sopenharmony_ci nfp_prog->error = -EFAULT; 3878c2ecf20Sopenharmony_ci return; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci emit_shf(nfp_prog, dst, lreg, op, rreg, sc, 0); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic void 3948c2ecf20Sopenharmony_ci__emit_alu(struct nfp_prog *nfp_prog, u16 dst, enum alu_dst_ab dst_ab, 3958c2ecf20Sopenharmony_ci u16 areg, enum alu_op op, u16 breg, bool swap, bool wr_both, 3968c2ecf20Sopenharmony_ci bool dst_lmextn, bool src_lmextn) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci u64 insn; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci insn = OP_ALU_BASE | 4018c2ecf20Sopenharmony_ci FIELD_PREP(OP_ALU_A_SRC, areg) | 4028c2ecf20Sopenharmony_ci FIELD_PREP(OP_ALU_B_SRC, breg) | 4038c2ecf20Sopenharmony_ci FIELD_PREP(OP_ALU_DST, dst) | 4048c2ecf20Sopenharmony_ci FIELD_PREP(OP_ALU_SW, swap) | 4058c2ecf20Sopenharmony_ci FIELD_PREP(OP_ALU_OP, op) | 4068c2ecf20Sopenharmony_ci FIELD_PREP(OP_ALU_DST_AB, dst_ab) | 4078c2ecf20Sopenharmony_ci FIELD_PREP(OP_ALU_WR_AB, wr_both) | 4088c2ecf20Sopenharmony_ci FIELD_PREP(OP_ALU_SRC_LMEXTN, src_lmextn) | 4098c2ecf20Sopenharmony_ci FIELD_PREP(OP_ALU_DST_LMEXTN, dst_lmextn); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci nfp_prog_push(nfp_prog, insn); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic void 4158c2ecf20Sopenharmony_ciemit_alu(struct nfp_prog *nfp_prog, swreg dst, 4168c2ecf20Sopenharmony_ci swreg lreg, enum alu_op op, swreg rreg) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct nfp_insn_ur_regs reg; 4198c2ecf20Sopenharmony_ci int err; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci err = swreg_to_unrestricted(dst, lreg, rreg, ®); 4228c2ecf20Sopenharmony_ci if (err) { 4238c2ecf20Sopenharmony_ci nfp_prog->error = err; 4248c2ecf20Sopenharmony_ci return; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci __emit_alu(nfp_prog, reg.dst, reg.dst_ab, 4288c2ecf20Sopenharmony_ci reg.areg, op, reg.breg, reg.swap, reg.wr_both, 4298c2ecf20Sopenharmony_ci reg.dst_lmextn, reg.src_lmextn); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic void 4338c2ecf20Sopenharmony_ci__emit_mul(struct nfp_prog *nfp_prog, enum alu_dst_ab dst_ab, u16 areg, 4348c2ecf20Sopenharmony_ci enum mul_type type, enum mul_step step, u16 breg, bool swap, 4358c2ecf20Sopenharmony_ci bool wr_both, bool dst_lmextn, bool src_lmextn) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci u64 insn; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci insn = OP_MUL_BASE | 4408c2ecf20Sopenharmony_ci FIELD_PREP(OP_MUL_A_SRC, areg) | 4418c2ecf20Sopenharmony_ci FIELD_PREP(OP_MUL_B_SRC, breg) | 4428c2ecf20Sopenharmony_ci FIELD_PREP(OP_MUL_STEP, step) | 4438c2ecf20Sopenharmony_ci FIELD_PREP(OP_MUL_DST_AB, dst_ab) | 4448c2ecf20Sopenharmony_ci FIELD_PREP(OP_MUL_SW, swap) | 4458c2ecf20Sopenharmony_ci FIELD_PREP(OP_MUL_TYPE, type) | 4468c2ecf20Sopenharmony_ci FIELD_PREP(OP_MUL_WR_AB, wr_both) | 4478c2ecf20Sopenharmony_ci FIELD_PREP(OP_MUL_SRC_LMEXTN, src_lmextn) | 4488c2ecf20Sopenharmony_ci FIELD_PREP(OP_MUL_DST_LMEXTN, dst_lmextn); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci nfp_prog_push(nfp_prog, insn); 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void 4548c2ecf20Sopenharmony_ciemit_mul(struct nfp_prog *nfp_prog, swreg lreg, enum mul_type type, 4558c2ecf20Sopenharmony_ci enum mul_step step, swreg rreg) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct nfp_insn_ur_regs reg; 4588c2ecf20Sopenharmony_ci u16 areg; 4598c2ecf20Sopenharmony_ci int err; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (type == MUL_TYPE_START && step != MUL_STEP_NONE) { 4628c2ecf20Sopenharmony_ci nfp_prog->error = -EINVAL; 4638c2ecf20Sopenharmony_ci return; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (step == MUL_LAST || step == MUL_LAST_2) { 4678c2ecf20Sopenharmony_ci /* When type is step and step Number is LAST or LAST2, left 4688c2ecf20Sopenharmony_ci * source is used as destination. 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci err = swreg_to_unrestricted(lreg, reg_none(), rreg, ®); 4718c2ecf20Sopenharmony_ci areg = reg.dst; 4728c2ecf20Sopenharmony_ci } else { 4738c2ecf20Sopenharmony_ci err = swreg_to_unrestricted(reg_none(), lreg, rreg, ®); 4748c2ecf20Sopenharmony_ci areg = reg.areg; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (err) { 4788c2ecf20Sopenharmony_ci nfp_prog->error = err; 4798c2ecf20Sopenharmony_ci return; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci __emit_mul(nfp_prog, reg.dst_ab, areg, type, step, reg.breg, reg.swap, 4838c2ecf20Sopenharmony_ci reg.wr_both, reg.dst_lmextn, reg.src_lmextn); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic void 4878c2ecf20Sopenharmony_ci__emit_ld_field(struct nfp_prog *nfp_prog, enum shf_sc sc, 4888c2ecf20Sopenharmony_ci u8 areg, u8 bmask, u8 breg, u8 shift, bool imm8, 4898c2ecf20Sopenharmony_ci bool zero, bool swap, bool wr_both, 4908c2ecf20Sopenharmony_ci bool dst_lmextn, bool src_lmextn) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci u64 insn; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci insn = OP_LDF_BASE | 4958c2ecf20Sopenharmony_ci FIELD_PREP(OP_LDF_A_SRC, areg) | 4968c2ecf20Sopenharmony_ci FIELD_PREP(OP_LDF_SC, sc) | 4978c2ecf20Sopenharmony_ci FIELD_PREP(OP_LDF_B_SRC, breg) | 4988c2ecf20Sopenharmony_ci FIELD_PREP(OP_LDF_I8, imm8) | 4998c2ecf20Sopenharmony_ci FIELD_PREP(OP_LDF_SW, swap) | 5008c2ecf20Sopenharmony_ci FIELD_PREP(OP_LDF_ZF, zero) | 5018c2ecf20Sopenharmony_ci FIELD_PREP(OP_LDF_BMASK, bmask) | 5028c2ecf20Sopenharmony_ci FIELD_PREP(OP_LDF_SHF, shift) | 5038c2ecf20Sopenharmony_ci FIELD_PREP(OP_LDF_WR_AB, wr_both) | 5048c2ecf20Sopenharmony_ci FIELD_PREP(OP_LDF_SRC_LMEXTN, src_lmextn) | 5058c2ecf20Sopenharmony_ci FIELD_PREP(OP_LDF_DST_LMEXTN, dst_lmextn); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci nfp_prog_push(nfp_prog, insn); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic void 5118c2ecf20Sopenharmony_ciemit_ld_field_any(struct nfp_prog *nfp_prog, swreg dst, u8 bmask, swreg src, 5128c2ecf20Sopenharmony_ci enum shf_sc sc, u8 shift, bool zero) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct nfp_insn_re_regs reg; 5158c2ecf20Sopenharmony_ci int err; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* Note: ld_field is special as it uses one of the src regs as dst */ 5188c2ecf20Sopenharmony_ci err = swreg_to_restricted(dst, dst, src, ®, true); 5198c2ecf20Sopenharmony_ci if (err) { 5208c2ecf20Sopenharmony_ci nfp_prog->error = err; 5218c2ecf20Sopenharmony_ci return; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci __emit_ld_field(nfp_prog, sc, reg.areg, bmask, reg.breg, shift, 5258c2ecf20Sopenharmony_ci reg.i8, zero, reg.swap, reg.wr_both, 5268c2ecf20Sopenharmony_ci reg.dst_lmextn, reg.src_lmextn); 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic void 5308c2ecf20Sopenharmony_ciemit_ld_field(struct nfp_prog *nfp_prog, swreg dst, u8 bmask, swreg src, 5318c2ecf20Sopenharmony_ci enum shf_sc sc, u8 shift) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci emit_ld_field_any(nfp_prog, dst, bmask, src, sc, shift, false); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic void 5378c2ecf20Sopenharmony_ci__emit_lcsr(struct nfp_prog *nfp_prog, u16 areg, u16 breg, bool wr, u16 addr, 5388c2ecf20Sopenharmony_ci bool dst_lmextn, bool src_lmextn) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci u64 insn; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci insn = OP_LCSR_BASE | 5438c2ecf20Sopenharmony_ci FIELD_PREP(OP_LCSR_A_SRC, areg) | 5448c2ecf20Sopenharmony_ci FIELD_PREP(OP_LCSR_B_SRC, breg) | 5458c2ecf20Sopenharmony_ci FIELD_PREP(OP_LCSR_WRITE, wr) | 5468c2ecf20Sopenharmony_ci FIELD_PREP(OP_LCSR_ADDR, addr / 4) | 5478c2ecf20Sopenharmony_ci FIELD_PREP(OP_LCSR_SRC_LMEXTN, src_lmextn) | 5488c2ecf20Sopenharmony_ci FIELD_PREP(OP_LCSR_DST_LMEXTN, dst_lmextn); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci nfp_prog_push(nfp_prog, insn); 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic void emit_csr_wr(struct nfp_prog *nfp_prog, swreg src, u16 addr) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci struct nfp_insn_ur_regs reg; 5568c2ecf20Sopenharmony_ci int err; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* This instruction takes immeds instead of reg_none() for the ignored 5598c2ecf20Sopenharmony_ci * operand, but we can't encode 2 immeds in one instr with our normal 5608c2ecf20Sopenharmony_ci * swreg infra so if param is an immed, we encode as reg_none() and 5618c2ecf20Sopenharmony_ci * copy the immed to both operands. 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_ci if (swreg_type(src) == NN_REG_IMM) { 5648c2ecf20Sopenharmony_ci err = swreg_to_unrestricted(reg_none(), src, reg_none(), ®); 5658c2ecf20Sopenharmony_ci reg.breg = reg.areg; 5668c2ecf20Sopenharmony_ci } else { 5678c2ecf20Sopenharmony_ci err = swreg_to_unrestricted(reg_none(), src, reg_imm(0), ®); 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci if (err) { 5708c2ecf20Sopenharmony_ci nfp_prog->error = err; 5718c2ecf20Sopenharmony_ci return; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci __emit_lcsr(nfp_prog, reg.areg, reg.breg, true, addr, 5758c2ecf20Sopenharmony_ci false, reg.src_lmextn); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci/* CSR value is read in following immed[gpr, 0] */ 5798c2ecf20Sopenharmony_cistatic void __emit_csr_rd(struct nfp_prog *nfp_prog, u16 addr) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci __emit_lcsr(nfp_prog, 0, 0, false, addr, false, false); 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic void emit_nop(struct nfp_prog *nfp_prog) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci __emit_immed(nfp_prog, UR_REG_IMM, UR_REG_IMM, 0, 0, 0, 0, 0, 0, 0); 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci/* --- Wrappers --- */ 5908c2ecf20Sopenharmony_cistatic bool pack_immed(u32 imm, u16 *val, enum immed_shift *shift) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci if (!(imm & 0xffff0000)) { 5938c2ecf20Sopenharmony_ci *val = imm; 5948c2ecf20Sopenharmony_ci *shift = IMMED_SHIFT_0B; 5958c2ecf20Sopenharmony_ci } else if (!(imm & 0xff0000ff)) { 5968c2ecf20Sopenharmony_ci *val = imm >> 8; 5978c2ecf20Sopenharmony_ci *shift = IMMED_SHIFT_1B; 5988c2ecf20Sopenharmony_ci } else if (!(imm & 0x0000ffff)) { 5998c2ecf20Sopenharmony_ci *val = imm >> 16; 6008c2ecf20Sopenharmony_ci *shift = IMMED_SHIFT_2B; 6018c2ecf20Sopenharmony_ci } else { 6028c2ecf20Sopenharmony_ci return false; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return true; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic void wrp_immed(struct nfp_prog *nfp_prog, swreg dst, u32 imm) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci enum immed_shift shift; 6118c2ecf20Sopenharmony_ci u16 val; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (pack_immed(imm, &val, &shift)) { 6148c2ecf20Sopenharmony_ci emit_immed(nfp_prog, dst, val, IMMED_WIDTH_ALL, false, shift); 6158c2ecf20Sopenharmony_ci } else if (pack_immed(~imm, &val, &shift)) { 6168c2ecf20Sopenharmony_ci emit_immed(nfp_prog, dst, val, IMMED_WIDTH_ALL, true, shift); 6178c2ecf20Sopenharmony_ci } else { 6188c2ecf20Sopenharmony_ci emit_immed(nfp_prog, dst, imm & 0xffff, IMMED_WIDTH_ALL, 6198c2ecf20Sopenharmony_ci false, IMMED_SHIFT_0B); 6208c2ecf20Sopenharmony_ci emit_immed(nfp_prog, dst, imm >> 16, IMMED_WIDTH_WORD, 6218c2ecf20Sopenharmony_ci false, IMMED_SHIFT_2B); 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic void 6268c2ecf20Sopenharmony_ciwrp_zext(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, u8 dst) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci if (meta->flags & FLAG_INSN_DO_ZEXT) 6298c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(dst + 1), 0); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic void 6338c2ecf20Sopenharmony_ciwrp_immed_relo(struct nfp_prog *nfp_prog, swreg dst, u32 imm, 6348c2ecf20Sopenharmony_ci enum nfp_relo_type relo) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci if (imm > 0xffff) { 6378c2ecf20Sopenharmony_ci pr_err("relocation of a large immediate!\n"); 6388c2ecf20Sopenharmony_ci nfp_prog->error = -EFAULT; 6398c2ecf20Sopenharmony_ci return; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci emit_immed(nfp_prog, dst, imm, IMMED_WIDTH_ALL, false, IMMED_SHIFT_0B); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci nfp_prog->prog[nfp_prog->prog_len - 1] |= 6448c2ecf20Sopenharmony_ci FIELD_PREP(OP_RELO_TYPE, relo); 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci/* ur_load_imm_any() - encode immediate or use tmp register (unrestricted) 6488c2ecf20Sopenharmony_ci * If the @imm is small enough encode it directly in operand and return 6498c2ecf20Sopenharmony_ci * otherwise load @imm to a spare register and return its encoding. 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_cistatic swreg ur_load_imm_any(struct nfp_prog *nfp_prog, u32 imm, swreg tmp_reg) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci if (FIELD_FIT(UR_REG_IMM_MAX, imm)) 6548c2ecf20Sopenharmony_ci return reg_imm(imm); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, tmp_reg, imm); 6578c2ecf20Sopenharmony_ci return tmp_reg; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci/* re_load_imm_any() - encode immediate or use tmp register (restricted) 6618c2ecf20Sopenharmony_ci * If the @imm is small enough encode it directly in operand and return 6628c2ecf20Sopenharmony_ci * otherwise load @imm to a spare register and return its encoding. 6638c2ecf20Sopenharmony_ci */ 6648c2ecf20Sopenharmony_cistatic swreg re_load_imm_any(struct nfp_prog *nfp_prog, u32 imm, swreg tmp_reg) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci if (FIELD_FIT(RE_REG_IMM_MAX, imm)) 6678c2ecf20Sopenharmony_ci return reg_imm(imm); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, tmp_reg, imm); 6708c2ecf20Sopenharmony_ci return tmp_reg; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic void wrp_nops(struct nfp_prog *nfp_prog, unsigned int count) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci while (count--) 6768c2ecf20Sopenharmony_ci emit_nop(nfp_prog); 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic void wrp_mov(struct nfp_prog *nfp_prog, swreg dst, swreg src) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci emit_alu(nfp_prog, dst, reg_none(), ALU_OP_NONE, src); 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic void wrp_reg_mov(struct nfp_prog *nfp_prog, u16 dst, u16 src) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_both(dst), reg_b(src)); 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci/* wrp_reg_subpart() - load @field_len bytes from @offset of @src, write the 6908c2ecf20Sopenharmony_ci * result to @dst from low end. 6918c2ecf20Sopenharmony_ci */ 6928c2ecf20Sopenharmony_cistatic void 6938c2ecf20Sopenharmony_ciwrp_reg_subpart(struct nfp_prog *nfp_prog, swreg dst, swreg src, u8 field_len, 6948c2ecf20Sopenharmony_ci u8 offset) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci enum shf_sc sc = offset ? SHF_SC_R_SHF : SHF_SC_NONE; 6978c2ecf20Sopenharmony_ci u8 mask = (1 << field_len) - 1; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci emit_ld_field_any(nfp_prog, dst, mask, src, sc, offset * 8, true); 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci/* wrp_reg_or_subpart() - load @field_len bytes from low end of @src, or the 7038c2ecf20Sopenharmony_ci * result to @dst from offset, there is no change on the other bits of @dst. 7048c2ecf20Sopenharmony_ci */ 7058c2ecf20Sopenharmony_cistatic void 7068c2ecf20Sopenharmony_ciwrp_reg_or_subpart(struct nfp_prog *nfp_prog, swreg dst, swreg src, 7078c2ecf20Sopenharmony_ci u8 field_len, u8 offset) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci enum shf_sc sc = offset ? SHF_SC_L_SHF : SHF_SC_NONE; 7108c2ecf20Sopenharmony_ci u8 mask = ((1 << field_len) - 1) << offset; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci emit_ld_field(nfp_prog, dst, mask, src, sc, 32 - offset * 8); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic void 7168c2ecf20Sopenharmony_ciaddr40_offset(struct nfp_prog *nfp_prog, u8 src_gpr, swreg offset, 7178c2ecf20Sopenharmony_ci swreg *rega, swreg *regb) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci if (offset == reg_imm(0)) { 7208c2ecf20Sopenharmony_ci *rega = reg_a(src_gpr); 7218c2ecf20Sopenharmony_ci *regb = reg_b(src_gpr + 1); 7228c2ecf20Sopenharmony_ci return; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imm_a(nfp_prog), reg_a(src_gpr), ALU_OP_ADD, offset); 7268c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imm_b(nfp_prog), reg_b(src_gpr + 1), ALU_OP_ADD_C, 7278c2ecf20Sopenharmony_ci reg_imm(0)); 7288c2ecf20Sopenharmony_ci *rega = imm_a(nfp_prog); 7298c2ecf20Sopenharmony_ci *regb = imm_b(nfp_prog); 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci/* NFP has Command Push Pull bus which supports bluk memory operations. */ 7338c2ecf20Sopenharmony_cistatic int nfp_cpp_memcpy(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci bool descending_seq = meta->ldst_gather_len < 0; 7368c2ecf20Sopenharmony_ci s16 len = abs(meta->ldst_gather_len); 7378c2ecf20Sopenharmony_ci swreg src_base, off; 7388c2ecf20Sopenharmony_ci bool src_40bit_addr; 7398c2ecf20Sopenharmony_ci unsigned int i; 7408c2ecf20Sopenharmony_ci u8 xfer_num; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci off = re_load_imm_any(nfp_prog, meta->insn.off, imm_b(nfp_prog)); 7438c2ecf20Sopenharmony_ci src_40bit_addr = meta->ptr.type == PTR_TO_MAP_VALUE; 7448c2ecf20Sopenharmony_ci src_base = reg_a(meta->insn.src_reg * 2); 7458c2ecf20Sopenharmony_ci xfer_num = round_up(len, 4) / 4; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (src_40bit_addr) 7488c2ecf20Sopenharmony_ci addr40_offset(nfp_prog, meta->insn.src_reg * 2, off, &src_base, 7498c2ecf20Sopenharmony_ci &off); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci /* Setup PREV_ALU fields to override memory read length. */ 7528c2ecf20Sopenharmony_ci if (len > 32) 7538c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_none(), 7548c2ecf20Sopenharmony_ci CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, xfer_num - 1)); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Memory read from source addr into transfer-in registers. */ 7578c2ecf20Sopenharmony_ci emit_cmd_any(nfp_prog, CMD_TGT_READ32_SWAP, 7588c2ecf20Sopenharmony_ci src_40bit_addr ? CMD_MODE_40b_BA : CMD_MODE_32b, 0, 7598c2ecf20Sopenharmony_ci src_base, off, xfer_num - 1, CMD_CTX_SWAP, len > 32); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* Move from transfer-in to transfer-out. */ 7628c2ecf20Sopenharmony_ci for (i = 0; i < xfer_num; i++) 7638c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_xfer(i), reg_xfer(i)); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci off = re_load_imm_any(nfp_prog, meta->paired_st->off, imm_b(nfp_prog)); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if (len <= 8) { 7688c2ecf20Sopenharmony_ci /* Use single direct_ref write8. */ 7698c2ecf20Sopenharmony_ci emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 0, 7708c2ecf20Sopenharmony_ci reg_a(meta->paired_st->dst_reg * 2), off, len - 1, 7718c2ecf20Sopenharmony_ci CMD_CTX_SWAP); 7728c2ecf20Sopenharmony_ci } else if (len <= 32 && IS_ALIGNED(len, 4)) { 7738c2ecf20Sopenharmony_ci /* Use single direct_ref write32. */ 7748c2ecf20Sopenharmony_ci emit_cmd(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0, 7758c2ecf20Sopenharmony_ci reg_a(meta->paired_st->dst_reg * 2), off, xfer_num - 1, 7768c2ecf20Sopenharmony_ci CMD_CTX_SWAP); 7778c2ecf20Sopenharmony_ci } else if (len <= 32) { 7788c2ecf20Sopenharmony_ci /* Use single indirect_ref write8. */ 7798c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_none(), 7808c2ecf20Sopenharmony_ci CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, len - 1)); 7818c2ecf20Sopenharmony_ci emit_cmd_indir(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 0, 7828c2ecf20Sopenharmony_ci reg_a(meta->paired_st->dst_reg * 2), off, 7838c2ecf20Sopenharmony_ci len - 1, CMD_CTX_SWAP); 7848c2ecf20Sopenharmony_ci } else if (IS_ALIGNED(len, 4)) { 7858c2ecf20Sopenharmony_ci /* Use single indirect_ref write32. */ 7868c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_none(), 7878c2ecf20Sopenharmony_ci CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, xfer_num - 1)); 7888c2ecf20Sopenharmony_ci emit_cmd_indir(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0, 7898c2ecf20Sopenharmony_ci reg_a(meta->paired_st->dst_reg * 2), off, 7908c2ecf20Sopenharmony_ci xfer_num - 1, CMD_CTX_SWAP); 7918c2ecf20Sopenharmony_ci } else if (len <= 40) { 7928c2ecf20Sopenharmony_ci /* Use one direct_ref write32 to write the first 32-bytes, then 7938c2ecf20Sopenharmony_ci * another direct_ref write8 to write the remaining bytes. 7948c2ecf20Sopenharmony_ci */ 7958c2ecf20Sopenharmony_ci emit_cmd(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0, 7968c2ecf20Sopenharmony_ci reg_a(meta->paired_st->dst_reg * 2), off, 7, 7978c2ecf20Sopenharmony_ci CMD_CTX_SWAP); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci off = re_load_imm_any(nfp_prog, meta->paired_st->off + 32, 8008c2ecf20Sopenharmony_ci imm_b(nfp_prog)); 8018c2ecf20Sopenharmony_ci emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 8, 8028c2ecf20Sopenharmony_ci reg_a(meta->paired_st->dst_reg * 2), off, len - 33, 8038c2ecf20Sopenharmony_ci CMD_CTX_SWAP); 8048c2ecf20Sopenharmony_ci } else { 8058c2ecf20Sopenharmony_ci /* Use one indirect_ref write32 to write 4-bytes aligned length, 8068c2ecf20Sopenharmony_ci * then another direct_ref write8 to write the remaining bytes. 8078c2ecf20Sopenharmony_ci */ 8088c2ecf20Sopenharmony_ci u8 new_off; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_none(), 8118c2ecf20Sopenharmony_ci CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, xfer_num - 2)); 8128c2ecf20Sopenharmony_ci emit_cmd_indir(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0, 8138c2ecf20Sopenharmony_ci reg_a(meta->paired_st->dst_reg * 2), off, 8148c2ecf20Sopenharmony_ci xfer_num - 2, CMD_CTX_SWAP); 8158c2ecf20Sopenharmony_ci new_off = meta->paired_st->off + (xfer_num - 1) * 4; 8168c2ecf20Sopenharmony_ci off = re_load_imm_any(nfp_prog, new_off, imm_b(nfp_prog)); 8178c2ecf20Sopenharmony_ci emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 8188c2ecf20Sopenharmony_ci xfer_num - 1, reg_a(meta->paired_st->dst_reg * 2), off, 8198c2ecf20Sopenharmony_ci (len & 0x3) - 1, CMD_CTX_SWAP); 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci /* TODO: The following extra load is to make sure data flow be identical 8238c2ecf20Sopenharmony_ci * before and after we do memory copy optimization. 8248c2ecf20Sopenharmony_ci * 8258c2ecf20Sopenharmony_ci * The load destination register is not guaranteed to be dead, so we 8268c2ecf20Sopenharmony_ci * need to make sure it is loaded with the value the same as before 8278c2ecf20Sopenharmony_ci * this transformation. 8288c2ecf20Sopenharmony_ci * 8298c2ecf20Sopenharmony_ci * These extra loads could be removed once we have accurate register 8308c2ecf20Sopenharmony_ci * usage information. 8318c2ecf20Sopenharmony_ci */ 8328c2ecf20Sopenharmony_ci if (descending_seq) 8338c2ecf20Sopenharmony_ci xfer_num = 0; 8348c2ecf20Sopenharmony_ci else if (BPF_SIZE(meta->insn.code) != BPF_DW) 8358c2ecf20Sopenharmony_ci xfer_num = xfer_num - 1; 8368c2ecf20Sopenharmony_ci else 8378c2ecf20Sopenharmony_ci xfer_num = xfer_num - 2; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci switch (BPF_SIZE(meta->insn.code)) { 8408c2ecf20Sopenharmony_ci case BPF_B: 8418c2ecf20Sopenharmony_ci wrp_reg_subpart(nfp_prog, reg_both(meta->insn.dst_reg * 2), 8428c2ecf20Sopenharmony_ci reg_xfer(xfer_num), 1, 8438c2ecf20Sopenharmony_ci IS_ALIGNED(len, 4) ? 3 : (len & 3) - 1); 8448c2ecf20Sopenharmony_ci break; 8458c2ecf20Sopenharmony_ci case BPF_H: 8468c2ecf20Sopenharmony_ci wrp_reg_subpart(nfp_prog, reg_both(meta->insn.dst_reg * 2), 8478c2ecf20Sopenharmony_ci reg_xfer(xfer_num), 2, (len & 3) ^ 2); 8488c2ecf20Sopenharmony_ci break; 8498c2ecf20Sopenharmony_ci case BPF_W: 8508c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_both(meta->insn.dst_reg * 2), 8518c2ecf20Sopenharmony_ci reg_xfer(0)); 8528c2ecf20Sopenharmony_ci break; 8538c2ecf20Sopenharmony_ci case BPF_DW: 8548c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_both(meta->insn.dst_reg * 2), 8558c2ecf20Sopenharmony_ci reg_xfer(xfer_num)); 8568c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_both(meta->insn.dst_reg * 2 + 1), 8578c2ecf20Sopenharmony_ci reg_xfer(xfer_num + 1)); 8588c2ecf20Sopenharmony_ci break; 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (BPF_SIZE(meta->insn.code) != BPF_DW) 8628c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(meta->insn.dst_reg * 2 + 1), 0); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci return 0; 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_cistatic int 8688c2ecf20Sopenharmony_cidata_ld(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, swreg offset, 8698c2ecf20Sopenharmony_ci u8 dst_gpr, int size) 8708c2ecf20Sopenharmony_ci{ 8718c2ecf20Sopenharmony_ci unsigned int i; 8728c2ecf20Sopenharmony_ci u16 shift, sz; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* We load the value from the address indicated in @offset and then 8758c2ecf20Sopenharmony_ci * shift out the data we don't need. Note: this is big endian! 8768c2ecf20Sopenharmony_ci */ 8778c2ecf20Sopenharmony_ci sz = max(size, 4); 8788c2ecf20Sopenharmony_ci shift = size < 4 ? 4 - size : 0; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci emit_cmd(nfp_prog, CMD_TGT_READ8, CMD_MODE_32b, 0, 8818c2ecf20Sopenharmony_ci pptr_reg(nfp_prog), offset, sz - 1, CMD_CTX_SWAP); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci i = 0; 8848c2ecf20Sopenharmony_ci if (shift) 8858c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst_gpr), reg_none(), SHF_OP_NONE, 8868c2ecf20Sopenharmony_ci reg_xfer(0), SHF_SC_R_SHF, shift * 8); 8878c2ecf20Sopenharmony_ci else 8888c2ecf20Sopenharmony_ci for (; i * 4 < size; i++) 8898c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_both(dst_gpr + i), reg_xfer(i)); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (i < 2) 8928c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst_gpr); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci return 0; 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic int 8988c2ecf20Sopenharmony_cidata_ld_host_order(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 8998c2ecf20Sopenharmony_ci u8 dst_gpr, swreg lreg, swreg rreg, int size, 9008c2ecf20Sopenharmony_ci enum cmd_mode mode) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci unsigned int i; 9038c2ecf20Sopenharmony_ci u8 mask, sz; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci /* We load the value from the address indicated in rreg + lreg and then 9068c2ecf20Sopenharmony_ci * mask out the data we don't need. Note: this is little endian! 9078c2ecf20Sopenharmony_ci */ 9088c2ecf20Sopenharmony_ci sz = max(size, 4); 9098c2ecf20Sopenharmony_ci mask = size < 4 ? GENMASK(size - 1, 0) : 0; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci emit_cmd(nfp_prog, CMD_TGT_READ32_SWAP, mode, 0, 9128c2ecf20Sopenharmony_ci lreg, rreg, sz / 4 - 1, CMD_CTX_SWAP); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci i = 0; 9158c2ecf20Sopenharmony_ci if (mask) 9168c2ecf20Sopenharmony_ci emit_ld_field_any(nfp_prog, reg_both(dst_gpr), mask, 9178c2ecf20Sopenharmony_ci reg_xfer(0), SHF_SC_NONE, 0, true); 9188c2ecf20Sopenharmony_ci else 9198c2ecf20Sopenharmony_ci for (; i * 4 < size; i++) 9208c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_both(dst_gpr + i), reg_xfer(i)); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci if (i < 2) 9238c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst_gpr); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci return 0; 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cistatic int 9298c2ecf20Sopenharmony_cidata_ld_host_order_addr32(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 9308c2ecf20Sopenharmony_ci u8 src_gpr, swreg offset, u8 dst_gpr, u8 size) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci return data_ld_host_order(nfp_prog, meta, dst_gpr, reg_a(src_gpr), 9338c2ecf20Sopenharmony_ci offset, size, CMD_MODE_32b); 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_cistatic int 9378c2ecf20Sopenharmony_cidata_ld_host_order_addr40(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 9388c2ecf20Sopenharmony_ci u8 src_gpr, swreg offset, u8 dst_gpr, u8 size) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci swreg rega, regb; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci addr40_offset(nfp_prog, src_gpr, offset, ®a, ®b); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci return data_ld_host_order(nfp_prog, meta, dst_gpr, rega, regb, 9458c2ecf20Sopenharmony_ci size, CMD_MODE_40b_BA); 9468c2ecf20Sopenharmony_ci} 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic int 9498c2ecf20Sopenharmony_ciconstruct_data_ind_ld(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 9508c2ecf20Sopenharmony_ci u16 offset, u16 src, u8 size) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci swreg tmp_reg; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci /* Calculate the true offset (src_reg + imm) */ 9558c2ecf20Sopenharmony_ci tmp_reg = ur_load_imm_any(nfp_prog, offset, imm_b(nfp_prog)); 9568c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imm_both(nfp_prog), reg_a(src), ALU_OP_ADD, tmp_reg); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci /* Check packet length (size guaranteed to fit b/c it's u8) */ 9598c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imm_a(nfp_prog), 9608c2ecf20Sopenharmony_ci imm_a(nfp_prog), ALU_OP_ADD, reg_imm(size)); 9618c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), 9628c2ecf20Sopenharmony_ci plen_reg(nfp_prog), ALU_OP_SUB, imm_a(nfp_prog)); 9638c2ecf20Sopenharmony_ci emit_br_relo(nfp_prog, BR_BLO, BR_OFF_RELO, 0, RELO_BR_GO_ABORT); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* Load data */ 9668c2ecf20Sopenharmony_ci return data_ld(nfp_prog, meta, imm_b(nfp_prog), 0, size); 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cistatic int 9708c2ecf20Sopenharmony_ciconstruct_data_ld(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 9718c2ecf20Sopenharmony_ci u16 offset, u8 size) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci swreg tmp_reg; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* Check packet length */ 9768c2ecf20Sopenharmony_ci tmp_reg = ur_load_imm_any(nfp_prog, offset + size, imm_a(nfp_prog)); 9778c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), plen_reg(nfp_prog), ALU_OP_SUB, tmp_reg); 9788c2ecf20Sopenharmony_ci emit_br_relo(nfp_prog, BR_BLO, BR_OFF_RELO, 0, RELO_BR_GO_ABORT); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* Load data */ 9818c2ecf20Sopenharmony_ci tmp_reg = re_load_imm_any(nfp_prog, offset, imm_b(nfp_prog)); 9828c2ecf20Sopenharmony_ci return data_ld(nfp_prog, meta, tmp_reg, 0, size); 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic int 9868c2ecf20Sopenharmony_cidata_stx_host_order(struct nfp_prog *nfp_prog, u8 dst_gpr, swreg offset, 9878c2ecf20Sopenharmony_ci u8 src_gpr, u8 size) 9888c2ecf20Sopenharmony_ci{ 9898c2ecf20Sopenharmony_ci unsigned int i; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci for (i = 0; i * 4 < size; i++) 9928c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_xfer(i), reg_a(src_gpr + i)); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 0, 9958c2ecf20Sopenharmony_ci reg_a(dst_gpr), offset, size - 1, CMD_CTX_SWAP); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci return 0; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistatic int 10018c2ecf20Sopenharmony_cidata_st_host_order(struct nfp_prog *nfp_prog, u8 dst_gpr, swreg offset, 10028c2ecf20Sopenharmony_ci u64 imm, u8 size) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_xfer(0), imm); 10058c2ecf20Sopenharmony_ci if (size == 8) 10068c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_xfer(1), imm >> 32); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 0, 10098c2ecf20Sopenharmony_ci reg_a(dst_gpr), offset, size - 1, CMD_CTX_SWAP); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci return 0; 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_citypedef int 10158c2ecf20Sopenharmony_ci(*lmem_step)(struct nfp_prog *nfp_prog, u8 gpr, u8 gpr_byte, s32 off, 10168c2ecf20Sopenharmony_ci unsigned int size, bool first, bool new_gpr, bool last, bool lm3, 10178c2ecf20Sopenharmony_ci bool needs_inc); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic int 10208c2ecf20Sopenharmony_ciwrp_lmem_load(struct nfp_prog *nfp_prog, u8 dst, u8 dst_byte, s32 off, 10218c2ecf20Sopenharmony_ci unsigned int size, bool first, bool new_gpr, bool last, bool lm3, 10228c2ecf20Sopenharmony_ci bool needs_inc) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci bool should_inc = needs_inc && new_gpr && !last; 10258c2ecf20Sopenharmony_ci u32 idx, src_byte; 10268c2ecf20Sopenharmony_ci enum shf_sc sc; 10278c2ecf20Sopenharmony_ci swreg reg; 10288c2ecf20Sopenharmony_ci int shf; 10298c2ecf20Sopenharmony_ci u8 mask; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(dst_byte + size > 4 || off % 4 + size > 4)) 10328c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci idx = off / 4; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci /* Move the entire word */ 10378c2ecf20Sopenharmony_ci if (size == 4) { 10388c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_both(dst), 10398c2ecf20Sopenharmony_ci should_inc ? reg_lm_inc(3) : reg_lm(lm3 ? 3 : 0, idx)); 10408c2ecf20Sopenharmony_ci return 0; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(lm3 && idx > RE_REG_LM_IDX_MAX)) 10448c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci src_byte = off % 4; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci mask = (1 << size) - 1; 10498c2ecf20Sopenharmony_ci mask <<= dst_byte; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(mask > 0xf)) 10528c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci shf = abs(src_byte - dst_byte) * 8; 10558c2ecf20Sopenharmony_ci if (src_byte == dst_byte) { 10568c2ecf20Sopenharmony_ci sc = SHF_SC_NONE; 10578c2ecf20Sopenharmony_ci } else if (src_byte < dst_byte) { 10588c2ecf20Sopenharmony_ci shf = 32 - shf; 10598c2ecf20Sopenharmony_ci sc = SHF_SC_L_SHF; 10608c2ecf20Sopenharmony_ci } else { 10618c2ecf20Sopenharmony_ci sc = SHF_SC_R_SHF; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* ld_field can address fewer indexes, if offset too large do RMW. 10658c2ecf20Sopenharmony_ci * Because we RMV twice we waste 2 cycles on unaligned 8 byte writes. 10668c2ecf20Sopenharmony_ci */ 10678c2ecf20Sopenharmony_ci if (idx <= RE_REG_LM_IDX_MAX) { 10688c2ecf20Sopenharmony_ci reg = reg_lm(lm3 ? 3 : 0, idx); 10698c2ecf20Sopenharmony_ci } else { 10708c2ecf20Sopenharmony_ci reg = imm_a(nfp_prog); 10718c2ecf20Sopenharmony_ci /* If it's not the first part of the load and we start a new GPR 10728c2ecf20Sopenharmony_ci * that means we are loading a second part of the LMEM word into 10738c2ecf20Sopenharmony_ci * a new GPR. IOW we've already looked that LMEM word and 10748c2ecf20Sopenharmony_ci * therefore it has been loaded into imm_a(). 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_ci if (first || !new_gpr) 10778c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg, reg_lm(0, idx)); 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci emit_ld_field_any(nfp_prog, reg_both(dst), mask, reg, sc, shf, new_gpr); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci if (should_inc) 10838c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_none(), reg_lm_inc(3)); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci return 0; 10868c2ecf20Sopenharmony_ci} 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_cistatic int 10898c2ecf20Sopenharmony_ciwrp_lmem_store(struct nfp_prog *nfp_prog, u8 src, u8 src_byte, s32 off, 10908c2ecf20Sopenharmony_ci unsigned int size, bool first, bool new_gpr, bool last, bool lm3, 10918c2ecf20Sopenharmony_ci bool needs_inc) 10928c2ecf20Sopenharmony_ci{ 10938c2ecf20Sopenharmony_ci bool should_inc = needs_inc && new_gpr && !last; 10948c2ecf20Sopenharmony_ci u32 idx, dst_byte; 10958c2ecf20Sopenharmony_ci enum shf_sc sc; 10968c2ecf20Sopenharmony_ci swreg reg; 10978c2ecf20Sopenharmony_ci int shf; 10988c2ecf20Sopenharmony_ci u8 mask; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(src_byte + size > 4 || off % 4 + size > 4)) 11018c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci idx = off / 4; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* Move the entire word */ 11068c2ecf20Sopenharmony_ci if (size == 4) { 11078c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, 11088c2ecf20Sopenharmony_ci should_inc ? reg_lm_inc(3) : reg_lm(lm3 ? 3 : 0, idx), 11098c2ecf20Sopenharmony_ci reg_b(src)); 11108c2ecf20Sopenharmony_ci return 0; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(lm3 && idx > RE_REG_LM_IDX_MAX)) 11148c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci dst_byte = off % 4; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci mask = (1 << size) - 1; 11198c2ecf20Sopenharmony_ci mask <<= dst_byte; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(mask > 0xf)) 11228c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci shf = abs(src_byte - dst_byte) * 8; 11258c2ecf20Sopenharmony_ci if (src_byte == dst_byte) { 11268c2ecf20Sopenharmony_ci sc = SHF_SC_NONE; 11278c2ecf20Sopenharmony_ci } else if (src_byte < dst_byte) { 11288c2ecf20Sopenharmony_ci shf = 32 - shf; 11298c2ecf20Sopenharmony_ci sc = SHF_SC_L_SHF; 11308c2ecf20Sopenharmony_ci } else { 11318c2ecf20Sopenharmony_ci sc = SHF_SC_R_SHF; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci /* ld_field can address fewer indexes, if offset too large do RMW. 11358c2ecf20Sopenharmony_ci * Because we RMV twice we waste 2 cycles on unaligned 8 byte writes. 11368c2ecf20Sopenharmony_ci */ 11378c2ecf20Sopenharmony_ci if (idx <= RE_REG_LM_IDX_MAX) { 11388c2ecf20Sopenharmony_ci reg = reg_lm(lm3 ? 3 : 0, idx); 11398c2ecf20Sopenharmony_ci } else { 11408c2ecf20Sopenharmony_ci reg = imm_a(nfp_prog); 11418c2ecf20Sopenharmony_ci /* Only first and last LMEM locations are going to need RMW, 11428c2ecf20Sopenharmony_ci * the middle location will be overwritten fully. 11438c2ecf20Sopenharmony_ci */ 11448c2ecf20Sopenharmony_ci if (first || last) 11458c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg, reg_lm(0, idx)); 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci emit_ld_field(nfp_prog, reg, mask, reg_b(src), sc, shf); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci if (new_gpr || last) { 11518c2ecf20Sopenharmony_ci if (idx > RE_REG_LM_IDX_MAX) 11528c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_lm(0, idx), reg); 11538c2ecf20Sopenharmony_ci if (should_inc) 11548c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_none(), reg_lm_inc(3)); 11558c2ecf20Sopenharmony_ci } 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci return 0; 11588c2ecf20Sopenharmony_ci} 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_cistatic int 11618c2ecf20Sopenharmony_cimem_op_stack(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 11628c2ecf20Sopenharmony_ci unsigned int size, unsigned int ptr_off, u8 gpr, u8 ptr_gpr, 11638c2ecf20Sopenharmony_ci bool clr_gpr, lmem_step step) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci s32 off = nfp_prog->stack_frame_depth + meta->insn.off + ptr_off; 11668c2ecf20Sopenharmony_ci bool first = true, narrow_ld, last; 11678c2ecf20Sopenharmony_ci bool needs_inc = false; 11688c2ecf20Sopenharmony_ci swreg stack_off_reg; 11698c2ecf20Sopenharmony_ci u8 prev_gpr = 255; 11708c2ecf20Sopenharmony_ci u32 gpr_byte = 0; 11718c2ecf20Sopenharmony_ci bool lm3 = true; 11728c2ecf20Sopenharmony_ci int ret; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci if (meta->ptr_not_const || 11758c2ecf20Sopenharmony_ci meta->flags & FLAG_INSN_PTR_CALLER_STACK_FRAME) { 11768c2ecf20Sopenharmony_ci /* Use of the last encountered ptr_off is OK, they all have 11778c2ecf20Sopenharmony_ci * the same alignment. Depend on low bits of value being 11788c2ecf20Sopenharmony_ci * discarded when written to LMaddr register. 11798c2ecf20Sopenharmony_ci */ 11808c2ecf20Sopenharmony_ci stack_off_reg = ur_load_imm_any(nfp_prog, meta->insn.off, 11818c2ecf20Sopenharmony_ci stack_imm(nfp_prog)); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imm_b(nfp_prog), 11848c2ecf20Sopenharmony_ci reg_a(ptr_gpr), ALU_OP_ADD, stack_off_reg); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci needs_inc = true; 11878c2ecf20Sopenharmony_ci } else if (off + size <= 64) { 11888c2ecf20Sopenharmony_ci /* We can reach bottom 64B with LMaddr0 */ 11898c2ecf20Sopenharmony_ci lm3 = false; 11908c2ecf20Sopenharmony_ci } else if (round_down(off, 32) == round_down(off + size - 1, 32)) { 11918c2ecf20Sopenharmony_ci /* We have to set up a new pointer. If we know the offset 11928c2ecf20Sopenharmony_ci * and the entire access falls into a single 32 byte aligned 11938c2ecf20Sopenharmony_ci * window we won't have to increment the LM pointer. 11948c2ecf20Sopenharmony_ci * The 32 byte alignment is imporant because offset is ORed in 11958c2ecf20Sopenharmony_ci * not added when doing *l$indexN[off]. 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_ci stack_off_reg = ur_load_imm_any(nfp_prog, round_down(off, 32), 11988c2ecf20Sopenharmony_ci stack_imm(nfp_prog)); 11998c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imm_b(nfp_prog), 12008c2ecf20Sopenharmony_ci stack_reg(nfp_prog), ALU_OP_ADD, stack_off_reg); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci off %= 32; 12038c2ecf20Sopenharmony_ci } else { 12048c2ecf20Sopenharmony_ci stack_off_reg = ur_load_imm_any(nfp_prog, round_down(off, 4), 12058c2ecf20Sopenharmony_ci stack_imm(nfp_prog)); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imm_b(nfp_prog), 12088c2ecf20Sopenharmony_ci stack_reg(nfp_prog), ALU_OP_ADD, stack_off_reg); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci needs_inc = true; 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci narrow_ld = clr_gpr && size < 8; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci if (lm3) { 12168c2ecf20Sopenharmony_ci unsigned int nop_cnt; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci emit_csr_wr(nfp_prog, imm_b(nfp_prog), NFP_CSR_ACT_LM_ADDR3); 12198c2ecf20Sopenharmony_ci /* For size < 4 one slot will be filled by zeroing of upper, 12208c2ecf20Sopenharmony_ci * but be careful, that zeroing could be eliminated by zext 12218c2ecf20Sopenharmony_ci * optimization. 12228c2ecf20Sopenharmony_ci */ 12238c2ecf20Sopenharmony_ci nop_cnt = narrow_ld && meta->flags & FLAG_INSN_DO_ZEXT ? 2 : 3; 12248c2ecf20Sopenharmony_ci wrp_nops(nfp_prog, nop_cnt); 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci if (narrow_ld) 12288c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, gpr); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci while (size) { 12318c2ecf20Sopenharmony_ci u32 slice_end; 12328c2ecf20Sopenharmony_ci u8 slice_size; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci slice_size = min(size, 4 - gpr_byte); 12358c2ecf20Sopenharmony_ci slice_end = min(off + slice_size, round_up(off + 1, 4)); 12368c2ecf20Sopenharmony_ci slice_size = slice_end - off; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci last = slice_size == size; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci if (needs_inc) 12418c2ecf20Sopenharmony_ci off %= 4; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci ret = step(nfp_prog, gpr, gpr_byte, off, slice_size, 12448c2ecf20Sopenharmony_ci first, gpr != prev_gpr, last, lm3, needs_inc); 12458c2ecf20Sopenharmony_ci if (ret) 12468c2ecf20Sopenharmony_ci return ret; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci prev_gpr = gpr; 12498c2ecf20Sopenharmony_ci first = false; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci gpr_byte += slice_size; 12528c2ecf20Sopenharmony_ci if (gpr_byte >= 4) { 12538c2ecf20Sopenharmony_ci gpr_byte -= 4; 12548c2ecf20Sopenharmony_ci gpr++; 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci size -= slice_size; 12588c2ecf20Sopenharmony_ci off += slice_size; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci return 0; 12628c2ecf20Sopenharmony_ci} 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_cistatic void 12658c2ecf20Sopenharmony_ciwrp_alu_imm(struct nfp_prog *nfp_prog, u8 dst, enum alu_op alu_op, u32 imm) 12668c2ecf20Sopenharmony_ci{ 12678c2ecf20Sopenharmony_ci swreg tmp_reg; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci if (alu_op == ALU_OP_AND) { 12708c2ecf20Sopenharmony_ci if (!imm) 12718c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(dst), 0); 12728c2ecf20Sopenharmony_ci if (!imm || !~imm) 12738c2ecf20Sopenharmony_ci return; 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci if (alu_op == ALU_OP_OR) { 12768c2ecf20Sopenharmony_ci if (!~imm) 12778c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(dst), ~0U); 12788c2ecf20Sopenharmony_ci if (!imm || !~imm) 12798c2ecf20Sopenharmony_ci return; 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci if (alu_op == ALU_OP_XOR) { 12828c2ecf20Sopenharmony_ci if (!~imm) 12838c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_both(dst), reg_none(), 12848c2ecf20Sopenharmony_ci ALU_OP_NOT, reg_b(dst)); 12858c2ecf20Sopenharmony_ci if (!imm || !~imm) 12868c2ecf20Sopenharmony_ci return; 12878c2ecf20Sopenharmony_ci } 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci tmp_reg = ur_load_imm_any(nfp_prog, imm, imm_b(nfp_prog)); 12908c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_both(dst), reg_a(dst), alu_op, tmp_reg); 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_cistatic int 12948c2ecf20Sopenharmony_ciwrp_alu64_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 12958c2ecf20Sopenharmony_ci enum alu_op alu_op, bool skip) 12968c2ecf20Sopenharmony_ci{ 12978c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 12988c2ecf20Sopenharmony_ci u64 imm = insn->imm; /* sign extend */ 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci if (skip) { 13018c2ecf20Sopenharmony_ci meta->flags |= FLAG_INSN_SKIP_NOOP; 13028c2ecf20Sopenharmony_ci return 0; 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci wrp_alu_imm(nfp_prog, insn->dst_reg * 2, alu_op, imm & ~0U); 13068c2ecf20Sopenharmony_ci wrp_alu_imm(nfp_prog, insn->dst_reg * 2 + 1, alu_op, imm >> 32); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci return 0; 13098c2ecf20Sopenharmony_ci} 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_cistatic int 13128c2ecf20Sopenharmony_ciwrp_alu64_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 13138c2ecf20Sopenharmony_ci enum alu_op alu_op) 13148c2ecf20Sopenharmony_ci{ 13158c2ecf20Sopenharmony_ci u8 dst = meta->insn.dst_reg * 2, src = meta->insn.src_reg * 2; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_both(dst), reg_a(dst), alu_op, reg_b(src)); 13188c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_both(dst + 1), 13198c2ecf20Sopenharmony_ci reg_a(dst + 1), alu_op, reg_b(src + 1)); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci return 0; 13228c2ecf20Sopenharmony_ci} 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_cistatic int 13258c2ecf20Sopenharmony_ciwrp_alu32_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 13268c2ecf20Sopenharmony_ci enum alu_op alu_op) 13278c2ecf20Sopenharmony_ci{ 13288c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 13298c2ecf20Sopenharmony_ci u8 dst = insn->dst_reg * 2; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci wrp_alu_imm(nfp_prog, dst, alu_op, insn->imm); 13328c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst); 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci return 0; 13358c2ecf20Sopenharmony_ci} 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_cistatic int 13388c2ecf20Sopenharmony_ciwrp_alu32_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 13398c2ecf20Sopenharmony_ci enum alu_op alu_op) 13408c2ecf20Sopenharmony_ci{ 13418c2ecf20Sopenharmony_ci u8 dst = meta->insn.dst_reg * 2, src = meta->insn.src_reg * 2; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_both(dst), reg_a(dst), alu_op, reg_b(src)); 13448c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst); 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci return 0; 13478c2ecf20Sopenharmony_ci} 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_cistatic void 13508c2ecf20Sopenharmony_ciwrp_test_reg_one(struct nfp_prog *nfp_prog, u8 dst, enum alu_op alu_op, u8 src, 13518c2ecf20Sopenharmony_ci enum br_mask br_mask, u16 off) 13528c2ecf20Sopenharmony_ci{ 13538c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(dst), alu_op, reg_b(src)); 13548c2ecf20Sopenharmony_ci emit_br(nfp_prog, br_mask, off, 0); 13558c2ecf20Sopenharmony_ci} 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_cistatic int 13588c2ecf20Sopenharmony_ciwrp_test_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 13598c2ecf20Sopenharmony_ci enum alu_op alu_op, enum br_mask br_mask) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci wrp_test_reg_one(nfp_prog, insn->dst_reg * 2, alu_op, 13648c2ecf20Sopenharmony_ci insn->src_reg * 2, br_mask, insn->off); 13658c2ecf20Sopenharmony_ci if (is_mbpf_jmp64(meta)) 13668c2ecf20Sopenharmony_ci wrp_test_reg_one(nfp_prog, insn->dst_reg * 2 + 1, alu_op, 13678c2ecf20Sopenharmony_ci insn->src_reg * 2 + 1, br_mask, insn->off); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci return 0; 13708c2ecf20Sopenharmony_ci} 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_cistatic const struct jmp_code_map { 13738c2ecf20Sopenharmony_ci enum br_mask br_mask; 13748c2ecf20Sopenharmony_ci bool swap; 13758c2ecf20Sopenharmony_ci} jmp_code_map[] = { 13768c2ecf20Sopenharmony_ci [BPF_JGT >> 4] = { BR_BLO, true }, 13778c2ecf20Sopenharmony_ci [BPF_JGE >> 4] = { BR_BHS, false }, 13788c2ecf20Sopenharmony_ci [BPF_JLT >> 4] = { BR_BLO, false }, 13798c2ecf20Sopenharmony_ci [BPF_JLE >> 4] = { BR_BHS, true }, 13808c2ecf20Sopenharmony_ci [BPF_JSGT >> 4] = { BR_BLT, true }, 13818c2ecf20Sopenharmony_ci [BPF_JSGE >> 4] = { BR_BGE, false }, 13828c2ecf20Sopenharmony_ci [BPF_JSLT >> 4] = { BR_BLT, false }, 13838c2ecf20Sopenharmony_ci [BPF_JSLE >> 4] = { BR_BGE, true }, 13848c2ecf20Sopenharmony_ci}; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_cistatic const struct jmp_code_map *nfp_jmp_code_get(struct nfp_insn_meta *meta) 13878c2ecf20Sopenharmony_ci{ 13888c2ecf20Sopenharmony_ci unsigned int op; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci op = BPF_OP(meta->insn.code) >> 4; 13918c2ecf20Sopenharmony_ci /* br_mask of 0 is BR_BEQ which we don't use in jump code table */ 13928c2ecf20Sopenharmony_ci if (WARN_ONCE(op >= ARRAY_SIZE(jmp_code_map) || 13938c2ecf20Sopenharmony_ci !jmp_code_map[op].br_mask, 13948c2ecf20Sopenharmony_ci "no code found for jump instruction")) 13958c2ecf20Sopenharmony_ci return NULL; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci return &jmp_code_map[op]; 13988c2ecf20Sopenharmony_ci} 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_cistatic int cmp_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 14018c2ecf20Sopenharmony_ci{ 14028c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 14038c2ecf20Sopenharmony_ci u64 imm = insn->imm; /* sign extend */ 14048c2ecf20Sopenharmony_ci const struct jmp_code_map *code; 14058c2ecf20Sopenharmony_ci enum alu_op alu_op, carry_op; 14068c2ecf20Sopenharmony_ci u8 reg = insn->dst_reg * 2; 14078c2ecf20Sopenharmony_ci swreg tmp_reg; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci code = nfp_jmp_code_get(meta); 14108c2ecf20Sopenharmony_ci if (!code) 14118c2ecf20Sopenharmony_ci return -EINVAL; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci alu_op = meta->jump_neg_op ? ALU_OP_ADD : ALU_OP_SUB; 14148c2ecf20Sopenharmony_ci carry_op = meta->jump_neg_op ? ALU_OP_ADD_C : ALU_OP_SUB_C; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci tmp_reg = ur_load_imm_any(nfp_prog, imm & ~0U, imm_b(nfp_prog)); 14178c2ecf20Sopenharmony_ci if (!code->swap) 14188c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(reg), alu_op, tmp_reg); 14198c2ecf20Sopenharmony_ci else 14208c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), tmp_reg, alu_op, reg_a(reg)); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci if (is_mbpf_jmp64(meta)) { 14238c2ecf20Sopenharmony_ci tmp_reg = ur_load_imm_any(nfp_prog, imm >> 32, imm_b(nfp_prog)); 14248c2ecf20Sopenharmony_ci if (!code->swap) 14258c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), 14268c2ecf20Sopenharmony_ci reg_a(reg + 1), carry_op, tmp_reg); 14278c2ecf20Sopenharmony_ci else 14288c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), 14298c2ecf20Sopenharmony_ci tmp_reg, carry_op, reg_a(reg + 1)); 14308c2ecf20Sopenharmony_ci } 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci emit_br(nfp_prog, code->br_mask, insn->off, 0); 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci return 0; 14358c2ecf20Sopenharmony_ci} 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_cistatic int cmp_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 14388c2ecf20Sopenharmony_ci{ 14398c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 14408c2ecf20Sopenharmony_ci const struct jmp_code_map *code; 14418c2ecf20Sopenharmony_ci u8 areg, breg; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci code = nfp_jmp_code_get(meta); 14448c2ecf20Sopenharmony_ci if (!code) 14458c2ecf20Sopenharmony_ci return -EINVAL; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci areg = insn->dst_reg * 2; 14488c2ecf20Sopenharmony_ci breg = insn->src_reg * 2; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (code->swap) { 14518c2ecf20Sopenharmony_ci areg ^= breg; 14528c2ecf20Sopenharmony_ci breg ^= areg; 14538c2ecf20Sopenharmony_ci areg ^= breg; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(areg), ALU_OP_SUB, reg_b(breg)); 14578c2ecf20Sopenharmony_ci if (is_mbpf_jmp64(meta)) 14588c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), 14598c2ecf20Sopenharmony_ci reg_a(areg + 1), ALU_OP_SUB_C, reg_b(breg + 1)); 14608c2ecf20Sopenharmony_ci emit_br(nfp_prog, code->br_mask, insn->off, 0); 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci return 0; 14638c2ecf20Sopenharmony_ci} 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_cistatic void wrp_end32(struct nfp_prog *nfp_prog, swreg reg_in, u8 gpr_out) 14668c2ecf20Sopenharmony_ci{ 14678c2ecf20Sopenharmony_ci emit_ld_field(nfp_prog, reg_both(gpr_out), 0xf, reg_in, 14688c2ecf20Sopenharmony_ci SHF_SC_R_ROT, 8); 14698c2ecf20Sopenharmony_ci emit_ld_field(nfp_prog, reg_both(gpr_out), 0x5, reg_a(gpr_out), 14708c2ecf20Sopenharmony_ci SHF_SC_R_ROT, 16); 14718c2ecf20Sopenharmony_ci} 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_cistatic void 14748c2ecf20Sopenharmony_ciwrp_mul_u32(struct nfp_prog *nfp_prog, swreg dst_hi, swreg dst_lo, swreg lreg, 14758c2ecf20Sopenharmony_ci swreg rreg, bool gen_high_half) 14768c2ecf20Sopenharmony_ci{ 14778c2ecf20Sopenharmony_ci emit_mul(nfp_prog, lreg, MUL_TYPE_START, MUL_STEP_NONE, rreg); 14788c2ecf20Sopenharmony_ci emit_mul(nfp_prog, lreg, MUL_TYPE_STEP_32x32, MUL_STEP_1, rreg); 14798c2ecf20Sopenharmony_ci emit_mul(nfp_prog, lreg, MUL_TYPE_STEP_32x32, MUL_STEP_2, rreg); 14808c2ecf20Sopenharmony_ci emit_mul(nfp_prog, lreg, MUL_TYPE_STEP_32x32, MUL_STEP_3, rreg); 14818c2ecf20Sopenharmony_ci emit_mul(nfp_prog, lreg, MUL_TYPE_STEP_32x32, MUL_STEP_4, rreg); 14828c2ecf20Sopenharmony_ci emit_mul(nfp_prog, dst_lo, MUL_TYPE_STEP_32x32, MUL_LAST, reg_none()); 14838c2ecf20Sopenharmony_ci if (gen_high_half) 14848c2ecf20Sopenharmony_ci emit_mul(nfp_prog, dst_hi, MUL_TYPE_STEP_32x32, MUL_LAST_2, 14858c2ecf20Sopenharmony_ci reg_none()); 14868c2ecf20Sopenharmony_ci else 14878c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, dst_hi, 0); 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_cistatic void 14918c2ecf20Sopenharmony_ciwrp_mul_u16(struct nfp_prog *nfp_prog, swreg dst_hi, swreg dst_lo, swreg lreg, 14928c2ecf20Sopenharmony_ci swreg rreg) 14938c2ecf20Sopenharmony_ci{ 14948c2ecf20Sopenharmony_ci emit_mul(nfp_prog, lreg, MUL_TYPE_START, MUL_STEP_NONE, rreg); 14958c2ecf20Sopenharmony_ci emit_mul(nfp_prog, lreg, MUL_TYPE_STEP_16x16, MUL_STEP_1, rreg); 14968c2ecf20Sopenharmony_ci emit_mul(nfp_prog, lreg, MUL_TYPE_STEP_16x16, MUL_STEP_2, rreg); 14978c2ecf20Sopenharmony_ci emit_mul(nfp_prog, dst_lo, MUL_TYPE_STEP_16x16, MUL_LAST, reg_none()); 14988c2ecf20Sopenharmony_ci} 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_cistatic int 15018c2ecf20Sopenharmony_ciwrp_mul(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 15028c2ecf20Sopenharmony_ci bool gen_high_half, bool ropnd_from_reg) 15038c2ecf20Sopenharmony_ci{ 15048c2ecf20Sopenharmony_ci swreg multiplier, multiplicand, dst_hi, dst_lo; 15058c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 15068c2ecf20Sopenharmony_ci u32 lopnd_max, ropnd_max; 15078c2ecf20Sopenharmony_ci u8 dst_reg; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci dst_reg = insn->dst_reg; 15108c2ecf20Sopenharmony_ci multiplicand = reg_a(dst_reg * 2); 15118c2ecf20Sopenharmony_ci dst_hi = reg_both(dst_reg * 2 + 1); 15128c2ecf20Sopenharmony_ci dst_lo = reg_both(dst_reg * 2); 15138c2ecf20Sopenharmony_ci lopnd_max = meta->umax_dst; 15148c2ecf20Sopenharmony_ci if (ropnd_from_reg) { 15158c2ecf20Sopenharmony_ci multiplier = reg_b(insn->src_reg * 2); 15168c2ecf20Sopenharmony_ci ropnd_max = meta->umax_src; 15178c2ecf20Sopenharmony_ci } else { 15188c2ecf20Sopenharmony_ci u32 imm = insn->imm; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci multiplier = ur_load_imm_any(nfp_prog, imm, imm_b(nfp_prog)); 15218c2ecf20Sopenharmony_ci ropnd_max = imm; 15228c2ecf20Sopenharmony_ci } 15238c2ecf20Sopenharmony_ci if (lopnd_max > U16_MAX || ropnd_max > U16_MAX) 15248c2ecf20Sopenharmony_ci wrp_mul_u32(nfp_prog, dst_hi, dst_lo, multiplicand, multiplier, 15258c2ecf20Sopenharmony_ci gen_high_half); 15268c2ecf20Sopenharmony_ci else 15278c2ecf20Sopenharmony_ci wrp_mul_u16(nfp_prog, dst_hi, dst_lo, multiplicand, multiplier); 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci return 0; 15308c2ecf20Sopenharmony_ci} 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic int wrp_div_imm(struct nfp_prog *nfp_prog, u8 dst, u64 imm) 15338c2ecf20Sopenharmony_ci{ 15348c2ecf20Sopenharmony_ci swreg dst_both = reg_both(dst), dst_a = reg_a(dst), dst_b = reg_a(dst); 15358c2ecf20Sopenharmony_ci struct reciprocal_value_adv rvalue; 15368c2ecf20Sopenharmony_ci u8 pre_shift, exp; 15378c2ecf20Sopenharmony_ci swreg magic; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci if (imm > U32_MAX) { 15408c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, dst_both, 0); 15418c2ecf20Sopenharmony_ci return 0; 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci /* NOTE: because we are using "reciprocal_value_adv" which doesn't 15458c2ecf20Sopenharmony_ci * support "divisor > (1u << 31)", we need to JIT separate NFP sequence 15468c2ecf20Sopenharmony_ci * to handle such case which actually equals to the result of unsigned 15478c2ecf20Sopenharmony_ci * comparison "dst >= imm" which could be calculated using the following 15488c2ecf20Sopenharmony_ci * NFP sequence: 15498c2ecf20Sopenharmony_ci * 15508c2ecf20Sopenharmony_ci * alu[--, dst, -, imm] 15518c2ecf20Sopenharmony_ci * immed[imm, 0] 15528c2ecf20Sopenharmony_ci * alu[dst, imm, +carry, 0] 15538c2ecf20Sopenharmony_ci * 15548c2ecf20Sopenharmony_ci */ 15558c2ecf20Sopenharmony_ci if (imm > 1U << 31) { 15568c2ecf20Sopenharmony_ci swreg tmp_b = ur_load_imm_any(nfp_prog, imm, imm_b(nfp_prog)); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), dst_a, ALU_OP_SUB, tmp_b); 15598c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, imm_a(nfp_prog), 0); 15608c2ecf20Sopenharmony_ci emit_alu(nfp_prog, dst_both, imm_a(nfp_prog), ALU_OP_ADD_C, 15618c2ecf20Sopenharmony_ci reg_imm(0)); 15628c2ecf20Sopenharmony_ci return 0; 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci rvalue = reciprocal_value_adv(imm, 32); 15668c2ecf20Sopenharmony_ci exp = rvalue.exp; 15678c2ecf20Sopenharmony_ci if (rvalue.is_wide_m && !(imm & 1)) { 15688c2ecf20Sopenharmony_ci pre_shift = fls(imm & -imm) - 1; 15698c2ecf20Sopenharmony_ci rvalue = reciprocal_value_adv(imm >> pre_shift, 32 - pre_shift); 15708c2ecf20Sopenharmony_ci } else { 15718c2ecf20Sopenharmony_ci pre_shift = 0; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci magic = ur_load_imm_any(nfp_prog, rvalue.m, imm_b(nfp_prog)); 15748c2ecf20Sopenharmony_ci if (imm == 1U << exp) { 15758c2ecf20Sopenharmony_ci emit_shf(nfp_prog, dst_both, reg_none(), SHF_OP_NONE, dst_b, 15768c2ecf20Sopenharmony_ci SHF_SC_R_SHF, exp); 15778c2ecf20Sopenharmony_ci } else if (rvalue.is_wide_m) { 15788c2ecf20Sopenharmony_ci wrp_mul_u32(nfp_prog, imm_both(nfp_prog), reg_none(), dst_a, 15798c2ecf20Sopenharmony_ci magic, true); 15808c2ecf20Sopenharmony_ci emit_alu(nfp_prog, dst_both, dst_a, ALU_OP_SUB, 15818c2ecf20Sopenharmony_ci imm_b(nfp_prog)); 15828c2ecf20Sopenharmony_ci emit_shf(nfp_prog, dst_both, reg_none(), SHF_OP_NONE, dst_b, 15838c2ecf20Sopenharmony_ci SHF_SC_R_SHF, 1); 15848c2ecf20Sopenharmony_ci emit_alu(nfp_prog, dst_both, dst_a, ALU_OP_ADD, 15858c2ecf20Sopenharmony_ci imm_b(nfp_prog)); 15868c2ecf20Sopenharmony_ci emit_shf(nfp_prog, dst_both, reg_none(), SHF_OP_NONE, dst_b, 15878c2ecf20Sopenharmony_ci SHF_SC_R_SHF, rvalue.sh - 1); 15888c2ecf20Sopenharmony_ci } else { 15898c2ecf20Sopenharmony_ci if (pre_shift) 15908c2ecf20Sopenharmony_ci emit_shf(nfp_prog, dst_both, reg_none(), SHF_OP_NONE, 15918c2ecf20Sopenharmony_ci dst_b, SHF_SC_R_SHF, pre_shift); 15928c2ecf20Sopenharmony_ci wrp_mul_u32(nfp_prog, dst_both, reg_none(), dst_a, magic, true); 15938c2ecf20Sopenharmony_ci emit_shf(nfp_prog, dst_both, reg_none(), SHF_OP_NONE, 15948c2ecf20Sopenharmony_ci dst_b, SHF_SC_R_SHF, rvalue.sh); 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci return 0; 15988c2ecf20Sopenharmony_ci} 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_cistatic int adjust_head(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 16018c2ecf20Sopenharmony_ci{ 16028c2ecf20Sopenharmony_ci swreg tmp = imm_a(nfp_prog), tmp_len = imm_b(nfp_prog); 16038c2ecf20Sopenharmony_ci struct nfp_bpf_cap_adjust_head *adjust_head; 16048c2ecf20Sopenharmony_ci u32 ret_einval, end; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci adjust_head = &nfp_prog->bpf->adjust_head; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci /* Optimized version - 5 vs 14 cycles */ 16098c2ecf20Sopenharmony_ci if (nfp_prog->adjust_head_location != UINT_MAX) { 16108c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(nfp_prog->adjust_head_location != meta->n)) 16118c2ecf20Sopenharmony_ci return -EINVAL; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci emit_alu(nfp_prog, pptr_reg(nfp_prog), 16148c2ecf20Sopenharmony_ci reg_a(2 * 2), ALU_OP_ADD, pptr_reg(nfp_prog)); 16158c2ecf20Sopenharmony_ci emit_alu(nfp_prog, plen_reg(nfp_prog), 16168c2ecf20Sopenharmony_ci plen_reg(nfp_prog), ALU_OP_SUB, reg_a(2 * 2)); 16178c2ecf20Sopenharmony_ci emit_alu(nfp_prog, pv_len(nfp_prog), 16188c2ecf20Sopenharmony_ci pv_len(nfp_prog), ALU_OP_SUB, reg_a(2 * 2)); 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(0), 0); 16218c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(1), 0); 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci /* TODO: when adjust head is guaranteed to succeed we can 16248c2ecf20Sopenharmony_ci * also eliminate the following if (r0 == 0) branch. 16258c2ecf20Sopenharmony_ci */ 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci return 0; 16288c2ecf20Sopenharmony_ci } 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci ret_einval = nfp_prog_current_offset(nfp_prog) + 14; 16318c2ecf20Sopenharmony_ci end = ret_einval + 2; 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci /* We need to use a temp because offset is just a part of the pkt ptr */ 16348c2ecf20Sopenharmony_ci emit_alu(nfp_prog, tmp, 16358c2ecf20Sopenharmony_ci reg_a(2 * 2), ALU_OP_ADD_2B, pptr_reg(nfp_prog)); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci /* Validate result will fit within FW datapath constraints */ 16388c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), 16398c2ecf20Sopenharmony_ci tmp, ALU_OP_SUB, reg_imm(adjust_head->off_min)); 16408c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BLO, ret_einval, 0); 16418c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), 16428c2ecf20Sopenharmony_ci reg_imm(adjust_head->off_max), ALU_OP_SUB, tmp); 16438c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BLO, ret_einval, 0); 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci /* Validate the length is at least ETH_HLEN */ 16468c2ecf20Sopenharmony_ci emit_alu(nfp_prog, tmp_len, 16478c2ecf20Sopenharmony_ci plen_reg(nfp_prog), ALU_OP_SUB, reg_a(2 * 2)); 16488c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), 16498c2ecf20Sopenharmony_ci tmp_len, ALU_OP_SUB, reg_imm(ETH_HLEN)); 16508c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BMI, ret_einval, 0); 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci /* Load the ret code */ 16538c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(0), 0); 16548c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(1), 0); 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci /* Modify the packet metadata */ 16578c2ecf20Sopenharmony_ci emit_ld_field(nfp_prog, pptr_reg(nfp_prog), 0x3, tmp, SHF_SC_NONE, 0); 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci /* Skip over the -EINVAL ret code (defer 2) */ 16608c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_UNC, end, 2); 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci emit_alu(nfp_prog, plen_reg(nfp_prog), 16638c2ecf20Sopenharmony_ci plen_reg(nfp_prog), ALU_OP_SUB, reg_a(2 * 2)); 16648c2ecf20Sopenharmony_ci emit_alu(nfp_prog, pv_len(nfp_prog), 16658c2ecf20Sopenharmony_ci pv_len(nfp_prog), ALU_OP_SUB, reg_a(2 * 2)); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci /* return -EINVAL target */ 16688c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, ret_einval)) 16698c2ecf20Sopenharmony_ci return -EINVAL; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(0), -22); 16728c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(1), ~0); 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, end)) 16758c2ecf20Sopenharmony_ci return -EINVAL; 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci return 0; 16788c2ecf20Sopenharmony_ci} 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_cistatic int adjust_tail(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 16818c2ecf20Sopenharmony_ci{ 16828c2ecf20Sopenharmony_ci u32 ret_einval, end; 16838c2ecf20Sopenharmony_ci swreg plen, delta; 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci BUILD_BUG_ON(plen_reg(nfp_prog) != reg_b(STATIC_REG_PKT_LEN)); 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci plen = imm_a(nfp_prog); 16888c2ecf20Sopenharmony_ci delta = reg_a(2 * 2); 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci ret_einval = nfp_prog_current_offset(nfp_prog) + 9; 16918c2ecf20Sopenharmony_ci end = nfp_prog_current_offset(nfp_prog) + 11; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci /* Calculate resulting length */ 16948c2ecf20Sopenharmony_ci emit_alu(nfp_prog, plen, plen_reg(nfp_prog), ALU_OP_ADD, delta); 16958c2ecf20Sopenharmony_ci /* delta == 0 is not allowed by the kernel, add must overflow to make 16968c2ecf20Sopenharmony_ci * length smaller. 16978c2ecf20Sopenharmony_ci */ 16988c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BCC, ret_einval, 0); 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci /* if (new_len < 14) then -EINVAL */ 17018c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), plen, ALU_OP_SUB, reg_imm(ETH_HLEN)); 17028c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BMI, ret_einval, 0); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci emit_alu(nfp_prog, plen_reg(nfp_prog), 17058c2ecf20Sopenharmony_ci plen_reg(nfp_prog), ALU_OP_ADD, delta); 17068c2ecf20Sopenharmony_ci emit_alu(nfp_prog, pv_len(nfp_prog), 17078c2ecf20Sopenharmony_ci pv_len(nfp_prog), ALU_OP_ADD, delta); 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_UNC, end, 2); 17108c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(0), 0); 17118c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(1), 0); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, ret_einval)) 17148c2ecf20Sopenharmony_ci return -EINVAL; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(0), -22); 17178c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(1), ~0); 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, end)) 17208c2ecf20Sopenharmony_ci return -EINVAL; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci return 0; 17238c2ecf20Sopenharmony_ci} 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_cistatic int 17268c2ecf20Sopenharmony_cimap_call_stack_common(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 17278c2ecf20Sopenharmony_ci{ 17288c2ecf20Sopenharmony_ci bool load_lm_ptr; 17298c2ecf20Sopenharmony_ci u32 ret_tgt; 17308c2ecf20Sopenharmony_ci s64 lm_off; 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci /* We only have to reload LM0 if the key is not at start of stack */ 17338c2ecf20Sopenharmony_ci lm_off = nfp_prog->stack_frame_depth; 17348c2ecf20Sopenharmony_ci lm_off += meta->arg2.reg.var_off.value + meta->arg2.reg.off; 17358c2ecf20Sopenharmony_ci load_lm_ptr = meta->arg2.var_off || lm_off; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci /* Set LM0 to start of key */ 17388c2ecf20Sopenharmony_ci if (load_lm_ptr) 17398c2ecf20Sopenharmony_ci emit_csr_wr(nfp_prog, reg_b(2 * 2), NFP_CSR_ACT_LM_ADDR0); 17408c2ecf20Sopenharmony_ci if (meta->func_id == BPF_FUNC_map_update_elem) 17418c2ecf20Sopenharmony_ci emit_csr_wr(nfp_prog, reg_b(3 * 2), NFP_CSR_ACT_LM_ADDR2); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO + meta->func_id, 17448c2ecf20Sopenharmony_ci 2, RELO_BR_HELPER); 17458c2ecf20Sopenharmony_ci ret_tgt = nfp_prog_current_offset(nfp_prog) + 2; 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci /* Load map ID into A0 */ 17488c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_a(0), reg_a(2)); 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci /* Load the return address into B0 */ 17518c2ecf20Sopenharmony_ci wrp_immed_relo(nfp_prog, reg_b(0), ret_tgt, RELO_IMMED_REL); 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, ret_tgt)) 17548c2ecf20Sopenharmony_ci return -EINVAL; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci /* Reset the LM0 pointer */ 17578c2ecf20Sopenharmony_ci if (!load_lm_ptr) 17588c2ecf20Sopenharmony_ci return 0; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci emit_csr_wr(nfp_prog, stack_reg(nfp_prog), NFP_CSR_ACT_LM_ADDR0); 17618c2ecf20Sopenharmony_ci wrp_nops(nfp_prog, 3); 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci return 0; 17648c2ecf20Sopenharmony_ci} 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_cistatic int 17678c2ecf20Sopenharmony_cinfp_get_prandom_u32(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 17688c2ecf20Sopenharmony_ci{ 17698c2ecf20Sopenharmony_ci __emit_csr_rd(nfp_prog, NFP_CSR_PSEUDO_RND_NUM); 17708c2ecf20Sopenharmony_ci /* CSR value is read in following immed[gpr, 0] */ 17718c2ecf20Sopenharmony_ci emit_immed(nfp_prog, reg_both(0), 0, 17728c2ecf20Sopenharmony_ci IMMED_WIDTH_ALL, false, IMMED_SHIFT_0B); 17738c2ecf20Sopenharmony_ci emit_immed(nfp_prog, reg_both(1), 0, 17748c2ecf20Sopenharmony_ci IMMED_WIDTH_ALL, false, IMMED_SHIFT_0B); 17758c2ecf20Sopenharmony_ci return 0; 17768c2ecf20Sopenharmony_ci} 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_cistatic int 17798c2ecf20Sopenharmony_cinfp_perf_event_output(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 17808c2ecf20Sopenharmony_ci{ 17818c2ecf20Sopenharmony_ci swreg ptr_type; 17828c2ecf20Sopenharmony_ci u32 ret_tgt; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci ptr_type = ur_load_imm_any(nfp_prog, meta->arg1.type, imm_a(nfp_prog)); 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci ret_tgt = nfp_prog_current_offset(nfp_prog) + 3; 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO + meta->func_id, 17898c2ecf20Sopenharmony_ci 2, RELO_BR_HELPER); 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci /* Load ptr type into A1 */ 17928c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_a(1), ptr_type); 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci /* Load the return address into B0 */ 17958c2ecf20Sopenharmony_ci wrp_immed_relo(nfp_prog, reg_b(0), ret_tgt, RELO_IMMED_REL); 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, ret_tgt)) 17988c2ecf20Sopenharmony_ci return -EINVAL; 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci return 0; 18018c2ecf20Sopenharmony_ci} 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_cistatic int 18048c2ecf20Sopenharmony_cinfp_queue_select(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 18058c2ecf20Sopenharmony_ci{ 18068c2ecf20Sopenharmony_ci u32 jmp_tgt; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci jmp_tgt = nfp_prog_current_offset(nfp_prog) + 5; 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci /* Make sure the queue id fits into FW field */ 18118c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(meta->insn.src_reg * 2), 18128c2ecf20Sopenharmony_ci ALU_OP_AND_NOT_B, reg_imm(0xff)); 18138c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BEQ, jmp_tgt, 2); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci /* Set the 'queue selected' bit and the queue value */ 18168c2ecf20Sopenharmony_ci emit_shf(nfp_prog, pv_qsel_set(nfp_prog), 18178c2ecf20Sopenharmony_ci pv_qsel_set(nfp_prog), SHF_OP_OR, reg_imm(1), 18188c2ecf20Sopenharmony_ci SHF_SC_L_SHF, PKT_VEL_QSEL_SET_BIT); 18198c2ecf20Sopenharmony_ci emit_ld_field(nfp_prog, 18208c2ecf20Sopenharmony_ci pv_qsel_val(nfp_prog), 0x1, reg_b(meta->insn.src_reg * 2), 18218c2ecf20Sopenharmony_ci SHF_SC_NONE, 0); 18228c2ecf20Sopenharmony_ci /* Delay slots end here, we will jump over next instruction if queue 18238c2ecf20Sopenharmony_ci * value fits into the field. 18248c2ecf20Sopenharmony_ci */ 18258c2ecf20Sopenharmony_ci emit_ld_field(nfp_prog, 18268c2ecf20Sopenharmony_ci pv_qsel_val(nfp_prog), 0x1, reg_imm(NFP_NET_RXR_MAX), 18278c2ecf20Sopenharmony_ci SHF_SC_NONE, 0); 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, jmp_tgt)) 18308c2ecf20Sopenharmony_ci return -EINVAL; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci return 0; 18338c2ecf20Sopenharmony_ci} 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci/* --- Callbacks --- */ 18368c2ecf20Sopenharmony_cistatic int mov_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 18378c2ecf20Sopenharmony_ci{ 18388c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 18398c2ecf20Sopenharmony_ci u8 dst = insn->dst_reg * 2; 18408c2ecf20Sopenharmony_ci u8 src = insn->src_reg * 2; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci if (insn->src_reg == BPF_REG_10) { 18438c2ecf20Sopenharmony_ci swreg stack_depth_reg; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci stack_depth_reg = ur_load_imm_any(nfp_prog, 18468c2ecf20Sopenharmony_ci nfp_prog->stack_frame_depth, 18478c2ecf20Sopenharmony_ci stack_imm(nfp_prog)); 18488c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_both(dst), stack_reg(nfp_prog), 18498c2ecf20Sopenharmony_ci ALU_OP_ADD, stack_depth_reg); 18508c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(dst + 1), 0); 18518c2ecf20Sopenharmony_ci } else { 18528c2ecf20Sopenharmony_ci wrp_reg_mov(nfp_prog, dst, src); 18538c2ecf20Sopenharmony_ci wrp_reg_mov(nfp_prog, dst + 1, src + 1); 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci return 0; 18578c2ecf20Sopenharmony_ci} 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_cistatic int mov_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 18608c2ecf20Sopenharmony_ci{ 18618c2ecf20Sopenharmony_ci u64 imm = meta->insn.imm; /* sign extend */ 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(meta->insn.dst_reg * 2), imm & ~0U); 18648c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(meta->insn.dst_reg * 2 + 1), imm >> 32); 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci return 0; 18678c2ecf20Sopenharmony_ci} 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_cistatic int xor_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 18708c2ecf20Sopenharmony_ci{ 18718c2ecf20Sopenharmony_ci return wrp_alu64_reg(nfp_prog, meta, ALU_OP_XOR); 18728c2ecf20Sopenharmony_ci} 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_cistatic int xor_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 18758c2ecf20Sopenharmony_ci{ 18768c2ecf20Sopenharmony_ci return wrp_alu64_imm(nfp_prog, meta, ALU_OP_XOR, !meta->insn.imm); 18778c2ecf20Sopenharmony_ci} 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_cistatic int and_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 18808c2ecf20Sopenharmony_ci{ 18818c2ecf20Sopenharmony_ci return wrp_alu64_reg(nfp_prog, meta, ALU_OP_AND); 18828c2ecf20Sopenharmony_ci} 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_cistatic int and_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 18858c2ecf20Sopenharmony_ci{ 18868c2ecf20Sopenharmony_ci return wrp_alu64_imm(nfp_prog, meta, ALU_OP_AND, !~meta->insn.imm); 18878c2ecf20Sopenharmony_ci} 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_cistatic int or_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 18908c2ecf20Sopenharmony_ci{ 18918c2ecf20Sopenharmony_ci return wrp_alu64_reg(nfp_prog, meta, ALU_OP_OR); 18928c2ecf20Sopenharmony_ci} 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_cistatic int or_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 18958c2ecf20Sopenharmony_ci{ 18968c2ecf20Sopenharmony_ci return wrp_alu64_imm(nfp_prog, meta, ALU_OP_OR, !meta->insn.imm); 18978c2ecf20Sopenharmony_ci} 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_cistatic int add_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 19008c2ecf20Sopenharmony_ci{ 19018c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_both(insn->dst_reg * 2), 19048c2ecf20Sopenharmony_ci reg_a(insn->dst_reg * 2), ALU_OP_ADD, 19058c2ecf20Sopenharmony_ci reg_b(insn->src_reg * 2)); 19068c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_both(insn->dst_reg * 2 + 1), 19078c2ecf20Sopenharmony_ci reg_a(insn->dst_reg * 2 + 1), ALU_OP_ADD_C, 19088c2ecf20Sopenharmony_ci reg_b(insn->src_reg * 2 + 1)); 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci return 0; 19118c2ecf20Sopenharmony_ci} 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_cistatic int add_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 19148c2ecf20Sopenharmony_ci{ 19158c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 19168c2ecf20Sopenharmony_ci u64 imm = insn->imm; /* sign extend */ 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci wrp_alu_imm(nfp_prog, insn->dst_reg * 2, ALU_OP_ADD, imm & ~0U); 19198c2ecf20Sopenharmony_ci wrp_alu_imm(nfp_prog, insn->dst_reg * 2 + 1, ALU_OP_ADD_C, imm >> 32); 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci return 0; 19228c2ecf20Sopenharmony_ci} 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_cistatic int sub_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 19258c2ecf20Sopenharmony_ci{ 19268c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_both(insn->dst_reg * 2), 19298c2ecf20Sopenharmony_ci reg_a(insn->dst_reg * 2), ALU_OP_SUB, 19308c2ecf20Sopenharmony_ci reg_b(insn->src_reg * 2)); 19318c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_both(insn->dst_reg * 2 + 1), 19328c2ecf20Sopenharmony_ci reg_a(insn->dst_reg * 2 + 1), ALU_OP_SUB_C, 19338c2ecf20Sopenharmony_ci reg_b(insn->src_reg * 2 + 1)); 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci return 0; 19368c2ecf20Sopenharmony_ci} 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_cistatic int sub_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 19398c2ecf20Sopenharmony_ci{ 19408c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 19418c2ecf20Sopenharmony_ci u64 imm = insn->imm; /* sign extend */ 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci wrp_alu_imm(nfp_prog, insn->dst_reg * 2, ALU_OP_SUB, imm & ~0U); 19448c2ecf20Sopenharmony_ci wrp_alu_imm(nfp_prog, insn->dst_reg * 2 + 1, ALU_OP_SUB_C, imm >> 32); 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci return 0; 19478c2ecf20Sopenharmony_ci} 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_cistatic int mul_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 19508c2ecf20Sopenharmony_ci{ 19518c2ecf20Sopenharmony_ci return wrp_mul(nfp_prog, meta, true, true); 19528c2ecf20Sopenharmony_ci} 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_cistatic int mul_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 19558c2ecf20Sopenharmony_ci{ 19568c2ecf20Sopenharmony_ci return wrp_mul(nfp_prog, meta, true, false); 19578c2ecf20Sopenharmony_ci} 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_cistatic int div_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 19608c2ecf20Sopenharmony_ci{ 19618c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci return wrp_div_imm(nfp_prog, insn->dst_reg * 2, insn->imm); 19648c2ecf20Sopenharmony_ci} 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_cistatic int div_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 19678c2ecf20Sopenharmony_ci{ 19688c2ecf20Sopenharmony_ci /* NOTE: verifier hook has rejected cases for which verifier doesn't 19698c2ecf20Sopenharmony_ci * know whether the source operand is constant or not. 19708c2ecf20Sopenharmony_ci */ 19718c2ecf20Sopenharmony_ci return wrp_div_imm(nfp_prog, meta->insn.dst_reg * 2, meta->umin_src); 19728c2ecf20Sopenharmony_ci} 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_cistatic int neg_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 19758c2ecf20Sopenharmony_ci{ 19768c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_both(insn->dst_reg * 2), reg_imm(0), 19798c2ecf20Sopenharmony_ci ALU_OP_SUB, reg_b(insn->dst_reg * 2)); 19808c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_both(insn->dst_reg * 2 + 1), reg_imm(0), 19818c2ecf20Sopenharmony_ci ALU_OP_SUB_C, reg_b(insn->dst_reg * 2 + 1)); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci return 0; 19848c2ecf20Sopenharmony_ci} 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci/* Pseudo code: 19878c2ecf20Sopenharmony_ci * if shift_amt >= 32 19888c2ecf20Sopenharmony_ci * dst_high = dst_low << shift_amt[4:0] 19898c2ecf20Sopenharmony_ci * dst_low = 0; 19908c2ecf20Sopenharmony_ci * else 19918c2ecf20Sopenharmony_ci * dst_high = (dst_high, dst_low) >> (32 - shift_amt) 19928c2ecf20Sopenharmony_ci * dst_low = dst_low << shift_amt 19938c2ecf20Sopenharmony_ci * 19948c2ecf20Sopenharmony_ci * The indirect shift will use the same logic at runtime. 19958c2ecf20Sopenharmony_ci */ 19968c2ecf20Sopenharmony_cistatic int __shl_imm64(struct nfp_prog *nfp_prog, u8 dst, u8 shift_amt) 19978c2ecf20Sopenharmony_ci{ 19988c2ecf20Sopenharmony_ci if (!shift_amt) 19998c2ecf20Sopenharmony_ci return 0; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci if (shift_amt < 32) { 20028c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst + 1), reg_a(dst + 1), 20038c2ecf20Sopenharmony_ci SHF_OP_NONE, reg_b(dst), SHF_SC_R_DSHF, 20048c2ecf20Sopenharmony_ci 32 - shift_amt); 20058c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE, 20068c2ecf20Sopenharmony_ci reg_b(dst), SHF_SC_L_SHF, shift_amt); 20078c2ecf20Sopenharmony_ci } else if (shift_amt == 32) { 20088c2ecf20Sopenharmony_ci wrp_reg_mov(nfp_prog, dst + 1, dst); 20098c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(dst), 0); 20108c2ecf20Sopenharmony_ci } else if (shift_amt > 32) { 20118c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_NONE, 20128c2ecf20Sopenharmony_ci reg_b(dst), SHF_SC_L_SHF, shift_amt - 32); 20138c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(dst), 0); 20148c2ecf20Sopenharmony_ci } 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci return 0; 20178c2ecf20Sopenharmony_ci} 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_cistatic int shl_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 20208c2ecf20Sopenharmony_ci{ 20218c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 20228c2ecf20Sopenharmony_ci u8 dst = insn->dst_reg * 2; 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci return __shl_imm64(nfp_prog, dst, insn->imm); 20258c2ecf20Sopenharmony_ci} 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_cistatic void shl_reg64_lt32_high(struct nfp_prog *nfp_prog, u8 dst, u8 src) 20288c2ecf20Sopenharmony_ci{ 20298c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imm_both(nfp_prog), reg_imm(32), ALU_OP_SUB, 20308c2ecf20Sopenharmony_ci reg_b(src)); 20318c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), imm_a(nfp_prog), ALU_OP_OR, reg_imm(0)); 20328c2ecf20Sopenharmony_ci emit_shf_indir(nfp_prog, reg_both(dst + 1), reg_a(dst + 1), SHF_OP_NONE, 20338c2ecf20Sopenharmony_ci reg_b(dst), SHF_SC_R_DSHF); 20348c2ecf20Sopenharmony_ci} 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci/* NOTE: for indirect left shift, HIGH part should be calculated first. */ 20378c2ecf20Sopenharmony_cistatic void shl_reg64_lt32_low(struct nfp_prog *nfp_prog, u8 dst, u8 src) 20388c2ecf20Sopenharmony_ci{ 20398c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_imm(0)); 20408c2ecf20Sopenharmony_ci emit_shf_indir(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE, 20418c2ecf20Sopenharmony_ci reg_b(dst), SHF_SC_L_SHF); 20428c2ecf20Sopenharmony_ci} 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_cistatic void shl_reg64_lt32(struct nfp_prog *nfp_prog, u8 dst, u8 src) 20458c2ecf20Sopenharmony_ci{ 20468c2ecf20Sopenharmony_ci shl_reg64_lt32_high(nfp_prog, dst, src); 20478c2ecf20Sopenharmony_ci shl_reg64_lt32_low(nfp_prog, dst, src); 20488c2ecf20Sopenharmony_ci} 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_cistatic void shl_reg64_ge32(struct nfp_prog *nfp_prog, u8 dst, u8 src) 20518c2ecf20Sopenharmony_ci{ 20528c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_imm(0)); 20538c2ecf20Sopenharmony_ci emit_shf_indir(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_NONE, 20548c2ecf20Sopenharmony_ci reg_b(dst), SHF_SC_L_SHF); 20558c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(dst), 0); 20568c2ecf20Sopenharmony_ci} 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_cistatic int shl_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 20598c2ecf20Sopenharmony_ci{ 20608c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 20618c2ecf20Sopenharmony_ci u64 umin, umax; 20628c2ecf20Sopenharmony_ci u8 dst, src; 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci dst = insn->dst_reg * 2; 20658c2ecf20Sopenharmony_ci umin = meta->umin_src; 20668c2ecf20Sopenharmony_ci umax = meta->umax_src; 20678c2ecf20Sopenharmony_ci if (umin == umax) 20688c2ecf20Sopenharmony_ci return __shl_imm64(nfp_prog, dst, umin); 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci src = insn->src_reg * 2; 20718c2ecf20Sopenharmony_ci if (umax < 32) { 20728c2ecf20Sopenharmony_ci shl_reg64_lt32(nfp_prog, dst, src); 20738c2ecf20Sopenharmony_ci } else if (umin >= 32) { 20748c2ecf20Sopenharmony_ci shl_reg64_ge32(nfp_prog, dst, src); 20758c2ecf20Sopenharmony_ci } else { 20768c2ecf20Sopenharmony_ci /* Generate different instruction sequences depending on runtime 20778c2ecf20Sopenharmony_ci * value of shift amount. 20788c2ecf20Sopenharmony_ci */ 20798c2ecf20Sopenharmony_ci u16 label_ge32, label_end; 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci label_ge32 = nfp_prog_current_offset(nfp_prog) + 7; 20828c2ecf20Sopenharmony_ci emit_br_bset(nfp_prog, reg_a(src), 5, label_ge32, 0); 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci shl_reg64_lt32_high(nfp_prog, dst, src); 20858c2ecf20Sopenharmony_ci label_end = nfp_prog_current_offset(nfp_prog) + 6; 20868c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_UNC, label_end, 2); 20878c2ecf20Sopenharmony_ci /* shl_reg64_lt32_low packed in delay slot. */ 20888c2ecf20Sopenharmony_ci shl_reg64_lt32_low(nfp_prog, dst, src); 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, label_ge32)) 20918c2ecf20Sopenharmony_ci return -EINVAL; 20928c2ecf20Sopenharmony_ci shl_reg64_ge32(nfp_prog, dst, src); 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, label_end)) 20958c2ecf20Sopenharmony_ci return -EINVAL; 20968c2ecf20Sopenharmony_ci } 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci return 0; 20998c2ecf20Sopenharmony_ci} 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci/* Pseudo code: 21028c2ecf20Sopenharmony_ci * if shift_amt >= 32 21038c2ecf20Sopenharmony_ci * dst_high = 0; 21048c2ecf20Sopenharmony_ci * dst_low = dst_high >> shift_amt[4:0] 21058c2ecf20Sopenharmony_ci * else 21068c2ecf20Sopenharmony_ci * dst_high = dst_high >> shift_amt 21078c2ecf20Sopenharmony_ci * dst_low = (dst_high, dst_low) >> shift_amt 21088c2ecf20Sopenharmony_ci * 21098c2ecf20Sopenharmony_ci * The indirect shift will use the same logic at runtime. 21108c2ecf20Sopenharmony_ci */ 21118c2ecf20Sopenharmony_cistatic int __shr_imm64(struct nfp_prog *nfp_prog, u8 dst, u8 shift_amt) 21128c2ecf20Sopenharmony_ci{ 21138c2ecf20Sopenharmony_ci if (!shift_amt) 21148c2ecf20Sopenharmony_ci return 0; 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci if (shift_amt < 32) { 21178c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst), reg_a(dst + 1), SHF_OP_NONE, 21188c2ecf20Sopenharmony_ci reg_b(dst), SHF_SC_R_DSHF, shift_amt); 21198c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_NONE, 21208c2ecf20Sopenharmony_ci reg_b(dst + 1), SHF_SC_R_SHF, shift_amt); 21218c2ecf20Sopenharmony_ci } else if (shift_amt == 32) { 21228c2ecf20Sopenharmony_ci wrp_reg_mov(nfp_prog, dst, dst + 1); 21238c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(dst + 1), 0); 21248c2ecf20Sopenharmony_ci } else if (shift_amt > 32) { 21258c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE, 21268c2ecf20Sopenharmony_ci reg_b(dst + 1), SHF_SC_R_SHF, shift_amt - 32); 21278c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(dst + 1), 0); 21288c2ecf20Sopenharmony_ci } 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci return 0; 21318c2ecf20Sopenharmony_ci} 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_cistatic int shr_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 21348c2ecf20Sopenharmony_ci{ 21358c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 21368c2ecf20Sopenharmony_ci u8 dst = insn->dst_reg * 2; 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci return __shr_imm64(nfp_prog, dst, insn->imm); 21398c2ecf20Sopenharmony_ci} 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci/* NOTE: for indirect right shift, LOW part should be calculated first. */ 21428c2ecf20Sopenharmony_cistatic void shr_reg64_lt32_high(struct nfp_prog *nfp_prog, u8 dst, u8 src) 21438c2ecf20Sopenharmony_ci{ 21448c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_imm(0)); 21458c2ecf20Sopenharmony_ci emit_shf_indir(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_NONE, 21468c2ecf20Sopenharmony_ci reg_b(dst + 1), SHF_SC_R_SHF); 21478c2ecf20Sopenharmony_ci} 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_cistatic void shr_reg64_lt32_low(struct nfp_prog *nfp_prog, u8 dst, u8 src) 21508c2ecf20Sopenharmony_ci{ 21518c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_imm(0)); 21528c2ecf20Sopenharmony_ci emit_shf_indir(nfp_prog, reg_both(dst), reg_a(dst + 1), SHF_OP_NONE, 21538c2ecf20Sopenharmony_ci reg_b(dst), SHF_SC_R_DSHF); 21548c2ecf20Sopenharmony_ci} 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_cistatic void shr_reg64_lt32(struct nfp_prog *nfp_prog, u8 dst, u8 src) 21578c2ecf20Sopenharmony_ci{ 21588c2ecf20Sopenharmony_ci shr_reg64_lt32_low(nfp_prog, dst, src); 21598c2ecf20Sopenharmony_ci shr_reg64_lt32_high(nfp_prog, dst, src); 21608c2ecf20Sopenharmony_ci} 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_cistatic void shr_reg64_ge32(struct nfp_prog *nfp_prog, u8 dst, u8 src) 21638c2ecf20Sopenharmony_ci{ 21648c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_imm(0)); 21658c2ecf20Sopenharmony_ci emit_shf_indir(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE, 21668c2ecf20Sopenharmony_ci reg_b(dst + 1), SHF_SC_R_SHF); 21678c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(dst + 1), 0); 21688c2ecf20Sopenharmony_ci} 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_cistatic int shr_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 21718c2ecf20Sopenharmony_ci{ 21728c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 21738c2ecf20Sopenharmony_ci u64 umin, umax; 21748c2ecf20Sopenharmony_ci u8 dst, src; 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci dst = insn->dst_reg * 2; 21778c2ecf20Sopenharmony_ci umin = meta->umin_src; 21788c2ecf20Sopenharmony_ci umax = meta->umax_src; 21798c2ecf20Sopenharmony_ci if (umin == umax) 21808c2ecf20Sopenharmony_ci return __shr_imm64(nfp_prog, dst, umin); 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci src = insn->src_reg * 2; 21838c2ecf20Sopenharmony_ci if (umax < 32) { 21848c2ecf20Sopenharmony_ci shr_reg64_lt32(nfp_prog, dst, src); 21858c2ecf20Sopenharmony_ci } else if (umin >= 32) { 21868c2ecf20Sopenharmony_ci shr_reg64_ge32(nfp_prog, dst, src); 21878c2ecf20Sopenharmony_ci } else { 21888c2ecf20Sopenharmony_ci /* Generate different instruction sequences depending on runtime 21898c2ecf20Sopenharmony_ci * value of shift amount. 21908c2ecf20Sopenharmony_ci */ 21918c2ecf20Sopenharmony_ci u16 label_ge32, label_end; 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci label_ge32 = nfp_prog_current_offset(nfp_prog) + 6; 21948c2ecf20Sopenharmony_ci emit_br_bset(nfp_prog, reg_a(src), 5, label_ge32, 0); 21958c2ecf20Sopenharmony_ci shr_reg64_lt32_low(nfp_prog, dst, src); 21968c2ecf20Sopenharmony_ci label_end = nfp_prog_current_offset(nfp_prog) + 6; 21978c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_UNC, label_end, 2); 21988c2ecf20Sopenharmony_ci /* shr_reg64_lt32_high packed in delay slot. */ 21998c2ecf20Sopenharmony_ci shr_reg64_lt32_high(nfp_prog, dst, src); 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, label_ge32)) 22028c2ecf20Sopenharmony_ci return -EINVAL; 22038c2ecf20Sopenharmony_ci shr_reg64_ge32(nfp_prog, dst, src); 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, label_end)) 22068c2ecf20Sopenharmony_ci return -EINVAL; 22078c2ecf20Sopenharmony_ci } 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci return 0; 22108c2ecf20Sopenharmony_ci} 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci/* Code logic is the same as __shr_imm64 except ashr requires signedness bit 22138c2ecf20Sopenharmony_ci * told through PREV_ALU result. 22148c2ecf20Sopenharmony_ci */ 22158c2ecf20Sopenharmony_cistatic int __ashr_imm64(struct nfp_prog *nfp_prog, u8 dst, u8 shift_amt) 22168c2ecf20Sopenharmony_ci{ 22178c2ecf20Sopenharmony_ci if (!shift_amt) 22188c2ecf20Sopenharmony_ci return 0; 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci if (shift_amt < 32) { 22218c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst), reg_a(dst + 1), SHF_OP_NONE, 22228c2ecf20Sopenharmony_ci reg_b(dst), SHF_SC_R_DSHF, shift_amt); 22238c2ecf20Sopenharmony_ci /* Set signedness bit. */ 22248c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(dst + 1), ALU_OP_OR, 22258c2ecf20Sopenharmony_ci reg_imm(0)); 22268c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_ASHR, 22278c2ecf20Sopenharmony_ci reg_b(dst + 1), SHF_SC_R_SHF, shift_amt); 22288c2ecf20Sopenharmony_ci } else if (shift_amt == 32) { 22298c2ecf20Sopenharmony_ci /* NOTE: this also helps setting signedness bit. */ 22308c2ecf20Sopenharmony_ci wrp_reg_mov(nfp_prog, dst, dst + 1); 22318c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_ASHR, 22328c2ecf20Sopenharmony_ci reg_b(dst + 1), SHF_SC_R_SHF, 31); 22338c2ecf20Sopenharmony_ci } else if (shift_amt > 32) { 22348c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(dst + 1), ALU_OP_OR, 22358c2ecf20Sopenharmony_ci reg_imm(0)); 22368c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst), reg_none(), SHF_OP_ASHR, 22378c2ecf20Sopenharmony_ci reg_b(dst + 1), SHF_SC_R_SHF, shift_amt - 32); 22388c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_ASHR, 22398c2ecf20Sopenharmony_ci reg_b(dst + 1), SHF_SC_R_SHF, 31); 22408c2ecf20Sopenharmony_ci } 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci return 0; 22438c2ecf20Sopenharmony_ci} 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_cistatic int ashr_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 22468c2ecf20Sopenharmony_ci{ 22478c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 22488c2ecf20Sopenharmony_ci u8 dst = insn->dst_reg * 2; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci return __ashr_imm64(nfp_prog, dst, insn->imm); 22518c2ecf20Sopenharmony_ci} 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_cistatic void ashr_reg64_lt32_high(struct nfp_prog *nfp_prog, u8 dst, u8 src) 22548c2ecf20Sopenharmony_ci{ 22558c2ecf20Sopenharmony_ci /* NOTE: the first insn will set both indirect shift amount (source A) 22568c2ecf20Sopenharmony_ci * and signedness bit (MSB of result). 22578c2ecf20Sopenharmony_ci */ 22588c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_b(dst + 1)); 22598c2ecf20Sopenharmony_ci emit_shf_indir(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_ASHR, 22608c2ecf20Sopenharmony_ci reg_b(dst + 1), SHF_SC_R_SHF); 22618c2ecf20Sopenharmony_ci} 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_cistatic void ashr_reg64_lt32_low(struct nfp_prog *nfp_prog, u8 dst, u8 src) 22648c2ecf20Sopenharmony_ci{ 22658c2ecf20Sopenharmony_ci /* NOTE: it is the same as logic shift because we don't need to shift in 22668c2ecf20Sopenharmony_ci * signedness bit when the shift amount is less than 32. 22678c2ecf20Sopenharmony_ci */ 22688c2ecf20Sopenharmony_ci return shr_reg64_lt32_low(nfp_prog, dst, src); 22698c2ecf20Sopenharmony_ci} 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_cistatic void ashr_reg64_lt32(struct nfp_prog *nfp_prog, u8 dst, u8 src) 22728c2ecf20Sopenharmony_ci{ 22738c2ecf20Sopenharmony_ci ashr_reg64_lt32_low(nfp_prog, dst, src); 22748c2ecf20Sopenharmony_ci ashr_reg64_lt32_high(nfp_prog, dst, src); 22758c2ecf20Sopenharmony_ci} 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_cistatic void ashr_reg64_ge32(struct nfp_prog *nfp_prog, u8 dst, u8 src) 22788c2ecf20Sopenharmony_ci{ 22798c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_b(dst + 1)); 22808c2ecf20Sopenharmony_ci emit_shf_indir(nfp_prog, reg_both(dst), reg_none(), SHF_OP_ASHR, 22818c2ecf20Sopenharmony_ci reg_b(dst + 1), SHF_SC_R_SHF); 22828c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_ASHR, 22838c2ecf20Sopenharmony_ci reg_b(dst + 1), SHF_SC_R_SHF, 31); 22848c2ecf20Sopenharmony_ci} 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci/* Like ashr_imm64, but need to use indirect shift. */ 22878c2ecf20Sopenharmony_cistatic int ashr_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 22888c2ecf20Sopenharmony_ci{ 22898c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 22908c2ecf20Sopenharmony_ci u64 umin, umax; 22918c2ecf20Sopenharmony_ci u8 dst, src; 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci dst = insn->dst_reg * 2; 22948c2ecf20Sopenharmony_ci umin = meta->umin_src; 22958c2ecf20Sopenharmony_ci umax = meta->umax_src; 22968c2ecf20Sopenharmony_ci if (umin == umax) 22978c2ecf20Sopenharmony_ci return __ashr_imm64(nfp_prog, dst, umin); 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci src = insn->src_reg * 2; 23008c2ecf20Sopenharmony_ci if (umax < 32) { 23018c2ecf20Sopenharmony_ci ashr_reg64_lt32(nfp_prog, dst, src); 23028c2ecf20Sopenharmony_ci } else if (umin >= 32) { 23038c2ecf20Sopenharmony_ci ashr_reg64_ge32(nfp_prog, dst, src); 23048c2ecf20Sopenharmony_ci } else { 23058c2ecf20Sopenharmony_ci u16 label_ge32, label_end; 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci label_ge32 = nfp_prog_current_offset(nfp_prog) + 6; 23088c2ecf20Sopenharmony_ci emit_br_bset(nfp_prog, reg_a(src), 5, label_ge32, 0); 23098c2ecf20Sopenharmony_ci ashr_reg64_lt32_low(nfp_prog, dst, src); 23108c2ecf20Sopenharmony_ci label_end = nfp_prog_current_offset(nfp_prog) + 6; 23118c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_UNC, label_end, 2); 23128c2ecf20Sopenharmony_ci /* ashr_reg64_lt32_high packed in delay slot. */ 23138c2ecf20Sopenharmony_ci ashr_reg64_lt32_high(nfp_prog, dst, src); 23148c2ecf20Sopenharmony_ci 23158c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, label_ge32)) 23168c2ecf20Sopenharmony_ci return -EINVAL; 23178c2ecf20Sopenharmony_ci ashr_reg64_ge32(nfp_prog, dst, src); 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, label_end)) 23208c2ecf20Sopenharmony_ci return -EINVAL; 23218c2ecf20Sopenharmony_ci } 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci return 0; 23248c2ecf20Sopenharmony_ci} 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_cistatic int mov_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 23278c2ecf20Sopenharmony_ci{ 23288c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci wrp_reg_mov(nfp_prog, insn->dst_reg * 2, insn->src_reg * 2); 23318c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(insn->dst_reg * 2 + 1), 0); 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci return 0; 23348c2ecf20Sopenharmony_ci} 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_cistatic int mov_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 23378c2ecf20Sopenharmony_ci{ 23388c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(insn->dst_reg * 2), insn->imm); 23418c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(insn->dst_reg * 2 + 1), 0); 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_ci return 0; 23448c2ecf20Sopenharmony_ci} 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_cistatic int xor_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 23478c2ecf20Sopenharmony_ci{ 23488c2ecf20Sopenharmony_ci return wrp_alu32_reg(nfp_prog, meta, ALU_OP_XOR); 23498c2ecf20Sopenharmony_ci} 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_cistatic int xor_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 23528c2ecf20Sopenharmony_ci{ 23538c2ecf20Sopenharmony_ci return wrp_alu32_imm(nfp_prog, meta, ALU_OP_XOR); 23548c2ecf20Sopenharmony_ci} 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_cistatic int and_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 23578c2ecf20Sopenharmony_ci{ 23588c2ecf20Sopenharmony_ci return wrp_alu32_reg(nfp_prog, meta, ALU_OP_AND); 23598c2ecf20Sopenharmony_ci} 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_cistatic int and_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 23628c2ecf20Sopenharmony_ci{ 23638c2ecf20Sopenharmony_ci return wrp_alu32_imm(nfp_prog, meta, ALU_OP_AND); 23648c2ecf20Sopenharmony_ci} 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_cistatic int or_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 23678c2ecf20Sopenharmony_ci{ 23688c2ecf20Sopenharmony_ci return wrp_alu32_reg(nfp_prog, meta, ALU_OP_OR); 23698c2ecf20Sopenharmony_ci} 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_cistatic int or_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 23728c2ecf20Sopenharmony_ci{ 23738c2ecf20Sopenharmony_ci return wrp_alu32_imm(nfp_prog, meta, ALU_OP_OR); 23748c2ecf20Sopenharmony_ci} 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_cistatic int add_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 23778c2ecf20Sopenharmony_ci{ 23788c2ecf20Sopenharmony_ci return wrp_alu32_reg(nfp_prog, meta, ALU_OP_ADD); 23798c2ecf20Sopenharmony_ci} 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_cistatic int add_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 23828c2ecf20Sopenharmony_ci{ 23838c2ecf20Sopenharmony_ci return wrp_alu32_imm(nfp_prog, meta, ALU_OP_ADD); 23848c2ecf20Sopenharmony_ci} 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_cistatic int sub_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 23878c2ecf20Sopenharmony_ci{ 23888c2ecf20Sopenharmony_ci return wrp_alu32_reg(nfp_prog, meta, ALU_OP_SUB); 23898c2ecf20Sopenharmony_ci} 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_cistatic int sub_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 23928c2ecf20Sopenharmony_ci{ 23938c2ecf20Sopenharmony_ci return wrp_alu32_imm(nfp_prog, meta, ALU_OP_SUB); 23948c2ecf20Sopenharmony_ci} 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_cistatic int mul_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 23978c2ecf20Sopenharmony_ci{ 23988c2ecf20Sopenharmony_ci return wrp_mul(nfp_prog, meta, false, true); 23998c2ecf20Sopenharmony_ci} 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_cistatic int mul_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 24028c2ecf20Sopenharmony_ci{ 24038c2ecf20Sopenharmony_ci return wrp_mul(nfp_prog, meta, false, false); 24048c2ecf20Sopenharmony_ci} 24058c2ecf20Sopenharmony_ci 24068c2ecf20Sopenharmony_cistatic int div_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 24078c2ecf20Sopenharmony_ci{ 24088c2ecf20Sopenharmony_ci return div_reg64(nfp_prog, meta); 24098c2ecf20Sopenharmony_ci} 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_cistatic int div_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 24128c2ecf20Sopenharmony_ci{ 24138c2ecf20Sopenharmony_ci return div_imm64(nfp_prog, meta); 24148c2ecf20Sopenharmony_ci} 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_cistatic int neg_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 24178c2ecf20Sopenharmony_ci{ 24188c2ecf20Sopenharmony_ci u8 dst = meta->insn.dst_reg * 2; 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_both(dst), reg_imm(0), ALU_OP_SUB, reg_b(dst)); 24218c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst); 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci return 0; 24248c2ecf20Sopenharmony_ci} 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_cistatic int 24278c2ecf20Sopenharmony_ci__ashr_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, u8 dst, 24288c2ecf20Sopenharmony_ci u8 shift_amt) 24298c2ecf20Sopenharmony_ci{ 24308c2ecf20Sopenharmony_ci if (shift_amt) { 24318c2ecf20Sopenharmony_ci /* Set signedness bit (MSB of result). */ 24328c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(dst), ALU_OP_OR, 24338c2ecf20Sopenharmony_ci reg_imm(0)); 24348c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst), reg_none(), SHF_OP_ASHR, 24358c2ecf20Sopenharmony_ci reg_b(dst), SHF_SC_R_SHF, shift_amt); 24368c2ecf20Sopenharmony_ci } 24378c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst); 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci return 0; 24408c2ecf20Sopenharmony_ci} 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_cistatic int ashr_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 24438c2ecf20Sopenharmony_ci{ 24448c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 24458c2ecf20Sopenharmony_ci u64 umin, umax; 24468c2ecf20Sopenharmony_ci u8 dst, src; 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_ci dst = insn->dst_reg * 2; 24498c2ecf20Sopenharmony_ci umin = meta->umin_src; 24508c2ecf20Sopenharmony_ci umax = meta->umax_src; 24518c2ecf20Sopenharmony_ci if (umin == umax) 24528c2ecf20Sopenharmony_ci return __ashr_imm(nfp_prog, meta, dst, umin); 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci src = insn->src_reg * 2; 24558c2ecf20Sopenharmony_ci /* NOTE: the first insn will set both indirect shift amount (source A) 24568c2ecf20Sopenharmony_ci * and signedness bit (MSB of result). 24578c2ecf20Sopenharmony_ci */ 24588c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_b(dst)); 24598c2ecf20Sopenharmony_ci emit_shf_indir(nfp_prog, reg_both(dst), reg_none(), SHF_OP_ASHR, 24608c2ecf20Sopenharmony_ci reg_b(dst), SHF_SC_R_SHF); 24618c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst); 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_ci return 0; 24648c2ecf20Sopenharmony_ci} 24658c2ecf20Sopenharmony_ci 24668c2ecf20Sopenharmony_cistatic int ashr_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 24678c2ecf20Sopenharmony_ci{ 24688c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 24698c2ecf20Sopenharmony_ci u8 dst = insn->dst_reg * 2; 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci return __ashr_imm(nfp_prog, meta, dst, insn->imm); 24728c2ecf20Sopenharmony_ci} 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_cistatic int 24758c2ecf20Sopenharmony_ci__shr_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, u8 dst, 24768c2ecf20Sopenharmony_ci u8 shift_amt) 24778c2ecf20Sopenharmony_ci{ 24788c2ecf20Sopenharmony_ci if (shift_amt) 24798c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE, 24808c2ecf20Sopenharmony_ci reg_b(dst), SHF_SC_R_SHF, shift_amt); 24818c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst); 24828c2ecf20Sopenharmony_ci return 0; 24838c2ecf20Sopenharmony_ci} 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_cistatic int shr_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 24868c2ecf20Sopenharmony_ci{ 24878c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 24888c2ecf20Sopenharmony_ci u8 dst = insn->dst_reg * 2; 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci return __shr_imm(nfp_prog, meta, dst, insn->imm); 24918c2ecf20Sopenharmony_ci} 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_cistatic int shr_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 24948c2ecf20Sopenharmony_ci{ 24958c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 24968c2ecf20Sopenharmony_ci u64 umin, umax; 24978c2ecf20Sopenharmony_ci u8 dst, src; 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci dst = insn->dst_reg * 2; 25008c2ecf20Sopenharmony_ci umin = meta->umin_src; 25018c2ecf20Sopenharmony_ci umax = meta->umax_src; 25028c2ecf20Sopenharmony_ci if (umin == umax) 25038c2ecf20Sopenharmony_ci return __shr_imm(nfp_prog, meta, dst, umin); 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci src = insn->src_reg * 2; 25068c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_imm(0)); 25078c2ecf20Sopenharmony_ci emit_shf_indir(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE, 25088c2ecf20Sopenharmony_ci reg_b(dst), SHF_SC_R_SHF); 25098c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst); 25108c2ecf20Sopenharmony_ci return 0; 25118c2ecf20Sopenharmony_ci} 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_cistatic int 25148c2ecf20Sopenharmony_ci__shl_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, u8 dst, 25158c2ecf20Sopenharmony_ci u8 shift_amt) 25168c2ecf20Sopenharmony_ci{ 25178c2ecf20Sopenharmony_ci if (shift_amt) 25188c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE, 25198c2ecf20Sopenharmony_ci reg_b(dst), SHF_SC_L_SHF, shift_amt); 25208c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst); 25218c2ecf20Sopenharmony_ci return 0; 25228c2ecf20Sopenharmony_ci} 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_cistatic int shl_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 25258c2ecf20Sopenharmony_ci{ 25268c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 25278c2ecf20Sopenharmony_ci u8 dst = insn->dst_reg * 2; 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci return __shl_imm(nfp_prog, meta, dst, insn->imm); 25308c2ecf20Sopenharmony_ci} 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_cistatic int shl_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 25338c2ecf20Sopenharmony_ci{ 25348c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 25358c2ecf20Sopenharmony_ci u64 umin, umax; 25368c2ecf20Sopenharmony_ci u8 dst, src; 25378c2ecf20Sopenharmony_ci 25388c2ecf20Sopenharmony_ci dst = insn->dst_reg * 2; 25398c2ecf20Sopenharmony_ci umin = meta->umin_src; 25408c2ecf20Sopenharmony_ci umax = meta->umax_src; 25418c2ecf20Sopenharmony_ci if (umin == umax) 25428c2ecf20Sopenharmony_ci return __shl_imm(nfp_prog, meta, dst, umin); 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci src = insn->src_reg * 2; 25458c2ecf20Sopenharmony_ci shl_reg64_lt32_low(nfp_prog, dst, src); 25468c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst); 25478c2ecf20Sopenharmony_ci return 0; 25488c2ecf20Sopenharmony_ci} 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_cistatic int end_reg32(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 25518c2ecf20Sopenharmony_ci{ 25528c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 25538c2ecf20Sopenharmony_ci u8 gpr = insn->dst_reg * 2; 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci switch (insn->imm) { 25568c2ecf20Sopenharmony_ci case 16: 25578c2ecf20Sopenharmony_ci emit_ld_field(nfp_prog, reg_both(gpr), 0x9, reg_b(gpr), 25588c2ecf20Sopenharmony_ci SHF_SC_R_ROT, 8); 25598c2ecf20Sopenharmony_ci emit_ld_field(nfp_prog, reg_both(gpr), 0xe, reg_a(gpr), 25608c2ecf20Sopenharmony_ci SHF_SC_R_SHF, 16); 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(gpr + 1), 0); 25638c2ecf20Sopenharmony_ci break; 25648c2ecf20Sopenharmony_ci case 32: 25658c2ecf20Sopenharmony_ci wrp_end32(nfp_prog, reg_a(gpr), gpr); 25668c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(gpr + 1), 0); 25678c2ecf20Sopenharmony_ci break; 25688c2ecf20Sopenharmony_ci case 64: 25698c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, imm_a(nfp_prog), reg_b(gpr + 1)); 25708c2ecf20Sopenharmony_ci 25718c2ecf20Sopenharmony_ci wrp_end32(nfp_prog, reg_a(gpr), gpr + 1); 25728c2ecf20Sopenharmony_ci wrp_end32(nfp_prog, imm_a(nfp_prog), gpr); 25738c2ecf20Sopenharmony_ci break; 25748c2ecf20Sopenharmony_ci } 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_ci return 0; 25778c2ecf20Sopenharmony_ci} 25788c2ecf20Sopenharmony_ci 25798c2ecf20Sopenharmony_cistatic int imm_ld8_part2(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 25808c2ecf20Sopenharmony_ci{ 25818c2ecf20Sopenharmony_ci struct nfp_insn_meta *prev = nfp_meta_prev(meta); 25828c2ecf20Sopenharmony_ci u32 imm_lo, imm_hi; 25838c2ecf20Sopenharmony_ci u8 dst; 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_ci dst = prev->insn.dst_reg * 2; 25868c2ecf20Sopenharmony_ci imm_lo = prev->insn.imm; 25878c2ecf20Sopenharmony_ci imm_hi = meta->insn.imm; 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(dst), imm_lo); 25908c2ecf20Sopenharmony_ci 25918c2ecf20Sopenharmony_ci /* mov is always 1 insn, load imm may be two, so try to use mov */ 25928c2ecf20Sopenharmony_ci if (imm_hi == imm_lo) 25938c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_both(dst + 1), reg_a(dst)); 25948c2ecf20Sopenharmony_ci else 25958c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(dst + 1), imm_hi); 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci return 0; 25988c2ecf20Sopenharmony_ci} 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_cistatic int imm_ld8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 26018c2ecf20Sopenharmony_ci{ 26028c2ecf20Sopenharmony_ci meta->double_cb = imm_ld8_part2; 26038c2ecf20Sopenharmony_ci return 0; 26048c2ecf20Sopenharmony_ci} 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_cistatic int data_ld1(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 26078c2ecf20Sopenharmony_ci{ 26088c2ecf20Sopenharmony_ci return construct_data_ld(nfp_prog, meta, meta->insn.imm, 1); 26098c2ecf20Sopenharmony_ci} 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_cistatic int data_ld2(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 26128c2ecf20Sopenharmony_ci{ 26138c2ecf20Sopenharmony_ci return construct_data_ld(nfp_prog, meta, meta->insn.imm, 2); 26148c2ecf20Sopenharmony_ci} 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_cistatic int data_ld4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 26178c2ecf20Sopenharmony_ci{ 26188c2ecf20Sopenharmony_ci return construct_data_ld(nfp_prog, meta, meta->insn.imm, 4); 26198c2ecf20Sopenharmony_ci} 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_cistatic int data_ind_ld1(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 26228c2ecf20Sopenharmony_ci{ 26238c2ecf20Sopenharmony_ci return construct_data_ind_ld(nfp_prog, meta, meta->insn.imm, 26248c2ecf20Sopenharmony_ci meta->insn.src_reg * 2, 1); 26258c2ecf20Sopenharmony_ci} 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_cistatic int data_ind_ld2(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 26288c2ecf20Sopenharmony_ci{ 26298c2ecf20Sopenharmony_ci return construct_data_ind_ld(nfp_prog, meta, meta->insn.imm, 26308c2ecf20Sopenharmony_ci meta->insn.src_reg * 2, 2); 26318c2ecf20Sopenharmony_ci} 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_cistatic int data_ind_ld4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 26348c2ecf20Sopenharmony_ci{ 26358c2ecf20Sopenharmony_ci return construct_data_ind_ld(nfp_prog, meta, meta->insn.imm, 26368c2ecf20Sopenharmony_ci meta->insn.src_reg * 2, 4); 26378c2ecf20Sopenharmony_ci} 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_cistatic int 26408c2ecf20Sopenharmony_cimem_ldx_stack(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 26418c2ecf20Sopenharmony_ci unsigned int size, unsigned int ptr_off) 26428c2ecf20Sopenharmony_ci{ 26438c2ecf20Sopenharmony_ci return mem_op_stack(nfp_prog, meta, size, ptr_off, 26448c2ecf20Sopenharmony_ci meta->insn.dst_reg * 2, meta->insn.src_reg * 2, 26458c2ecf20Sopenharmony_ci true, wrp_lmem_load); 26468c2ecf20Sopenharmony_ci} 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_cistatic int mem_ldx_skb(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 26498c2ecf20Sopenharmony_ci u8 size) 26508c2ecf20Sopenharmony_ci{ 26518c2ecf20Sopenharmony_ci swreg dst = reg_both(meta->insn.dst_reg * 2); 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_ci switch (meta->insn.off) { 26548c2ecf20Sopenharmony_ci case offsetof(struct __sk_buff, len): 26558c2ecf20Sopenharmony_ci if (size != sizeof_field(struct __sk_buff, len)) 26568c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 26578c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, dst, plen_reg(nfp_prog)); 26588c2ecf20Sopenharmony_ci break; 26598c2ecf20Sopenharmony_ci case offsetof(struct __sk_buff, data): 26608c2ecf20Sopenharmony_ci if (size != sizeof_field(struct __sk_buff, data)) 26618c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 26628c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, dst, pptr_reg(nfp_prog)); 26638c2ecf20Sopenharmony_ci break; 26648c2ecf20Sopenharmony_ci case offsetof(struct __sk_buff, data_end): 26658c2ecf20Sopenharmony_ci if (size != sizeof_field(struct __sk_buff, data_end)) 26668c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 26678c2ecf20Sopenharmony_ci emit_alu(nfp_prog, dst, 26688c2ecf20Sopenharmony_ci plen_reg(nfp_prog), ALU_OP_ADD, pptr_reg(nfp_prog)); 26698c2ecf20Sopenharmony_ci break; 26708c2ecf20Sopenharmony_ci default: 26718c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 26728c2ecf20Sopenharmony_ci } 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(meta->insn.dst_reg * 2 + 1), 0); 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci return 0; 26778c2ecf20Sopenharmony_ci} 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_cistatic int mem_ldx_xdp(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 26808c2ecf20Sopenharmony_ci u8 size) 26818c2ecf20Sopenharmony_ci{ 26828c2ecf20Sopenharmony_ci swreg dst = reg_both(meta->insn.dst_reg * 2); 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_ci switch (meta->insn.off) { 26858c2ecf20Sopenharmony_ci case offsetof(struct xdp_md, data): 26868c2ecf20Sopenharmony_ci if (size != sizeof_field(struct xdp_md, data)) 26878c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 26888c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, dst, pptr_reg(nfp_prog)); 26898c2ecf20Sopenharmony_ci break; 26908c2ecf20Sopenharmony_ci case offsetof(struct xdp_md, data_end): 26918c2ecf20Sopenharmony_ci if (size != sizeof_field(struct xdp_md, data_end)) 26928c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 26938c2ecf20Sopenharmony_ci emit_alu(nfp_prog, dst, 26948c2ecf20Sopenharmony_ci plen_reg(nfp_prog), ALU_OP_ADD, pptr_reg(nfp_prog)); 26958c2ecf20Sopenharmony_ci break; 26968c2ecf20Sopenharmony_ci default: 26978c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 26988c2ecf20Sopenharmony_ci } 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_both(meta->insn.dst_reg * 2 + 1), 0); 27018c2ecf20Sopenharmony_ci 27028c2ecf20Sopenharmony_ci return 0; 27038c2ecf20Sopenharmony_ci} 27048c2ecf20Sopenharmony_ci 27058c2ecf20Sopenharmony_cistatic int 27068c2ecf20Sopenharmony_cimem_ldx_data(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 27078c2ecf20Sopenharmony_ci unsigned int size) 27088c2ecf20Sopenharmony_ci{ 27098c2ecf20Sopenharmony_ci swreg tmp_reg; 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci tmp_reg = re_load_imm_any(nfp_prog, meta->insn.off, imm_b(nfp_prog)); 27128c2ecf20Sopenharmony_ci 27138c2ecf20Sopenharmony_ci return data_ld_host_order_addr32(nfp_prog, meta, meta->insn.src_reg * 2, 27148c2ecf20Sopenharmony_ci tmp_reg, meta->insn.dst_reg * 2, size); 27158c2ecf20Sopenharmony_ci} 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_cistatic int 27188c2ecf20Sopenharmony_cimem_ldx_emem(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 27198c2ecf20Sopenharmony_ci unsigned int size) 27208c2ecf20Sopenharmony_ci{ 27218c2ecf20Sopenharmony_ci swreg tmp_reg; 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci tmp_reg = re_load_imm_any(nfp_prog, meta->insn.off, imm_b(nfp_prog)); 27248c2ecf20Sopenharmony_ci 27258c2ecf20Sopenharmony_ci return data_ld_host_order_addr40(nfp_prog, meta, meta->insn.src_reg * 2, 27268c2ecf20Sopenharmony_ci tmp_reg, meta->insn.dst_reg * 2, size); 27278c2ecf20Sopenharmony_ci} 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_cistatic void 27308c2ecf20Sopenharmony_cimem_ldx_data_init_pktcache(struct nfp_prog *nfp_prog, 27318c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta) 27328c2ecf20Sopenharmony_ci{ 27338c2ecf20Sopenharmony_ci s16 range_start = meta->pkt_cache.range_start; 27348c2ecf20Sopenharmony_ci s16 range_end = meta->pkt_cache.range_end; 27358c2ecf20Sopenharmony_ci swreg src_base, off; 27368c2ecf20Sopenharmony_ci u8 xfer_num, len; 27378c2ecf20Sopenharmony_ci bool indir; 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci off = re_load_imm_any(nfp_prog, range_start, imm_b(nfp_prog)); 27408c2ecf20Sopenharmony_ci src_base = reg_a(meta->insn.src_reg * 2); 27418c2ecf20Sopenharmony_ci len = range_end - range_start; 27428c2ecf20Sopenharmony_ci xfer_num = round_up(len, REG_WIDTH) / REG_WIDTH; 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_ci indir = len > 8 * REG_WIDTH; 27458c2ecf20Sopenharmony_ci /* Setup PREV_ALU for indirect mode. */ 27468c2ecf20Sopenharmony_ci if (indir) 27478c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_none(), 27488c2ecf20Sopenharmony_ci CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, xfer_num - 1)); 27498c2ecf20Sopenharmony_ci 27508c2ecf20Sopenharmony_ci /* Cache memory into transfer-in registers. */ 27518c2ecf20Sopenharmony_ci emit_cmd_any(nfp_prog, CMD_TGT_READ32_SWAP, CMD_MODE_32b, 0, src_base, 27528c2ecf20Sopenharmony_ci off, xfer_num - 1, CMD_CTX_SWAP, indir); 27538c2ecf20Sopenharmony_ci} 27548c2ecf20Sopenharmony_ci 27558c2ecf20Sopenharmony_cistatic int 27568c2ecf20Sopenharmony_cimem_ldx_data_from_pktcache_unaligned(struct nfp_prog *nfp_prog, 27578c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta, 27588c2ecf20Sopenharmony_ci unsigned int size) 27598c2ecf20Sopenharmony_ci{ 27608c2ecf20Sopenharmony_ci s16 range_start = meta->pkt_cache.range_start; 27618c2ecf20Sopenharmony_ci s16 insn_off = meta->insn.off - range_start; 27628c2ecf20Sopenharmony_ci swreg dst_lo, dst_hi, src_lo, src_mid; 27638c2ecf20Sopenharmony_ci u8 dst_gpr = meta->insn.dst_reg * 2; 27648c2ecf20Sopenharmony_ci u8 len_lo = size, len_mid = 0; 27658c2ecf20Sopenharmony_ci u8 idx = insn_off / REG_WIDTH; 27668c2ecf20Sopenharmony_ci u8 off = insn_off % REG_WIDTH; 27678c2ecf20Sopenharmony_ci 27688c2ecf20Sopenharmony_ci dst_hi = reg_both(dst_gpr + 1); 27698c2ecf20Sopenharmony_ci dst_lo = reg_both(dst_gpr); 27708c2ecf20Sopenharmony_ci src_lo = reg_xfer(idx); 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci /* The read length could involve as many as three registers. */ 27738c2ecf20Sopenharmony_ci if (size > REG_WIDTH - off) { 27748c2ecf20Sopenharmony_ci /* Calculate the part in the second register. */ 27758c2ecf20Sopenharmony_ci len_lo = REG_WIDTH - off; 27768c2ecf20Sopenharmony_ci len_mid = size - len_lo; 27778c2ecf20Sopenharmony_ci 27788c2ecf20Sopenharmony_ci /* Calculate the part in the third register. */ 27798c2ecf20Sopenharmony_ci if (size > 2 * REG_WIDTH - off) 27808c2ecf20Sopenharmony_ci len_mid = REG_WIDTH; 27818c2ecf20Sopenharmony_ci } 27828c2ecf20Sopenharmony_ci 27838c2ecf20Sopenharmony_ci wrp_reg_subpart(nfp_prog, dst_lo, src_lo, len_lo, off); 27848c2ecf20Sopenharmony_ci 27858c2ecf20Sopenharmony_ci if (!len_mid) { 27868c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst_gpr); 27878c2ecf20Sopenharmony_ci return 0; 27888c2ecf20Sopenharmony_ci } 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci src_mid = reg_xfer(idx + 1); 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_ci if (size <= REG_WIDTH) { 27938c2ecf20Sopenharmony_ci wrp_reg_or_subpart(nfp_prog, dst_lo, src_mid, len_mid, len_lo); 27948c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst_gpr); 27958c2ecf20Sopenharmony_ci } else { 27968c2ecf20Sopenharmony_ci swreg src_hi = reg_xfer(idx + 2); 27978c2ecf20Sopenharmony_ci 27988c2ecf20Sopenharmony_ci wrp_reg_or_subpart(nfp_prog, dst_lo, src_mid, 27998c2ecf20Sopenharmony_ci REG_WIDTH - len_lo, len_lo); 28008c2ecf20Sopenharmony_ci wrp_reg_subpart(nfp_prog, dst_hi, src_mid, len_lo, 28018c2ecf20Sopenharmony_ci REG_WIDTH - len_lo); 28028c2ecf20Sopenharmony_ci wrp_reg_or_subpart(nfp_prog, dst_hi, src_hi, REG_WIDTH - len_lo, 28038c2ecf20Sopenharmony_ci len_lo); 28048c2ecf20Sopenharmony_ci } 28058c2ecf20Sopenharmony_ci 28068c2ecf20Sopenharmony_ci return 0; 28078c2ecf20Sopenharmony_ci} 28088c2ecf20Sopenharmony_ci 28098c2ecf20Sopenharmony_cistatic int 28108c2ecf20Sopenharmony_cimem_ldx_data_from_pktcache_aligned(struct nfp_prog *nfp_prog, 28118c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta, 28128c2ecf20Sopenharmony_ci unsigned int size) 28138c2ecf20Sopenharmony_ci{ 28148c2ecf20Sopenharmony_ci swreg dst_lo, dst_hi, src_lo; 28158c2ecf20Sopenharmony_ci u8 dst_gpr, idx; 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ci idx = (meta->insn.off - meta->pkt_cache.range_start) / REG_WIDTH; 28188c2ecf20Sopenharmony_ci dst_gpr = meta->insn.dst_reg * 2; 28198c2ecf20Sopenharmony_ci dst_hi = reg_both(dst_gpr + 1); 28208c2ecf20Sopenharmony_ci dst_lo = reg_both(dst_gpr); 28218c2ecf20Sopenharmony_ci src_lo = reg_xfer(idx); 28228c2ecf20Sopenharmony_ci 28238c2ecf20Sopenharmony_ci if (size < REG_WIDTH) { 28248c2ecf20Sopenharmony_ci wrp_reg_subpart(nfp_prog, dst_lo, src_lo, size, 0); 28258c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst_gpr); 28268c2ecf20Sopenharmony_ci } else if (size == REG_WIDTH) { 28278c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, dst_lo, src_lo); 28288c2ecf20Sopenharmony_ci wrp_zext(nfp_prog, meta, dst_gpr); 28298c2ecf20Sopenharmony_ci } else { 28308c2ecf20Sopenharmony_ci swreg src_hi = reg_xfer(idx + 1); 28318c2ecf20Sopenharmony_ci 28328c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, dst_lo, src_lo); 28338c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, dst_hi, src_hi); 28348c2ecf20Sopenharmony_ci } 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci return 0; 28378c2ecf20Sopenharmony_ci} 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_cistatic int 28408c2ecf20Sopenharmony_cimem_ldx_data_from_pktcache(struct nfp_prog *nfp_prog, 28418c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta, unsigned int size) 28428c2ecf20Sopenharmony_ci{ 28438c2ecf20Sopenharmony_ci u8 off = meta->insn.off - meta->pkt_cache.range_start; 28448c2ecf20Sopenharmony_ci 28458c2ecf20Sopenharmony_ci if (IS_ALIGNED(off, REG_WIDTH)) 28468c2ecf20Sopenharmony_ci return mem_ldx_data_from_pktcache_aligned(nfp_prog, meta, size); 28478c2ecf20Sopenharmony_ci 28488c2ecf20Sopenharmony_ci return mem_ldx_data_from_pktcache_unaligned(nfp_prog, meta, size); 28498c2ecf20Sopenharmony_ci} 28508c2ecf20Sopenharmony_ci 28518c2ecf20Sopenharmony_cistatic int 28528c2ecf20Sopenharmony_cimem_ldx(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 28538c2ecf20Sopenharmony_ci unsigned int size) 28548c2ecf20Sopenharmony_ci{ 28558c2ecf20Sopenharmony_ci if (meta->ldst_gather_len) 28568c2ecf20Sopenharmony_ci return nfp_cpp_memcpy(nfp_prog, meta); 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci if (meta->ptr.type == PTR_TO_CTX) { 28598c2ecf20Sopenharmony_ci if (nfp_prog->type == BPF_PROG_TYPE_XDP) 28608c2ecf20Sopenharmony_ci return mem_ldx_xdp(nfp_prog, meta, size); 28618c2ecf20Sopenharmony_ci else 28628c2ecf20Sopenharmony_ci return mem_ldx_skb(nfp_prog, meta, size); 28638c2ecf20Sopenharmony_ci } 28648c2ecf20Sopenharmony_ci 28658c2ecf20Sopenharmony_ci if (meta->ptr.type == PTR_TO_PACKET) { 28668c2ecf20Sopenharmony_ci if (meta->pkt_cache.range_end) { 28678c2ecf20Sopenharmony_ci if (meta->pkt_cache.do_init) 28688c2ecf20Sopenharmony_ci mem_ldx_data_init_pktcache(nfp_prog, meta); 28698c2ecf20Sopenharmony_ci 28708c2ecf20Sopenharmony_ci return mem_ldx_data_from_pktcache(nfp_prog, meta, size); 28718c2ecf20Sopenharmony_ci } else { 28728c2ecf20Sopenharmony_ci return mem_ldx_data(nfp_prog, meta, size); 28738c2ecf20Sopenharmony_ci } 28748c2ecf20Sopenharmony_ci } 28758c2ecf20Sopenharmony_ci 28768c2ecf20Sopenharmony_ci if (meta->ptr.type == PTR_TO_STACK) 28778c2ecf20Sopenharmony_ci return mem_ldx_stack(nfp_prog, meta, size, 28788c2ecf20Sopenharmony_ci meta->ptr.off + meta->ptr.var_off.value); 28798c2ecf20Sopenharmony_ci 28808c2ecf20Sopenharmony_ci if (meta->ptr.type == PTR_TO_MAP_VALUE) 28818c2ecf20Sopenharmony_ci return mem_ldx_emem(nfp_prog, meta, size); 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 28848c2ecf20Sopenharmony_ci} 28858c2ecf20Sopenharmony_ci 28868c2ecf20Sopenharmony_cistatic int mem_ldx1(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 28878c2ecf20Sopenharmony_ci{ 28888c2ecf20Sopenharmony_ci return mem_ldx(nfp_prog, meta, 1); 28898c2ecf20Sopenharmony_ci} 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_cistatic int mem_ldx2(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 28928c2ecf20Sopenharmony_ci{ 28938c2ecf20Sopenharmony_ci return mem_ldx(nfp_prog, meta, 2); 28948c2ecf20Sopenharmony_ci} 28958c2ecf20Sopenharmony_ci 28968c2ecf20Sopenharmony_cistatic int mem_ldx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 28978c2ecf20Sopenharmony_ci{ 28988c2ecf20Sopenharmony_ci return mem_ldx(nfp_prog, meta, 4); 28998c2ecf20Sopenharmony_ci} 29008c2ecf20Sopenharmony_ci 29018c2ecf20Sopenharmony_cistatic int mem_ldx8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 29028c2ecf20Sopenharmony_ci{ 29038c2ecf20Sopenharmony_ci return mem_ldx(nfp_prog, meta, 8); 29048c2ecf20Sopenharmony_ci} 29058c2ecf20Sopenharmony_ci 29068c2ecf20Sopenharmony_cistatic int 29078c2ecf20Sopenharmony_cimem_st_data(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 29088c2ecf20Sopenharmony_ci unsigned int size) 29098c2ecf20Sopenharmony_ci{ 29108c2ecf20Sopenharmony_ci u64 imm = meta->insn.imm; /* sign extend */ 29118c2ecf20Sopenharmony_ci swreg off_reg; 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci off_reg = re_load_imm_any(nfp_prog, meta->insn.off, imm_b(nfp_prog)); 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_ci return data_st_host_order(nfp_prog, meta->insn.dst_reg * 2, off_reg, 29168c2ecf20Sopenharmony_ci imm, size); 29178c2ecf20Sopenharmony_ci} 29188c2ecf20Sopenharmony_ci 29198c2ecf20Sopenharmony_cistatic int mem_st(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 29208c2ecf20Sopenharmony_ci unsigned int size) 29218c2ecf20Sopenharmony_ci{ 29228c2ecf20Sopenharmony_ci if (meta->ptr.type == PTR_TO_PACKET) 29238c2ecf20Sopenharmony_ci return mem_st_data(nfp_prog, meta, size); 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 29268c2ecf20Sopenharmony_ci} 29278c2ecf20Sopenharmony_ci 29288c2ecf20Sopenharmony_cistatic int mem_st1(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 29298c2ecf20Sopenharmony_ci{ 29308c2ecf20Sopenharmony_ci return mem_st(nfp_prog, meta, 1); 29318c2ecf20Sopenharmony_ci} 29328c2ecf20Sopenharmony_ci 29338c2ecf20Sopenharmony_cistatic int mem_st2(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 29348c2ecf20Sopenharmony_ci{ 29358c2ecf20Sopenharmony_ci return mem_st(nfp_prog, meta, 2); 29368c2ecf20Sopenharmony_ci} 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_cistatic int mem_st4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 29398c2ecf20Sopenharmony_ci{ 29408c2ecf20Sopenharmony_ci return mem_st(nfp_prog, meta, 4); 29418c2ecf20Sopenharmony_ci} 29428c2ecf20Sopenharmony_ci 29438c2ecf20Sopenharmony_cistatic int mem_st8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 29448c2ecf20Sopenharmony_ci{ 29458c2ecf20Sopenharmony_ci return mem_st(nfp_prog, meta, 8); 29468c2ecf20Sopenharmony_ci} 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_cistatic int 29498c2ecf20Sopenharmony_cimem_stx_data(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 29508c2ecf20Sopenharmony_ci unsigned int size) 29518c2ecf20Sopenharmony_ci{ 29528c2ecf20Sopenharmony_ci swreg off_reg; 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_ci off_reg = re_load_imm_any(nfp_prog, meta->insn.off, imm_b(nfp_prog)); 29558c2ecf20Sopenharmony_ci 29568c2ecf20Sopenharmony_ci return data_stx_host_order(nfp_prog, meta->insn.dst_reg * 2, off_reg, 29578c2ecf20Sopenharmony_ci meta->insn.src_reg * 2, size); 29588c2ecf20Sopenharmony_ci} 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_cistatic int 29618c2ecf20Sopenharmony_cimem_stx_stack(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 29628c2ecf20Sopenharmony_ci unsigned int size, unsigned int ptr_off) 29638c2ecf20Sopenharmony_ci{ 29648c2ecf20Sopenharmony_ci return mem_op_stack(nfp_prog, meta, size, ptr_off, 29658c2ecf20Sopenharmony_ci meta->insn.src_reg * 2, meta->insn.dst_reg * 2, 29668c2ecf20Sopenharmony_ci false, wrp_lmem_store); 29678c2ecf20Sopenharmony_ci} 29688c2ecf20Sopenharmony_ci 29698c2ecf20Sopenharmony_cistatic int mem_stx_xdp(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 29708c2ecf20Sopenharmony_ci{ 29718c2ecf20Sopenharmony_ci switch (meta->insn.off) { 29728c2ecf20Sopenharmony_ci case offsetof(struct xdp_md, rx_queue_index): 29738c2ecf20Sopenharmony_ci return nfp_queue_select(nfp_prog, meta); 29748c2ecf20Sopenharmony_ci } 29758c2ecf20Sopenharmony_ci 29768c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); /* verifier should have rejected bad accesses */ 29778c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 29788c2ecf20Sopenharmony_ci} 29798c2ecf20Sopenharmony_ci 29808c2ecf20Sopenharmony_cistatic int 29818c2ecf20Sopenharmony_cimem_stx(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 29828c2ecf20Sopenharmony_ci unsigned int size) 29838c2ecf20Sopenharmony_ci{ 29848c2ecf20Sopenharmony_ci if (meta->ptr.type == PTR_TO_PACKET) 29858c2ecf20Sopenharmony_ci return mem_stx_data(nfp_prog, meta, size); 29868c2ecf20Sopenharmony_ci 29878c2ecf20Sopenharmony_ci if (meta->ptr.type == PTR_TO_STACK) 29888c2ecf20Sopenharmony_ci return mem_stx_stack(nfp_prog, meta, size, 29898c2ecf20Sopenharmony_ci meta->ptr.off + meta->ptr.var_off.value); 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 29928c2ecf20Sopenharmony_ci} 29938c2ecf20Sopenharmony_ci 29948c2ecf20Sopenharmony_cistatic int mem_stx1(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 29958c2ecf20Sopenharmony_ci{ 29968c2ecf20Sopenharmony_ci return mem_stx(nfp_prog, meta, 1); 29978c2ecf20Sopenharmony_ci} 29988c2ecf20Sopenharmony_ci 29998c2ecf20Sopenharmony_cistatic int mem_stx2(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 30008c2ecf20Sopenharmony_ci{ 30018c2ecf20Sopenharmony_ci return mem_stx(nfp_prog, meta, 2); 30028c2ecf20Sopenharmony_ci} 30038c2ecf20Sopenharmony_ci 30048c2ecf20Sopenharmony_cistatic int mem_stx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 30058c2ecf20Sopenharmony_ci{ 30068c2ecf20Sopenharmony_ci if (meta->ptr.type == PTR_TO_CTX) 30078c2ecf20Sopenharmony_ci if (nfp_prog->type == BPF_PROG_TYPE_XDP) 30088c2ecf20Sopenharmony_ci return mem_stx_xdp(nfp_prog, meta); 30098c2ecf20Sopenharmony_ci return mem_stx(nfp_prog, meta, 4); 30108c2ecf20Sopenharmony_ci} 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_cistatic int mem_stx8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 30138c2ecf20Sopenharmony_ci{ 30148c2ecf20Sopenharmony_ci return mem_stx(nfp_prog, meta, 8); 30158c2ecf20Sopenharmony_ci} 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_cistatic int 30188c2ecf20Sopenharmony_cimem_xadd(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, bool is64) 30198c2ecf20Sopenharmony_ci{ 30208c2ecf20Sopenharmony_ci u8 dst_gpr = meta->insn.dst_reg * 2; 30218c2ecf20Sopenharmony_ci u8 src_gpr = meta->insn.src_reg * 2; 30228c2ecf20Sopenharmony_ci unsigned int full_add, out; 30238c2ecf20Sopenharmony_ci swreg addra, addrb, off; 30248c2ecf20Sopenharmony_ci 30258c2ecf20Sopenharmony_ci off = ur_load_imm_any(nfp_prog, meta->insn.off, imm_b(nfp_prog)); 30268c2ecf20Sopenharmony_ci 30278c2ecf20Sopenharmony_ci /* We can fit 16 bits into command immediate, if we know the immediate 30288c2ecf20Sopenharmony_ci * is guaranteed to either always or never fit into 16 bit we only 30298c2ecf20Sopenharmony_ci * generate code to handle that particular case, otherwise generate 30308c2ecf20Sopenharmony_ci * code for both. 30318c2ecf20Sopenharmony_ci */ 30328c2ecf20Sopenharmony_ci out = nfp_prog_current_offset(nfp_prog); 30338c2ecf20Sopenharmony_ci full_add = nfp_prog_current_offset(nfp_prog); 30348c2ecf20Sopenharmony_ci 30358c2ecf20Sopenharmony_ci if (meta->insn.off) { 30368c2ecf20Sopenharmony_ci out += 2; 30378c2ecf20Sopenharmony_ci full_add += 2; 30388c2ecf20Sopenharmony_ci } 30398c2ecf20Sopenharmony_ci if (meta->xadd_maybe_16bit) { 30408c2ecf20Sopenharmony_ci out += 3; 30418c2ecf20Sopenharmony_ci full_add += 3; 30428c2ecf20Sopenharmony_ci } 30438c2ecf20Sopenharmony_ci if (meta->xadd_over_16bit) 30448c2ecf20Sopenharmony_ci out += 2 + is64; 30458c2ecf20Sopenharmony_ci if (meta->xadd_maybe_16bit && meta->xadd_over_16bit) { 30468c2ecf20Sopenharmony_ci out += 5; 30478c2ecf20Sopenharmony_ci full_add += 5; 30488c2ecf20Sopenharmony_ci } 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci /* Generate the branch for choosing add_imm vs add */ 30518c2ecf20Sopenharmony_ci if (meta->xadd_maybe_16bit && meta->xadd_over_16bit) { 30528c2ecf20Sopenharmony_ci swreg max_imm = imm_a(nfp_prog); 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, max_imm, 0xffff); 30558c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), 30568c2ecf20Sopenharmony_ci max_imm, ALU_OP_SUB, reg_b(src_gpr)); 30578c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), 30588c2ecf20Sopenharmony_ci reg_imm(0), ALU_OP_SUB_C, reg_b(src_gpr + 1)); 30598c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BLO, full_add, meta->insn.off ? 2 : 0); 30608c2ecf20Sopenharmony_ci /* defer for add */ 30618c2ecf20Sopenharmony_ci } 30628c2ecf20Sopenharmony_ci 30638c2ecf20Sopenharmony_ci /* If insn has an offset add to the address */ 30648c2ecf20Sopenharmony_ci if (!meta->insn.off) { 30658c2ecf20Sopenharmony_ci addra = reg_a(dst_gpr); 30668c2ecf20Sopenharmony_ci addrb = reg_b(dst_gpr + 1); 30678c2ecf20Sopenharmony_ci } else { 30688c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imma_a(nfp_prog), 30698c2ecf20Sopenharmony_ci reg_a(dst_gpr), ALU_OP_ADD, off); 30708c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imma_b(nfp_prog), 30718c2ecf20Sopenharmony_ci reg_a(dst_gpr + 1), ALU_OP_ADD_C, reg_imm(0)); 30728c2ecf20Sopenharmony_ci addra = imma_a(nfp_prog); 30738c2ecf20Sopenharmony_ci addrb = imma_b(nfp_prog); 30748c2ecf20Sopenharmony_ci } 30758c2ecf20Sopenharmony_ci 30768c2ecf20Sopenharmony_ci /* Generate the add_imm if 16 bits are possible */ 30778c2ecf20Sopenharmony_ci if (meta->xadd_maybe_16bit) { 30788c2ecf20Sopenharmony_ci swreg prev_alu = imm_a(nfp_prog); 30798c2ecf20Sopenharmony_ci 30808c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, prev_alu, 30818c2ecf20Sopenharmony_ci FIELD_PREP(CMD_OVE_DATA, 2) | 30828c2ecf20Sopenharmony_ci CMD_OVE_LEN | 30838c2ecf20Sopenharmony_ci FIELD_PREP(CMD_OV_LEN, 0x8 | is64 << 2)); 30848c2ecf20Sopenharmony_ci wrp_reg_or_subpart(nfp_prog, prev_alu, reg_b(src_gpr), 2, 2); 30858c2ecf20Sopenharmony_ci emit_cmd_indir(nfp_prog, CMD_TGT_ADD_IMM, CMD_MODE_40b_BA, 0, 30868c2ecf20Sopenharmony_ci addra, addrb, 0, CMD_CTX_NO_SWAP); 30878c2ecf20Sopenharmony_ci 30888c2ecf20Sopenharmony_ci if (meta->xadd_over_16bit) 30898c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_UNC, out, 0); 30908c2ecf20Sopenharmony_ci } 30918c2ecf20Sopenharmony_ci 30928c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, full_add)) 30938c2ecf20Sopenharmony_ci return -EINVAL; 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_ci /* Generate the add if 16 bits are not guaranteed */ 30968c2ecf20Sopenharmony_ci if (meta->xadd_over_16bit) { 30978c2ecf20Sopenharmony_ci emit_cmd(nfp_prog, CMD_TGT_ADD, CMD_MODE_40b_BA, 0, 30988c2ecf20Sopenharmony_ci addra, addrb, is64 << 2, 30998c2ecf20Sopenharmony_ci is64 ? CMD_CTX_SWAP_DEFER2 : CMD_CTX_SWAP_DEFER1); 31008c2ecf20Sopenharmony_ci 31018c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_xfer(0), reg_a(src_gpr)); 31028c2ecf20Sopenharmony_ci if (is64) 31038c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_xfer(1), reg_a(src_gpr + 1)); 31048c2ecf20Sopenharmony_ci } 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, out)) 31078c2ecf20Sopenharmony_ci return -EINVAL; 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_ci return 0; 31108c2ecf20Sopenharmony_ci} 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_cistatic int mem_xadd4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 31138c2ecf20Sopenharmony_ci{ 31148c2ecf20Sopenharmony_ci return mem_xadd(nfp_prog, meta, false); 31158c2ecf20Sopenharmony_ci} 31168c2ecf20Sopenharmony_ci 31178c2ecf20Sopenharmony_cistatic int mem_xadd8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 31188c2ecf20Sopenharmony_ci{ 31198c2ecf20Sopenharmony_ci return mem_xadd(nfp_prog, meta, true); 31208c2ecf20Sopenharmony_ci} 31218c2ecf20Sopenharmony_ci 31228c2ecf20Sopenharmony_cistatic int jump(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 31238c2ecf20Sopenharmony_ci{ 31248c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_UNC, meta->insn.off, 0); 31258c2ecf20Sopenharmony_ci 31268c2ecf20Sopenharmony_ci return 0; 31278c2ecf20Sopenharmony_ci} 31288c2ecf20Sopenharmony_ci 31298c2ecf20Sopenharmony_cistatic int jeq_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 31308c2ecf20Sopenharmony_ci{ 31318c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 31328c2ecf20Sopenharmony_ci u64 imm = insn->imm; /* sign extend */ 31338c2ecf20Sopenharmony_ci swreg or1, or2, tmp_reg; 31348c2ecf20Sopenharmony_ci 31358c2ecf20Sopenharmony_ci or1 = reg_a(insn->dst_reg * 2); 31368c2ecf20Sopenharmony_ci or2 = reg_b(insn->dst_reg * 2 + 1); 31378c2ecf20Sopenharmony_ci 31388c2ecf20Sopenharmony_ci if (imm & ~0U) { 31398c2ecf20Sopenharmony_ci tmp_reg = ur_load_imm_any(nfp_prog, imm & ~0U, imm_b(nfp_prog)); 31408c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imm_a(nfp_prog), 31418c2ecf20Sopenharmony_ci reg_a(insn->dst_reg * 2), ALU_OP_XOR, tmp_reg); 31428c2ecf20Sopenharmony_ci or1 = imm_a(nfp_prog); 31438c2ecf20Sopenharmony_ci } 31448c2ecf20Sopenharmony_ci 31458c2ecf20Sopenharmony_ci if (imm >> 32) { 31468c2ecf20Sopenharmony_ci tmp_reg = ur_load_imm_any(nfp_prog, imm >> 32, imm_b(nfp_prog)); 31478c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imm_b(nfp_prog), 31488c2ecf20Sopenharmony_ci reg_a(insn->dst_reg * 2 + 1), ALU_OP_XOR, tmp_reg); 31498c2ecf20Sopenharmony_ci or2 = imm_b(nfp_prog); 31508c2ecf20Sopenharmony_ci } 31518c2ecf20Sopenharmony_ci 31528c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), or1, ALU_OP_OR, or2); 31538c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BEQ, insn->off, 0); 31548c2ecf20Sopenharmony_ci 31558c2ecf20Sopenharmony_ci return 0; 31568c2ecf20Sopenharmony_ci} 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_cistatic int jeq32_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 31598c2ecf20Sopenharmony_ci{ 31608c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 31618c2ecf20Sopenharmony_ci swreg tmp_reg; 31628c2ecf20Sopenharmony_ci 31638c2ecf20Sopenharmony_ci tmp_reg = ur_load_imm_any(nfp_prog, insn->imm, imm_b(nfp_prog)); 31648c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), 31658c2ecf20Sopenharmony_ci reg_a(insn->dst_reg * 2), ALU_OP_XOR, tmp_reg); 31668c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BEQ, insn->off, 0); 31678c2ecf20Sopenharmony_ci 31688c2ecf20Sopenharmony_ci return 0; 31698c2ecf20Sopenharmony_ci} 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_cistatic int jset_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 31728c2ecf20Sopenharmony_ci{ 31738c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 31748c2ecf20Sopenharmony_ci u64 imm = insn->imm; /* sign extend */ 31758c2ecf20Sopenharmony_ci u8 dst_gpr = insn->dst_reg * 2; 31768c2ecf20Sopenharmony_ci swreg tmp_reg; 31778c2ecf20Sopenharmony_ci 31788c2ecf20Sopenharmony_ci tmp_reg = ur_load_imm_any(nfp_prog, imm & ~0U, imm_b(nfp_prog)); 31798c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imm_b(nfp_prog), 31808c2ecf20Sopenharmony_ci reg_a(dst_gpr), ALU_OP_AND, tmp_reg); 31818c2ecf20Sopenharmony_ci /* Upper word of the mask can only be 0 or ~0 from sign extension, 31828c2ecf20Sopenharmony_ci * so either ignore it or OR the whole thing in. 31838c2ecf20Sopenharmony_ci */ 31848c2ecf20Sopenharmony_ci if (is_mbpf_jmp64(meta) && imm >> 32) { 31858c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), 31868c2ecf20Sopenharmony_ci reg_a(dst_gpr + 1), ALU_OP_OR, imm_b(nfp_prog)); 31878c2ecf20Sopenharmony_ci } 31888c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BNE, insn->off, 0); 31898c2ecf20Sopenharmony_ci 31908c2ecf20Sopenharmony_ci return 0; 31918c2ecf20Sopenharmony_ci} 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_cistatic int jne_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 31948c2ecf20Sopenharmony_ci{ 31958c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 31968c2ecf20Sopenharmony_ci u64 imm = insn->imm; /* sign extend */ 31978c2ecf20Sopenharmony_ci bool is_jmp32 = is_mbpf_jmp32(meta); 31988c2ecf20Sopenharmony_ci swreg tmp_reg; 31998c2ecf20Sopenharmony_ci 32008c2ecf20Sopenharmony_ci if (!imm) { 32018c2ecf20Sopenharmony_ci if (is_jmp32) 32028c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_none(), ALU_OP_NONE, 32038c2ecf20Sopenharmony_ci reg_b(insn->dst_reg * 2)); 32048c2ecf20Sopenharmony_ci else 32058c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(insn->dst_reg * 2), 32068c2ecf20Sopenharmony_ci ALU_OP_OR, reg_b(insn->dst_reg * 2 + 1)); 32078c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BNE, insn->off, 0); 32088c2ecf20Sopenharmony_ci return 0; 32098c2ecf20Sopenharmony_ci } 32108c2ecf20Sopenharmony_ci 32118c2ecf20Sopenharmony_ci tmp_reg = ur_load_imm_any(nfp_prog, imm & ~0U, imm_b(nfp_prog)); 32128c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), 32138c2ecf20Sopenharmony_ci reg_a(insn->dst_reg * 2), ALU_OP_XOR, tmp_reg); 32148c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BNE, insn->off, 0); 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci if (is_jmp32) 32178c2ecf20Sopenharmony_ci return 0; 32188c2ecf20Sopenharmony_ci 32198c2ecf20Sopenharmony_ci tmp_reg = ur_load_imm_any(nfp_prog, imm >> 32, imm_b(nfp_prog)); 32208c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), 32218c2ecf20Sopenharmony_ci reg_a(insn->dst_reg * 2 + 1), ALU_OP_XOR, tmp_reg); 32228c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BNE, insn->off, 0); 32238c2ecf20Sopenharmony_ci 32248c2ecf20Sopenharmony_ci return 0; 32258c2ecf20Sopenharmony_ci} 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_cistatic int jeq_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 32288c2ecf20Sopenharmony_ci{ 32298c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &meta->insn; 32308c2ecf20Sopenharmony_ci 32318c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imm_a(nfp_prog), reg_a(insn->dst_reg * 2), 32328c2ecf20Sopenharmony_ci ALU_OP_XOR, reg_b(insn->src_reg * 2)); 32338c2ecf20Sopenharmony_ci if (is_mbpf_jmp64(meta)) { 32348c2ecf20Sopenharmony_ci emit_alu(nfp_prog, imm_b(nfp_prog), 32358c2ecf20Sopenharmony_ci reg_a(insn->dst_reg * 2 + 1), ALU_OP_XOR, 32368c2ecf20Sopenharmony_ci reg_b(insn->src_reg * 2 + 1)); 32378c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), imm_a(nfp_prog), ALU_OP_OR, 32388c2ecf20Sopenharmony_ci imm_b(nfp_prog)); 32398c2ecf20Sopenharmony_ci } 32408c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BEQ, insn->off, 0); 32418c2ecf20Sopenharmony_ci 32428c2ecf20Sopenharmony_ci return 0; 32438c2ecf20Sopenharmony_ci} 32448c2ecf20Sopenharmony_ci 32458c2ecf20Sopenharmony_cistatic int jset_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 32468c2ecf20Sopenharmony_ci{ 32478c2ecf20Sopenharmony_ci return wrp_test_reg(nfp_prog, meta, ALU_OP_AND, BR_BNE); 32488c2ecf20Sopenharmony_ci} 32498c2ecf20Sopenharmony_ci 32508c2ecf20Sopenharmony_cistatic int jne_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 32518c2ecf20Sopenharmony_ci{ 32528c2ecf20Sopenharmony_ci return wrp_test_reg(nfp_prog, meta, ALU_OP_XOR, BR_BNE); 32538c2ecf20Sopenharmony_ci} 32548c2ecf20Sopenharmony_ci 32558c2ecf20Sopenharmony_cistatic int 32568c2ecf20Sopenharmony_cibpf_to_bpf_call(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 32578c2ecf20Sopenharmony_ci{ 32588c2ecf20Sopenharmony_ci u32 ret_tgt, stack_depth, offset_br; 32598c2ecf20Sopenharmony_ci swreg tmp_reg; 32608c2ecf20Sopenharmony_ci 32618c2ecf20Sopenharmony_ci stack_depth = round_up(nfp_prog->stack_frame_depth, STACK_FRAME_ALIGN); 32628c2ecf20Sopenharmony_ci /* Space for saving the return address is accounted for by the callee, 32638c2ecf20Sopenharmony_ci * so stack_depth can be zero for the main function. 32648c2ecf20Sopenharmony_ci */ 32658c2ecf20Sopenharmony_ci if (stack_depth) { 32668c2ecf20Sopenharmony_ci tmp_reg = ur_load_imm_any(nfp_prog, stack_depth, 32678c2ecf20Sopenharmony_ci stack_imm(nfp_prog)); 32688c2ecf20Sopenharmony_ci emit_alu(nfp_prog, stack_reg(nfp_prog), 32698c2ecf20Sopenharmony_ci stack_reg(nfp_prog), ALU_OP_ADD, tmp_reg); 32708c2ecf20Sopenharmony_ci emit_csr_wr(nfp_prog, stack_reg(nfp_prog), 32718c2ecf20Sopenharmony_ci NFP_CSR_ACT_LM_ADDR0); 32728c2ecf20Sopenharmony_ci } 32738c2ecf20Sopenharmony_ci 32748c2ecf20Sopenharmony_ci /* Two cases for jumping to the callee: 32758c2ecf20Sopenharmony_ci * 32768c2ecf20Sopenharmony_ci * - If callee uses and needs to save R6~R9 then: 32778c2ecf20Sopenharmony_ci * 1. Put the start offset of the callee into imm_b(). This will 32788c2ecf20Sopenharmony_ci * require a fixup step, as we do not necessarily know this 32798c2ecf20Sopenharmony_ci * address yet. 32808c2ecf20Sopenharmony_ci * 2. Put the return address from the callee to the caller into 32818c2ecf20Sopenharmony_ci * register ret_reg(). 32828c2ecf20Sopenharmony_ci * 3. (After defer slots are consumed) Jump to the subroutine that 32838c2ecf20Sopenharmony_ci * pushes the registers to the stack. 32848c2ecf20Sopenharmony_ci * The subroutine acts as a trampoline, and returns to the address in 32858c2ecf20Sopenharmony_ci * imm_b(), i.e. jumps to the callee. 32868c2ecf20Sopenharmony_ci * 32878c2ecf20Sopenharmony_ci * - If callee does not need to save R6~R9 then just load return 32888c2ecf20Sopenharmony_ci * address to the caller in ret_reg(), and jump to the callee 32898c2ecf20Sopenharmony_ci * directly. 32908c2ecf20Sopenharmony_ci * 32918c2ecf20Sopenharmony_ci * Using ret_reg() to pass the return address to the callee is set here 32928c2ecf20Sopenharmony_ci * as a convention. The callee can then push this address onto its 32938c2ecf20Sopenharmony_ci * stack frame in its prologue. The advantages of passing the return 32948c2ecf20Sopenharmony_ci * address through ret_reg(), instead of pushing it to the stack right 32958c2ecf20Sopenharmony_ci * here, are the following: 32968c2ecf20Sopenharmony_ci * - It looks cleaner. 32978c2ecf20Sopenharmony_ci * - If the called function is called multiple time, we get a lower 32988c2ecf20Sopenharmony_ci * program size. 32998c2ecf20Sopenharmony_ci * - We save two no-op instructions that should be added just before 33008c2ecf20Sopenharmony_ci * the emit_br() when stack depth is not null otherwise. 33018c2ecf20Sopenharmony_ci * - If we ever find a register to hold the return address during whole 33028c2ecf20Sopenharmony_ci * execution of the callee, we will not have to push the return 33038c2ecf20Sopenharmony_ci * address to the stack for leaf functions. 33048c2ecf20Sopenharmony_ci */ 33058c2ecf20Sopenharmony_ci if (!meta->jmp_dst) { 33068c2ecf20Sopenharmony_ci pr_err("BUG: BPF-to-BPF call has no destination recorded\n"); 33078c2ecf20Sopenharmony_ci return -ELOOP; 33088c2ecf20Sopenharmony_ci } 33098c2ecf20Sopenharmony_ci if (nfp_prog->subprog[meta->jmp_dst->subprog_idx].needs_reg_push) { 33108c2ecf20Sopenharmony_ci ret_tgt = nfp_prog_current_offset(nfp_prog) + 3; 33118c2ecf20Sopenharmony_ci emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO, 2, 33128c2ecf20Sopenharmony_ci RELO_BR_GO_CALL_PUSH_REGS); 33138c2ecf20Sopenharmony_ci offset_br = nfp_prog_current_offset(nfp_prog); 33148c2ecf20Sopenharmony_ci wrp_immed_relo(nfp_prog, imm_b(nfp_prog), 0, RELO_IMMED_REL); 33158c2ecf20Sopenharmony_ci } else { 33168c2ecf20Sopenharmony_ci ret_tgt = nfp_prog_current_offset(nfp_prog) + 2; 33178c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_UNC, meta->insn.imm, 1); 33188c2ecf20Sopenharmony_ci offset_br = nfp_prog_current_offset(nfp_prog); 33198c2ecf20Sopenharmony_ci } 33208c2ecf20Sopenharmony_ci wrp_immed_relo(nfp_prog, ret_reg(nfp_prog), ret_tgt, RELO_IMMED_REL); 33218c2ecf20Sopenharmony_ci 33228c2ecf20Sopenharmony_ci if (!nfp_prog_confirm_current_offset(nfp_prog, ret_tgt)) 33238c2ecf20Sopenharmony_ci return -EINVAL; 33248c2ecf20Sopenharmony_ci 33258c2ecf20Sopenharmony_ci if (stack_depth) { 33268c2ecf20Sopenharmony_ci tmp_reg = ur_load_imm_any(nfp_prog, stack_depth, 33278c2ecf20Sopenharmony_ci stack_imm(nfp_prog)); 33288c2ecf20Sopenharmony_ci emit_alu(nfp_prog, stack_reg(nfp_prog), 33298c2ecf20Sopenharmony_ci stack_reg(nfp_prog), ALU_OP_SUB, tmp_reg); 33308c2ecf20Sopenharmony_ci emit_csr_wr(nfp_prog, stack_reg(nfp_prog), 33318c2ecf20Sopenharmony_ci NFP_CSR_ACT_LM_ADDR0); 33328c2ecf20Sopenharmony_ci wrp_nops(nfp_prog, 3); 33338c2ecf20Sopenharmony_ci } 33348c2ecf20Sopenharmony_ci 33358c2ecf20Sopenharmony_ci meta->num_insns_after_br = nfp_prog_current_offset(nfp_prog); 33368c2ecf20Sopenharmony_ci meta->num_insns_after_br -= offset_br; 33378c2ecf20Sopenharmony_ci 33388c2ecf20Sopenharmony_ci return 0; 33398c2ecf20Sopenharmony_ci} 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_cistatic int helper_call(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 33428c2ecf20Sopenharmony_ci{ 33438c2ecf20Sopenharmony_ci switch (meta->insn.imm) { 33448c2ecf20Sopenharmony_ci case BPF_FUNC_xdp_adjust_head: 33458c2ecf20Sopenharmony_ci return adjust_head(nfp_prog, meta); 33468c2ecf20Sopenharmony_ci case BPF_FUNC_xdp_adjust_tail: 33478c2ecf20Sopenharmony_ci return adjust_tail(nfp_prog, meta); 33488c2ecf20Sopenharmony_ci case BPF_FUNC_map_lookup_elem: 33498c2ecf20Sopenharmony_ci case BPF_FUNC_map_update_elem: 33508c2ecf20Sopenharmony_ci case BPF_FUNC_map_delete_elem: 33518c2ecf20Sopenharmony_ci return map_call_stack_common(nfp_prog, meta); 33528c2ecf20Sopenharmony_ci case BPF_FUNC_get_prandom_u32: 33538c2ecf20Sopenharmony_ci return nfp_get_prandom_u32(nfp_prog, meta); 33548c2ecf20Sopenharmony_ci case BPF_FUNC_perf_event_output: 33558c2ecf20Sopenharmony_ci return nfp_perf_event_output(nfp_prog, meta); 33568c2ecf20Sopenharmony_ci default: 33578c2ecf20Sopenharmony_ci WARN_ONCE(1, "verifier allowed unsupported function\n"); 33588c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 33598c2ecf20Sopenharmony_ci } 33608c2ecf20Sopenharmony_ci} 33618c2ecf20Sopenharmony_ci 33628c2ecf20Sopenharmony_cistatic int call(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 33638c2ecf20Sopenharmony_ci{ 33648c2ecf20Sopenharmony_ci if (is_mbpf_pseudo_call(meta)) 33658c2ecf20Sopenharmony_ci return bpf_to_bpf_call(nfp_prog, meta); 33668c2ecf20Sopenharmony_ci else 33678c2ecf20Sopenharmony_ci return helper_call(nfp_prog, meta); 33688c2ecf20Sopenharmony_ci} 33698c2ecf20Sopenharmony_ci 33708c2ecf20Sopenharmony_cistatic bool nfp_is_main_function(struct nfp_insn_meta *meta) 33718c2ecf20Sopenharmony_ci{ 33728c2ecf20Sopenharmony_ci return meta->subprog_idx == 0; 33738c2ecf20Sopenharmony_ci} 33748c2ecf20Sopenharmony_ci 33758c2ecf20Sopenharmony_cistatic int goto_out(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 33768c2ecf20Sopenharmony_ci{ 33778c2ecf20Sopenharmony_ci emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO, 0, RELO_BR_GO_OUT); 33788c2ecf20Sopenharmony_ci 33798c2ecf20Sopenharmony_ci return 0; 33808c2ecf20Sopenharmony_ci} 33818c2ecf20Sopenharmony_ci 33828c2ecf20Sopenharmony_cistatic int 33838c2ecf20Sopenharmony_cinfp_subprog_epilogue(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 33848c2ecf20Sopenharmony_ci{ 33858c2ecf20Sopenharmony_ci if (nfp_prog->subprog[meta->subprog_idx].needs_reg_push) { 33868c2ecf20Sopenharmony_ci /* Pop R6~R9 to the stack via related subroutine. 33878c2ecf20Sopenharmony_ci * We loaded the return address to the caller into ret_reg(). 33888c2ecf20Sopenharmony_ci * This means that the subroutine does not come back here, we 33898c2ecf20Sopenharmony_ci * make it jump back to the subprogram caller directly! 33908c2ecf20Sopenharmony_ci */ 33918c2ecf20Sopenharmony_ci emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO, 1, 33928c2ecf20Sopenharmony_ci RELO_BR_GO_CALL_POP_REGS); 33938c2ecf20Sopenharmony_ci /* Pop return address from the stack. */ 33948c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, ret_reg(nfp_prog), reg_lm(0, 0)); 33958c2ecf20Sopenharmony_ci } else { 33968c2ecf20Sopenharmony_ci /* Pop return address from the stack. */ 33978c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, ret_reg(nfp_prog), reg_lm(0, 0)); 33988c2ecf20Sopenharmony_ci /* Jump back to caller if no callee-saved registers were used 33998c2ecf20Sopenharmony_ci * by the subprogram. 34008c2ecf20Sopenharmony_ci */ 34018c2ecf20Sopenharmony_ci emit_rtn(nfp_prog, ret_reg(nfp_prog), 0); 34028c2ecf20Sopenharmony_ci } 34038c2ecf20Sopenharmony_ci 34048c2ecf20Sopenharmony_ci return 0; 34058c2ecf20Sopenharmony_ci} 34068c2ecf20Sopenharmony_ci 34078c2ecf20Sopenharmony_cistatic int jmp_exit(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 34088c2ecf20Sopenharmony_ci{ 34098c2ecf20Sopenharmony_ci if (nfp_is_main_function(meta)) 34108c2ecf20Sopenharmony_ci return goto_out(nfp_prog, meta); 34118c2ecf20Sopenharmony_ci else 34128c2ecf20Sopenharmony_ci return nfp_subprog_epilogue(nfp_prog, meta); 34138c2ecf20Sopenharmony_ci} 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_cistatic const instr_cb_t instr_cb[256] = { 34168c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_MOV | BPF_X] = mov_reg64, 34178c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_MOV | BPF_K] = mov_imm64, 34188c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_XOR | BPF_X] = xor_reg64, 34198c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_XOR | BPF_K] = xor_imm64, 34208c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_AND | BPF_X] = and_reg64, 34218c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_AND | BPF_K] = and_imm64, 34228c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_OR | BPF_X] = or_reg64, 34238c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_OR | BPF_K] = or_imm64, 34248c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_ADD | BPF_X] = add_reg64, 34258c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_ADD | BPF_K] = add_imm64, 34268c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_SUB | BPF_X] = sub_reg64, 34278c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_SUB | BPF_K] = sub_imm64, 34288c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_MUL | BPF_X] = mul_reg64, 34298c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_MUL | BPF_K] = mul_imm64, 34308c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_DIV | BPF_X] = div_reg64, 34318c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_DIV | BPF_K] = div_imm64, 34328c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_NEG] = neg_reg64, 34338c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_LSH | BPF_X] = shl_reg64, 34348c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_LSH | BPF_K] = shl_imm64, 34358c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_RSH | BPF_X] = shr_reg64, 34368c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_RSH | BPF_K] = shr_imm64, 34378c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_ARSH | BPF_X] = ashr_reg64, 34388c2ecf20Sopenharmony_ci [BPF_ALU64 | BPF_ARSH | BPF_K] = ashr_imm64, 34398c2ecf20Sopenharmony_ci [BPF_ALU | BPF_MOV | BPF_X] = mov_reg, 34408c2ecf20Sopenharmony_ci [BPF_ALU | BPF_MOV | BPF_K] = mov_imm, 34418c2ecf20Sopenharmony_ci [BPF_ALU | BPF_XOR | BPF_X] = xor_reg, 34428c2ecf20Sopenharmony_ci [BPF_ALU | BPF_XOR | BPF_K] = xor_imm, 34438c2ecf20Sopenharmony_ci [BPF_ALU | BPF_AND | BPF_X] = and_reg, 34448c2ecf20Sopenharmony_ci [BPF_ALU | BPF_AND | BPF_K] = and_imm, 34458c2ecf20Sopenharmony_ci [BPF_ALU | BPF_OR | BPF_X] = or_reg, 34468c2ecf20Sopenharmony_ci [BPF_ALU | BPF_OR | BPF_K] = or_imm, 34478c2ecf20Sopenharmony_ci [BPF_ALU | BPF_ADD | BPF_X] = add_reg, 34488c2ecf20Sopenharmony_ci [BPF_ALU | BPF_ADD | BPF_K] = add_imm, 34498c2ecf20Sopenharmony_ci [BPF_ALU | BPF_SUB | BPF_X] = sub_reg, 34508c2ecf20Sopenharmony_ci [BPF_ALU | BPF_SUB | BPF_K] = sub_imm, 34518c2ecf20Sopenharmony_ci [BPF_ALU | BPF_MUL | BPF_X] = mul_reg, 34528c2ecf20Sopenharmony_ci [BPF_ALU | BPF_MUL | BPF_K] = mul_imm, 34538c2ecf20Sopenharmony_ci [BPF_ALU | BPF_DIV | BPF_X] = div_reg, 34548c2ecf20Sopenharmony_ci [BPF_ALU | BPF_DIV | BPF_K] = div_imm, 34558c2ecf20Sopenharmony_ci [BPF_ALU | BPF_NEG] = neg_reg, 34568c2ecf20Sopenharmony_ci [BPF_ALU | BPF_LSH | BPF_X] = shl_reg, 34578c2ecf20Sopenharmony_ci [BPF_ALU | BPF_LSH | BPF_K] = shl_imm, 34588c2ecf20Sopenharmony_ci [BPF_ALU | BPF_RSH | BPF_X] = shr_reg, 34598c2ecf20Sopenharmony_ci [BPF_ALU | BPF_RSH | BPF_K] = shr_imm, 34608c2ecf20Sopenharmony_ci [BPF_ALU | BPF_ARSH | BPF_X] = ashr_reg, 34618c2ecf20Sopenharmony_ci [BPF_ALU | BPF_ARSH | BPF_K] = ashr_imm, 34628c2ecf20Sopenharmony_ci [BPF_ALU | BPF_END | BPF_X] = end_reg32, 34638c2ecf20Sopenharmony_ci [BPF_LD | BPF_IMM | BPF_DW] = imm_ld8, 34648c2ecf20Sopenharmony_ci [BPF_LD | BPF_ABS | BPF_B] = data_ld1, 34658c2ecf20Sopenharmony_ci [BPF_LD | BPF_ABS | BPF_H] = data_ld2, 34668c2ecf20Sopenharmony_ci [BPF_LD | BPF_ABS | BPF_W] = data_ld4, 34678c2ecf20Sopenharmony_ci [BPF_LD | BPF_IND | BPF_B] = data_ind_ld1, 34688c2ecf20Sopenharmony_ci [BPF_LD | BPF_IND | BPF_H] = data_ind_ld2, 34698c2ecf20Sopenharmony_ci [BPF_LD | BPF_IND | BPF_W] = data_ind_ld4, 34708c2ecf20Sopenharmony_ci [BPF_LDX | BPF_MEM | BPF_B] = mem_ldx1, 34718c2ecf20Sopenharmony_ci [BPF_LDX | BPF_MEM | BPF_H] = mem_ldx2, 34728c2ecf20Sopenharmony_ci [BPF_LDX | BPF_MEM | BPF_W] = mem_ldx4, 34738c2ecf20Sopenharmony_ci [BPF_LDX | BPF_MEM | BPF_DW] = mem_ldx8, 34748c2ecf20Sopenharmony_ci [BPF_STX | BPF_MEM | BPF_B] = mem_stx1, 34758c2ecf20Sopenharmony_ci [BPF_STX | BPF_MEM | BPF_H] = mem_stx2, 34768c2ecf20Sopenharmony_ci [BPF_STX | BPF_MEM | BPF_W] = mem_stx4, 34778c2ecf20Sopenharmony_ci [BPF_STX | BPF_MEM | BPF_DW] = mem_stx8, 34788c2ecf20Sopenharmony_ci [BPF_STX | BPF_XADD | BPF_W] = mem_xadd4, 34798c2ecf20Sopenharmony_ci [BPF_STX | BPF_XADD | BPF_DW] = mem_xadd8, 34808c2ecf20Sopenharmony_ci [BPF_ST | BPF_MEM | BPF_B] = mem_st1, 34818c2ecf20Sopenharmony_ci [BPF_ST | BPF_MEM | BPF_H] = mem_st2, 34828c2ecf20Sopenharmony_ci [BPF_ST | BPF_MEM | BPF_W] = mem_st4, 34838c2ecf20Sopenharmony_ci [BPF_ST | BPF_MEM | BPF_DW] = mem_st8, 34848c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JA | BPF_K] = jump, 34858c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JEQ | BPF_K] = jeq_imm, 34868c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JGT | BPF_K] = cmp_imm, 34878c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JGE | BPF_K] = cmp_imm, 34888c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JLT | BPF_K] = cmp_imm, 34898c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JLE | BPF_K] = cmp_imm, 34908c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JSGT | BPF_K] = cmp_imm, 34918c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JSGE | BPF_K] = cmp_imm, 34928c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JSLT | BPF_K] = cmp_imm, 34938c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JSLE | BPF_K] = cmp_imm, 34948c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JSET | BPF_K] = jset_imm, 34958c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JNE | BPF_K] = jne_imm, 34968c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JEQ | BPF_X] = jeq_reg, 34978c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JGT | BPF_X] = cmp_reg, 34988c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JGE | BPF_X] = cmp_reg, 34998c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JLT | BPF_X] = cmp_reg, 35008c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JLE | BPF_X] = cmp_reg, 35018c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JSGT | BPF_X] = cmp_reg, 35028c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JSGE | BPF_X] = cmp_reg, 35038c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JSLT | BPF_X] = cmp_reg, 35048c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JSLE | BPF_X] = cmp_reg, 35058c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JSET | BPF_X] = jset_reg, 35068c2ecf20Sopenharmony_ci [BPF_JMP | BPF_JNE | BPF_X] = jne_reg, 35078c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JEQ | BPF_K] = jeq32_imm, 35088c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JGT | BPF_K] = cmp_imm, 35098c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JGE | BPF_K] = cmp_imm, 35108c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JLT | BPF_K] = cmp_imm, 35118c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JLE | BPF_K] = cmp_imm, 35128c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JSGT | BPF_K] =cmp_imm, 35138c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JSGE | BPF_K] =cmp_imm, 35148c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JSLT | BPF_K] =cmp_imm, 35158c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JSLE | BPF_K] =cmp_imm, 35168c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JSET | BPF_K] =jset_imm, 35178c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JNE | BPF_K] = jne_imm, 35188c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JEQ | BPF_X] = jeq_reg, 35198c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JGT | BPF_X] = cmp_reg, 35208c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JGE | BPF_X] = cmp_reg, 35218c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JLT | BPF_X] = cmp_reg, 35228c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JLE | BPF_X] = cmp_reg, 35238c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JSGT | BPF_X] =cmp_reg, 35248c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JSGE | BPF_X] =cmp_reg, 35258c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JSLT | BPF_X] =cmp_reg, 35268c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JSLE | BPF_X] =cmp_reg, 35278c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JSET | BPF_X] =jset_reg, 35288c2ecf20Sopenharmony_ci [BPF_JMP32 | BPF_JNE | BPF_X] = jne_reg, 35298c2ecf20Sopenharmony_ci [BPF_JMP | BPF_CALL] = call, 35308c2ecf20Sopenharmony_ci [BPF_JMP | BPF_EXIT] = jmp_exit, 35318c2ecf20Sopenharmony_ci}; 35328c2ecf20Sopenharmony_ci 35338c2ecf20Sopenharmony_ci/* --- Assembler logic --- */ 35348c2ecf20Sopenharmony_cistatic int 35358c2ecf20Sopenharmony_cinfp_fixup_immed_relo(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 35368c2ecf20Sopenharmony_ci struct nfp_insn_meta *jmp_dst, u32 br_idx) 35378c2ecf20Sopenharmony_ci{ 35388c2ecf20Sopenharmony_ci if (immed_get_value(nfp_prog->prog[br_idx + 1])) { 35398c2ecf20Sopenharmony_ci pr_err("BUG: failed to fix up callee register saving\n"); 35408c2ecf20Sopenharmony_ci return -EINVAL; 35418c2ecf20Sopenharmony_ci } 35428c2ecf20Sopenharmony_ci 35438c2ecf20Sopenharmony_ci immed_set_value(&nfp_prog->prog[br_idx + 1], jmp_dst->off); 35448c2ecf20Sopenharmony_ci 35458c2ecf20Sopenharmony_ci return 0; 35468c2ecf20Sopenharmony_ci} 35478c2ecf20Sopenharmony_ci 35488c2ecf20Sopenharmony_cistatic int nfp_fixup_branches(struct nfp_prog *nfp_prog) 35498c2ecf20Sopenharmony_ci{ 35508c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta, *jmp_dst; 35518c2ecf20Sopenharmony_ci u32 idx, br_idx; 35528c2ecf20Sopenharmony_ci int err; 35538c2ecf20Sopenharmony_ci 35548c2ecf20Sopenharmony_ci list_for_each_entry(meta, &nfp_prog->insns, l) { 35558c2ecf20Sopenharmony_ci if (meta->flags & FLAG_INSN_SKIP_MASK) 35568c2ecf20Sopenharmony_ci continue; 35578c2ecf20Sopenharmony_ci if (!is_mbpf_jmp(meta)) 35588c2ecf20Sopenharmony_ci continue; 35598c2ecf20Sopenharmony_ci if (meta->insn.code == (BPF_JMP | BPF_EXIT) && 35608c2ecf20Sopenharmony_ci !nfp_is_main_function(meta)) 35618c2ecf20Sopenharmony_ci continue; 35628c2ecf20Sopenharmony_ci if (is_mbpf_helper_call(meta)) 35638c2ecf20Sopenharmony_ci continue; 35648c2ecf20Sopenharmony_ci 35658c2ecf20Sopenharmony_ci if (list_is_last(&meta->l, &nfp_prog->insns)) 35668c2ecf20Sopenharmony_ci br_idx = nfp_prog->last_bpf_off; 35678c2ecf20Sopenharmony_ci else 35688c2ecf20Sopenharmony_ci br_idx = list_next_entry(meta, l)->off - 1; 35698c2ecf20Sopenharmony_ci 35708c2ecf20Sopenharmony_ci /* For BPF-to-BPF function call, a stack adjustment sequence is 35718c2ecf20Sopenharmony_ci * generated after the return instruction. Therefore, we must 35728c2ecf20Sopenharmony_ci * withdraw the length of this sequence to have br_idx pointing 35738c2ecf20Sopenharmony_ci * to where the "branch" NFP instruction is expected to be. 35748c2ecf20Sopenharmony_ci */ 35758c2ecf20Sopenharmony_ci if (is_mbpf_pseudo_call(meta)) 35768c2ecf20Sopenharmony_ci br_idx -= meta->num_insns_after_br; 35778c2ecf20Sopenharmony_ci 35788c2ecf20Sopenharmony_ci if (!nfp_is_br(nfp_prog->prog[br_idx])) { 35798c2ecf20Sopenharmony_ci pr_err("Fixup found block not ending in branch %d %02x %016llx!!\n", 35808c2ecf20Sopenharmony_ci br_idx, meta->insn.code, nfp_prog->prog[br_idx]); 35818c2ecf20Sopenharmony_ci return -ELOOP; 35828c2ecf20Sopenharmony_ci } 35838c2ecf20Sopenharmony_ci 35848c2ecf20Sopenharmony_ci if (meta->insn.code == (BPF_JMP | BPF_EXIT)) 35858c2ecf20Sopenharmony_ci continue; 35868c2ecf20Sopenharmony_ci 35878c2ecf20Sopenharmony_ci /* Leave special branches for later */ 35888c2ecf20Sopenharmony_ci if (FIELD_GET(OP_RELO_TYPE, nfp_prog->prog[br_idx]) != 35898c2ecf20Sopenharmony_ci RELO_BR_REL && !is_mbpf_pseudo_call(meta)) 35908c2ecf20Sopenharmony_ci continue; 35918c2ecf20Sopenharmony_ci 35928c2ecf20Sopenharmony_ci if (!meta->jmp_dst) { 35938c2ecf20Sopenharmony_ci pr_err("Non-exit jump doesn't have destination info recorded!!\n"); 35948c2ecf20Sopenharmony_ci return -ELOOP; 35958c2ecf20Sopenharmony_ci } 35968c2ecf20Sopenharmony_ci 35978c2ecf20Sopenharmony_ci jmp_dst = meta->jmp_dst; 35988c2ecf20Sopenharmony_ci 35998c2ecf20Sopenharmony_ci if (jmp_dst->flags & FLAG_INSN_SKIP_PREC_DEPENDENT) { 36008c2ecf20Sopenharmony_ci pr_err("Branch landing on removed instruction!!\n"); 36018c2ecf20Sopenharmony_ci return -ELOOP; 36028c2ecf20Sopenharmony_ci } 36038c2ecf20Sopenharmony_ci 36048c2ecf20Sopenharmony_ci if (is_mbpf_pseudo_call(meta) && 36058c2ecf20Sopenharmony_ci nfp_prog->subprog[jmp_dst->subprog_idx].needs_reg_push) { 36068c2ecf20Sopenharmony_ci err = nfp_fixup_immed_relo(nfp_prog, meta, 36078c2ecf20Sopenharmony_ci jmp_dst, br_idx); 36088c2ecf20Sopenharmony_ci if (err) 36098c2ecf20Sopenharmony_ci return err; 36108c2ecf20Sopenharmony_ci } 36118c2ecf20Sopenharmony_ci 36128c2ecf20Sopenharmony_ci if (FIELD_GET(OP_RELO_TYPE, nfp_prog->prog[br_idx]) != 36138c2ecf20Sopenharmony_ci RELO_BR_REL) 36148c2ecf20Sopenharmony_ci continue; 36158c2ecf20Sopenharmony_ci 36168c2ecf20Sopenharmony_ci for (idx = meta->off; idx <= br_idx; idx++) { 36178c2ecf20Sopenharmony_ci if (!nfp_is_br(nfp_prog->prog[idx])) 36188c2ecf20Sopenharmony_ci continue; 36198c2ecf20Sopenharmony_ci br_set_offset(&nfp_prog->prog[idx], jmp_dst->off); 36208c2ecf20Sopenharmony_ci } 36218c2ecf20Sopenharmony_ci } 36228c2ecf20Sopenharmony_ci 36238c2ecf20Sopenharmony_ci return 0; 36248c2ecf20Sopenharmony_ci} 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_cistatic void nfp_intro(struct nfp_prog *nfp_prog) 36278c2ecf20Sopenharmony_ci{ 36288c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, plen_reg(nfp_prog), GENMASK(13, 0)); 36298c2ecf20Sopenharmony_ci emit_alu(nfp_prog, plen_reg(nfp_prog), 36308c2ecf20Sopenharmony_ci plen_reg(nfp_prog), ALU_OP_AND, pv_len(nfp_prog)); 36318c2ecf20Sopenharmony_ci} 36328c2ecf20Sopenharmony_ci 36338c2ecf20Sopenharmony_cistatic void 36348c2ecf20Sopenharmony_cinfp_subprog_prologue(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 36358c2ecf20Sopenharmony_ci{ 36368c2ecf20Sopenharmony_ci /* Save return address into the stack. */ 36378c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_lm(0, 0), ret_reg(nfp_prog)); 36388c2ecf20Sopenharmony_ci} 36398c2ecf20Sopenharmony_ci 36408c2ecf20Sopenharmony_cistatic void 36418c2ecf20Sopenharmony_cinfp_start_subprog(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 36428c2ecf20Sopenharmony_ci{ 36438c2ecf20Sopenharmony_ci unsigned int depth = nfp_prog->subprog[meta->subprog_idx].stack_depth; 36448c2ecf20Sopenharmony_ci 36458c2ecf20Sopenharmony_ci nfp_prog->stack_frame_depth = round_up(depth, 4); 36468c2ecf20Sopenharmony_ci nfp_subprog_prologue(nfp_prog, meta); 36478c2ecf20Sopenharmony_ci} 36488c2ecf20Sopenharmony_ci 36498c2ecf20Sopenharmony_cibool nfp_is_subprog_start(struct nfp_insn_meta *meta) 36508c2ecf20Sopenharmony_ci{ 36518c2ecf20Sopenharmony_ci return meta->flags & FLAG_INSN_IS_SUBPROG_START; 36528c2ecf20Sopenharmony_ci} 36538c2ecf20Sopenharmony_ci 36548c2ecf20Sopenharmony_cistatic void nfp_outro_tc_da(struct nfp_prog *nfp_prog) 36558c2ecf20Sopenharmony_ci{ 36568c2ecf20Sopenharmony_ci /* TC direct-action mode: 36578c2ecf20Sopenharmony_ci * 0,1 ok NOT SUPPORTED[1] 36588c2ecf20Sopenharmony_ci * 2 drop 0x22 -> drop, count as stat1 36598c2ecf20Sopenharmony_ci * 4,5 nuke 0x02 -> drop 36608c2ecf20Sopenharmony_ci * 7 redir 0x44 -> redir, count as stat2 36618c2ecf20Sopenharmony_ci * * unspec 0x11 -> pass, count as stat0 36628c2ecf20Sopenharmony_ci * 36638c2ecf20Sopenharmony_ci * [1] We can't support OK and RECLASSIFY because we can't tell TC 36648c2ecf20Sopenharmony_ci * the exact decision made. We are forced to support UNSPEC 36658c2ecf20Sopenharmony_ci * to handle aborts so that's the only one we handle for passing 36668c2ecf20Sopenharmony_ci * packets up the stack. 36678c2ecf20Sopenharmony_ci */ 36688c2ecf20Sopenharmony_ci /* Target for aborts */ 36698c2ecf20Sopenharmony_ci nfp_prog->tgt_abort = nfp_prog_current_offset(nfp_prog); 36708c2ecf20Sopenharmony_ci 36718c2ecf20Sopenharmony_ci emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO, 2, RELO_BR_NEXT_PKT); 36728c2ecf20Sopenharmony_ci 36738c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_a(0), NFP_BPF_ABI_FLAGS); 36748c2ecf20Sopenharmony_ci emit_ld_field(nfp_prog, reg_a(0), 0xc, reg_imm(0x11), SHF_SC_L_SHF, 16); 36758c2ecf20Sopenharmony_ci 36768c2ecf20Sopenharmony_ci /* Target for normal exits */ 36778c2ecf20Sopenharmony_ci nfp_prog->tgt_out = nfp_prog_current_offset(nfp_prog); 36788c2ecf20Sopenharmony_ci 36798c2ecf20Sopenharmony_ci /* if R0 > 7 jump to abort */ 36808c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_imm(7), ALU_OP_SUB, reg_b(0)); 36818c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BLO, nfp_prog->tgt_abort, 0); 36828c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_a(0), NFP_BPF_ABI_FLAGS); 36838c2ecf20Sopenharmony_ci 36848c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_b(2), 0x41221211); 36858c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_b(3), 0x41001211); 36868c2ecf20Sopenharmony_ci 36878c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_a(1), 36888c2ecf20Sopenharmony_ci reg_none(), SHF_OP_NONE, reg_b(0), SHF_SC_L_SHF, 2); 36898c2ecf20Sopenharmony_ci 36908c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(1), ALU_OP_OR, reg_imm(0)); 36918c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_a(2), 36928c2ecf20Sopenharmony_ci reg_imm(0xf), SHF_OP_AND, reg_b(2), SHF_SC_R_SHF, 0); 36938c2ecf20Sopenharmony_ci 36948c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(1), ALU_OP_OR, reg_imm(0)); 36958c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_b(2), 36968c2ecf20Sopenharmony_ci reg_imm(0xf), SHF_OP_AND, reg_b(3), SHF_SC_R_SHF, 0); 36978c2ecf20Sopenharmony_ci 36988c2ecf20Sopenharmony_ci emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO, 2, RELO_BR_NEXT_PKT); 36998c2ecf20Sopenharmony_ci 37008c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_b(2), 37018c2ecf20Sopenharmony_ci reg_a(2), SHF_OP_OR, reg_b(2), SHF_SC_L_SHF, 4); 37028c2ecf20Sopenharmony_ci emit_ld_field(nfp_prog, reg_a(0), 0xc, reg_b(2), SHF_SC_L_SHF, 16); 37038c2ecf20Sopenharmony_ci} 37048c2ecf20Sopenharmony_ci 37058c2ecf20Sopenharmony_cistatic void nfp_outro_xdp(struct nfp_prog *nfp_prog) 37068c2ecf20Sopenharmony_ci{ 37078c2ecf20Sopenharmony_ci /* XDP return codes: 37088c2ecf20Sopenharmony_ci * 0 aborted 0x82 -> drop, count as stat3 37098c2ecf20Sopenharmony_ci * 1 drop 0x22 -> drop, count as stat1 37108c2ecf20Sopenharmony_ci * 2 pass 0x11 -> pass, count as stat0 37118c2ecf20Sopenharmony_ci * 3 tx 0x44 -> redir, count as stat2 37128c2ecf20Sopenharmony_ci * * unknown 0x82 -> drop, count as stat3 37138c2ecf20Sopenharmony_ci */ 37148c2ecf20Sopenharmony_ci /* Target for aborts */ 37158c2ecf20Sopenharmony_ci nfp_prog->tgt_abort = nfp_prog_current_offset(nfp_prog); 37168c2ecf20Sopenharmony_ci 37178c2ecf20Sopenharmony_ci emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO, 2, RELO_BR_NEXT_PKT); 37188c2ecf20Sopenharmony_ci 37198c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_a(0), NFP_BPF_ABI_FLAGS); 37208c2ecf20Sopenharmony_ci emit_ld_field(nfp_prog, reg_a(0), 0xc, reg_imm(0x82), SHF_SC_L_SHF, 16); 37218c2ecf20Sopenharmony_ci 37228c2ecf20Sopenharmony_ci /* Target for normal exits */ 37238c2ecf20Sopenharmony_ci nfp_prog->tgt_out = nfp_prog_current_offset(nfp_prog); 37248c2ecf20Sopenharmony_ci 37258c2ecf20Sopenharmony_ci /* if R0 > 3 jump to abort */ 37268c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_imm(3), ALU_OP_SUB, reg_b(0)); 37278c2ecf20Sopenharmony_ci emit_br(nfp_prog, BR_BLO, nfp_prog->tgt_abort, 0); 37288c2ecf20Sopenharmony_ci 37298c2ecf20Sopenharmony_ci wrp_immed(nfp_prog, reg_b(2), 0x44112282); 37308c2ecf20Sopenharmony_ci 37318c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_a(1), 37328c2ecf20Sopenharmony_ci reg_none(), SHF_OP_NONE, reg_b(0), SHF_SC_L_SHF, 3); 37338c2ecf20Sopenharmony_ci 37348c2ecf20Sopenharmony_ci emit_alu(nfp_prog, reg_none(), reg_a(1), ALU_OP_OR, reg_imm(0)); 37358c2ecf20Sopenharmony_ci emit_shf(nfp_prog, reg_b(2), 37368c2ecf20Sopenharmony_ci reg_imm(0xff), SHF_OP_AND, reg_b(2), SHF_SC_R_SHF, 0); 37378c2ecf20Sopenharmony_ci 37388c2ecf20Sopenharmony_ci emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO, 2, RELO_BR_NEXT_PKT); 37398c2ecf20Sopenharmony_ci 37408c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_a(0), NFP_BPF_ABI_FLAGS); 37418c2ecf20Sopenharmony_ci emit_ld_field(nfp_prog, reg_a(0), 0xc, reg_b(2), SHF_SC_L_SHF, 16); 37428c2ecf20Sopenharmony_ci} 37438c2ecf20Sopenharmony_ci 37448c2ecf20Sopenharmony_cistatic bool nfp_prog_needs_callee_reg_save(struct nfp_prog *nfp_prog) 37458c2ecf20Sopenharmony_ci{ 37468c2ecf20Sopenharmony_ci unsigned int idx; 37478c2ecf20Sopenharmony_ci 37488c2ecf20Sopenharmony_ci for (idx = 1; idx < nfp_prog->subprog_cnt; idx++) 37498c2ecf20Sopenharmony_ci if (nfp_prog->subprog[idx].needs_reg_push) 37508c2ecf20Sopenharmony_ci return true; 37518c2ecf20Sopenharmony_ci 37528c2ecf20Sopenharmony_ci return false; 37538c2ecf20Sopenharmony_ci} 37548c2ecf20Sopenharmony_ci 37558c2ecf20Sopenharmony_cistatic void nfp_push_callee_registers(struct nfp_prog *nfp_prog) 37568c2ecf20Sopenharmony_ci{ 37578c2ecf20Sopenharmony_ci u8 reg; 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_ci /* Subroutine: Save all callee saved registers (R6 ~ R9). 37608c2ecf20Sopenharmony_ci * imm_b() holds the return address. 37618c2ecf20Sopenharmony_ci */ 37628c2ecf20Sopenharmony_ci nfp_prog->tgt_call_push_regs = nfp_prog_current_offset(nfp_prog); 37638c2ecf20Sopenharmony_ci for (reg = BPF_REG_6; reg <= BPF_REG_9; reg++) { 37648c2ecf20Sopenharmony_ci u8 adj = (reg - BPF_REG_0) * 2; 37658c2ecf20Sopenharmony_ci u8 idx = (reg - BPF_REG_6) * 2; 37668c2ecf20Sopenharmony_ci 37678c2ecf20Sopenharmony_ci /* The first slot in the stack frame is used to push the return 37688c2ecf20Sopenharmony_ci * address in bpf_to_bpf_call(), start just after. 37698c2ecf20Sopenharmony_ci */ 37708c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_lm(0, 1 + idx), reg_b(adj)); 37718c2ecf20Sopenharmony_ci 37728c2ecf20Sopenharmony_ci if (reg == BPF_REG_8) 37738c2ecf20Sopenharmony_ci /* Prepare to jump back, last 3 insns use defer slots */ 37748c2ecf20Sopenharmony_ci emit_rtn(nfp_prog, imm_b(nfp_prog), 3); 37758c2ecf20Sopenharmony_ci 37768c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_lm(0, 1 + idx + 1), reg_b(adj + 1)); 37778c2ecf20Sopenharmony_ci } 37788c2ecf20Sopenharmony_ci} 37798c2ecf20Sopenharmony_ci 37808c2ecf20Sopenharmony_cistatic void nfp_pop_callee_registers(struct nfp_prog *nfp_prog) 37818c2ecf20Sopenharmony_ci{ 37828c2ecf20Sopenharmony_ci u8 reg; 37838c2ecf20Sopenharmony_ci 37848c2ecf20Sopenharmony_ci /* Subroutine: Restore all callee saved registers (R6 ~ R9). 37858c2ecf20Sopenharmony_ci * ret_reg() holds the return address. 37868c2ecf20Sopenharmony_ci */ 37878c2ecf20Sopenharmony_ci nfp_prog->tgt_call_pop_regs = nfp_prog_current_offset(nfp_prog); 37888c2ecf20Sopenharmony_ci for (reg = BPF_REG_6; reg <= BPF_REG_9; reg++) { 37898c2ecf20Sopenharmony_ci u8 adj = (reg - BPF_REG_0) * 2; 37908c2ecf20Sopenharmony_ci u8 idx = (reg - BPF_REG_6) * 2; 37918c2ecf20Sopenharmony_ci 37928c2ecf20Sopenharmony_ci /* The first slot in the stack frame holds the return address, 37938c2ecf20Sopenharmony_ci * start popping just after that. 37948c2ecf20Sopenharmony_ci */ 37958c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_both(adj), reg_lm(0, 1 + idx)); 37968c2ecf20Sopenharmony_ci 37978c2ecf20Sopenharmony_ci if (reg == BPF_REG_8) 37988c2ecf20Sopenharmony_ci /* Prepare to jump back, last 3 insns use defer slots */ 37998c2ecf20Sopenharmony_ci emit_rtn(nfp_prog, ret_reg(nfp_prog), 3); 38008c2ecf20Sopenharmony_ci 38018c2ecf20Sopenharmony_ci wrp_mov(nfp_prog, reg_both(adj + 1), reg_lm(0, 1 + idx + 1)); 38028c2ecf20Sopenharmony_ci } 38038c2ecf20Sopenharmony_ci} 38048c2ecf20Sopenharmony_ci 38058c2ecf20Sopenharmony_cistatic void nfp_outro(struct nfp_prog *nfp_prog) 38068c2ecf20Sopenharmony_ci{ 38078c2ecf20Sopenharmony_ci switch (nfp_prog->type) { 38088c2ecf20Sopenharmony_ci case BPF_PROG_TYPE_SCHED_CLS: 38098c2ecf20Sopenharmony_ci nfp_outro_tc_da(nfp_prog); 38108c2ecf20Sopenharmony_ci break; 38118c2ecf20Sopenharmony_ci case BPF_PROG_TYPE_XDP: 38128c2ecf20Sopenharmony_ci nfp_outro_xdp(nfp_prog); 38138c2ecf20Sopenharmony_ci break; 38148c2ecf20Sopenharmony_ci default: 38158c2ecf20Sopenharmony_ci WARN_ON(1); 38168c2ecf20Sopenharmony_ci } 38178c2ecf20Sopenharmony_ci 38188c2ecf20Sopenharmony_ci if (!nfp_prog_needs_callee_reg_save(nfp_prog)) 38198c2ecf20Sopenharmony_ci return; 38208c2ecf20Sopenharmony_ci 38218c2ecf20Sopenharmony_ci nfp_push_callee_registers(nfp_prog); 38228c2ecf20Sopenharmony_ci nfp_pop_callee_registers(nfp_prog); 38238c2ecf20Sopenharmony_ci} 38248c2ecf20Sopenharmony_ci 38258c2ecf20Sopenharmony_cistatic int nfp_translate(struct nfp_prog *nfp_prog) 38268c2ecf20Sopenharmony_ci{ 38278c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta; 38288c2ecf20Sopenharmony_ci unsigned int depth; 38298c2ecf20Sopenharmony_ci int err; 38308c2ecf20Sopenharmony_ci 38318c2ecf20Sopenharmony_ci depth = nfp_prog->subprog[0].stack_depth; 38328c2ecf20Sopenharmony_ci nfp_prog->stack_frame_depth = round_up(depth, 4); 38338c2ecf20Sopenharmony_ci 38348c2ecf20Sopenharmony_ci nfp_intro(nfp_prog); 38358c2ecf20Sopenharmony_ci if (nfp_prog->error) 38368c2ecf20Sopenharmony_ci return nfp_prog->error; 38378c2ecf20Sopenharmony_ci 38388c2ecf20Sopenharmony_ci list_for_each_entry(meta, &nfp_prog->insns, l) { 38398c2ecf20Sopenharmony_ci instr_cb_t cb = instr_cb[meta->insn.code]; 38408c2ecf20Sopenharmony_ci 38418c2ecf20Sopenharmony_ci meta->off = nfp_prog_current_offset(nfp_prog); 38428c2ecf20Sopenharmony_ci 38438c2ecf20Sopenharmony_ci if (nfp_is_subprog_start(meta)) { 38448c2ecf20Sopenharmony_ci nfp_start_subprog(nfp_prog, meta); 38458c2ecf20Sopenharmony_ci if (nfp_prog->error) 38468c2ecf20Sopenharmony_ci return nfp_prog->error; 38478c2ecf20Sopenharmony_ci } 38488c2ecf20Sopenharmony_ci 38498c2ecf20Sopenharmony_ci if (meta->flags & FLAG_INSN_SKIP_MASK) { 38508c2ecf20Sopenharmony_ci nfp_prog->n_translated++; 38518c2ecf20Sopenharmony_ci continue; 38528c2ecf20Sopenharmony_ci } 38538c2ecf20Sopenharmony_ci 38548c2ecf20Sopenharmony_ci if (nfp_meta_has_prev(nfp_prog, meta) && 38558c2ecf20Sopenharmony_ci nfp_meta_prev(meta)->double_cb) 38568c2ecf20Sopenharmony_ci cb = nfp_meta_prev(meta)->double_cb; 38578c2ecf20Sopenharmony_ci if (!cb) 38588c2ecf20Sopenharmony_ci return -ENOENT; 38598c2ecf20Sopenharmony_ci err = cb(nfp_prog, meta); 38608c2ecf20Sopenharmony_ci if (err) 38618c2ecf20Sopenharmony_ci return err; 38628c2ecf20Sopenharmony_ci if (nfp_prog->error) 38638c2ecf20Sopenharmony_ci return nfp_prog->error; 38648c2ecf20Sopenharmony_ci 38658c2ecf20Sopenharmony_ci nfp_prog->n_translated++; 38668c2ecf20Sopenharmony_ci } 38678c2ecf20Sopenharmony_ci 38688c2ecf20Sopenharmony_ci nfp_prog->last_bpf_off = nfp_prog_current_offset(nfp_prog) - 1; 38698c2ecf20Sopenharmony_ci 38708c2ecf20Sopenharmony_ci nfp_outro(nfp_prog); 38718c2ecf20Sopenharmony_ci if (nfp_prog->error) 38728c2ecf20Sopenharmony_ci return nfp_prog->error; 38738c2ecf20Sopenharmony_ci 38748c2ecf20Sopenharmony_ci wrp_nops(nfp_prog, NFP_USTORE_PREFETCH_WINDOW); 38758c2ecf20Sopenharmony_ci if (nfp_prog->error) 38768c2ecf20Sopenharmony_ci return nfp_prog->error; 38778c2ecf20Sopenharmony_ci 38788c2ecf20Sopenharmony_ci return nfp_fixup_branches(nfp_prog); 38798c2ecf20Sopenharmony_ci} 38808c2ecf20Sopenharmony_ci 38818c2ecf20Sopenharmony_ci/* --- Optimizations --- */ 38828c2ecf20Sopenharmony_cistatic void nfp_bpf_opt_reg_init(struct nfp_prog *nfp_prog) 38838c2ecf20Sopenharmony_ci{ 38848c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta; 38858c2ecf20Sopenharmony_ci 38868c2ecf20Sopenharmony_ci list_for_each_entry(meta, &nfp_prog->insns, l) { 38878c2ecf20Sopenharmony_ci struct bpf_insn insn = meta->insn; 38888c2ecf20Sopenharmony_ci 38898c2ecf20Sopenharmony_ci /* Programs converted from cBPF start with register xoring */ 38908c2ecf20Sopenharmony_ci if (insn.code == (BPF_ALU64 | BPF_XOR | BPF_X) && 38918c2ecf20Sopenharmony_ci insn.src_reg == insn.dst_reg) 38928c2ecf20Sopenharmony_ci continue; 38938c2ecf20Sopenharmony_ci 38948c2ecf20Sopenharmony_ci /* Programs start with R6 = R1 but we ignore the skb pointer */ 38958c2ecf20Sopenharmony_ci if (insn.code == (BPF_ALU64 | BPF_MOV | BPF_X) && 38968c2ecf20Sopenharmony_ci insn.src_reg == 1 && insn.dst_reg == 6) 38978c2ecf20Sopenharmony_ci meta->flags |= FLAG_INSN_SKIP_PREC_DEPENDENT; 38988c2ecf20Sopenharmony_ci 38998c2ecf20Sopenharmony_ci /* Return as soon as something doesn't match */ 39008c2ecf20Sopenharmony_ci if (!(meta->flags & FLAG_INSN_SKIP_MASK)) 39018c2ecf20Sopenharmony_ci return; 39028c2ecf20Sopenharmony_ci } 39038c2ecf20Sopenharmony_ci} 39048c2ecf20Sopenharmony_ci 39058c2ecf20Sopenharmony_ci/* abs(insn.imm) will fit better into unrestricted reg immediate - 39068c2ecf20Sopenharmony_ci * convert add/sub of a negative number into a sub/add of a positive one. 39078c2ecf20Sopenharmony_ci */ 39088c2ecf20Sopenharmony_cistatic void nfp_bpf_opt_neg_add_sub(struct nfp_prog *nfp_prog) 39098c2ecf20Sopenharmony_ci{ 39108c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta; 39118c2ecf20Sopenharmony_ci 39128c2ecf20Sopenharmony_ci list_for_each_entry(meta, &nfp_prog->insns, l) { 39138c2ecf20Sopenharmony_ci struct bpf_insn insn = meta->insn; 39148c2ecf20Sopenharmony_ci 39158c2ecf20Sopenharmony_ci if (meta->flags & FLAG_INSN_SKIP_MASK) 39168c2ecf20Sopenharmony_ci continue; 39178c2ecf20Sopenharmony_ci 39188c2ecf20Sopenharmony_ci if (!is_mbpf_alu(meta) && !is_mbpf_jmp(meta)) 39198c2ecf20Sopenharmony_ci continue; 39208c2ecf20Sopenharmony_ci if (BPF_SRC(insn.code) != BPF_K) 39218c2ecf20Sopenharmony_ci continue; 39228c2ecf20Sopenharmony_ci if (insn.imm >= 0) 39238c2ecf20Sopenharmony_ci continue; 39248c2ecf20Sopenharmony_ci 39258c2ecf20Sopenharmony_ci if (is_mbpf_jmp(meta)) { 39268c2ecf20Sopenharmony_ci switch (BPF_OP(insn.code)) { 39278c2ecf20Sopenharmony_ci case BPF_JGE: 39288c2ecf20Sopenharmony_ci case BPF_JSGE: 39298c2ecf20Sopenharmony_ci case BPF_JLT: 39308c2ecf20Sopenharmony_ci case BPF_JSLT: 39318c2ecf20Sopenharmony_ci meta->jump_neg_op = true; 39328c2ecf20Sopenharmony_ci break; 39338c2ecf20Sopenharmony_ci default: 39348c2ecf20Sopenharmony_ci continue; 39358c2ecf20Sopenharmony_ci } 39368c2ecf20Sopenharmony_ci } else { 39378c2ecf20Sopenharmony_ci if (BPF_OP(insn.code) == BPF_ADD) 39388c2ecf20Sopenharmony_ci insn.code = BPF_CLASS(insn.code) | BPF_SUB; 39398c2ecf20Sopenharmony_ci else if (BPF_OP(insn.code) == BPF_SUB) 39408c2ecf20Sopenharmony_ci insn.code = BPF_CLASS(insn.code) | BPF_ADD; 39418c2ecf20Sopenharmony_ci else 39428c2ecf20Sopenharmony_ci continue; 39438c2ecf20Sopenharmony_ci 39448c2ecf20Sopenharmony_ci meta->insn.code = insn.code | BPF_K; 39458c2ecf20Sopenharmony_ci } 39468c2ecf20Sopenharmony_ci 39478c2ecf20Sopenharmony_ci meta->insn.imm = -insn.imm; 39488c2ecf20Sopenharmony_ci } 39498c2ecf20Sopenharmony_ci} 39508c2ecf20Sopenharmony_ci 39518c2ecf20Sopenharmony_ci/* Remove masking after load since our load guarantees this is not needed */ 39528c2ecf20Sopenharmony_cistatic void nfp_bpf_opt_ld_mask(struct nfp_prog *nfp_prog) 39538c2ecf20Sopenharmony_ci{ 39548c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta1, *meta2; 39558c2ecf20Sopenharmony_ci static const s32 exp_mask[] = { 39568c2ecf20Sopenharmony_ci [BPF_B] = 0x000000ffU, 39578c2ecf20Sopenharmony_ci [BPF_H] = 0x0000ffffU, 39588c2ecf20Sopenharmony_ci [BPF_W] = 0xffffffffU, 39598c2ecf20Sopenharmony_ci }; 39608c2ecf20Sopenharmony_ci 39618c2ecf20Sopenharmony_ci nfp_for_each_insn_walk2(nfp_prog, meta1, meta2) { 39628c2ecf20Sopenharmony_ci struct bpf_insn insn, next; 39638c2ecf20Sopenharmony_ci 39648c2ecf20Sopenharmony_ci insn = meta1->insn; 39658c2ecf20Sopenharmony_ci next = meta2->insn; 39668c2ecf20Sopenharmony_ci 39678c2ecf20Sopenharmony_ci if (BPF_CLASS(insn.code) != BPF_LD) 39688c2ecf20Sopenharmony_ci continue; 39698c2ecf20Sopenharmony_ci if (BPF_MODE(insn.code) != BPF_ABS && 39708c2ecf20Sopenharmony_ci BPF_MODE(insn.code) != BPF_IND) 39718c2ecf20Sopenharmony_ci continue; 39728c2ecf20Sopenharmony_ci 39738c2ecf20Sopenharmony_ci if (next.code != (BPF_ALU64 | BPF_AND | BPF_K)) 39748c2ecf20Sopenharmony_ci continue; 39758c2ecf20Sopenharmony_ci 39768c2ecf20Sopenharmony_ci if (!exp_mask[BPF_SIZE(insn.code)]) 39778c2ecf20Sopenharmony_ci continue; 39788c2ecf20Sopenharmony_ci if (exp_mask[BPF_SIZE(insn.code)] != next.imm) 39798c2ecf20Sopenharmony_ci continue; 39808c2ecf20Sopenharmony_ci 39818c2ecf20Sopenharmony_ci if (next.src_reg || next.dst_reg) 39828c2ecf20Sopenharmony_ci continue; 39838c2ecf20Sopenharmony_ci 39848c2ecf20Sopenharmony_ci if (meta2->flags & FLAG_INSN_IS_JUMP_DST) 39858c2ecf20Sopenharmony_ci continue; 39868c2ecf20Sopenharmony_ci 39878c2ecf20Sopenharmony_ci meta2->flags |= FLAG_INSN_SKIP_PREC_DEPENDENT; 39888c2ecf20Sopenharmony_ci } 39898c2ecf20Sopenharmony_ci} 39908c2ecf20Sopenharmony_ci 39918c2ecf20Sopenharmony_cistatic void nfp_bpf_opt_ld_shift(struct nfp_prog *nfp_prog) 39928c2ecf20Sopenharmony_ci{ 39938c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta1, *meta2, *meta3; 39948c2ecf20Sopenharmony_ci 39958c2ecf20Sopenharmony_ci nfp_for_each_insn_walk3(nfp_prog, meta1, meta2, meta3) { 39968c2ecf20Sopenharmony_ci struct bpf_insn insn, next1, next2; 39978c2ecf20Sopenharmony_ci 39988c2ecf20Sopenharmony_ci insn = meta1->insn; 39998c2ecf20Sopenharmony_ci next1 = meta2->insn; 40008c2ecf20Sopenharmony_ci next2 = meta3->insn; 40018c2ecf20Sopenharmony_ci 40028c2ecf20Sopenharmony_ci if (BPF_CLASS(insn.code) != BPF_LD) 40038c2ecf20Sopenharmony_ci continue; 40048c2ecf20Sopenharmony_ci if (BPF_MODE(insn.code) != BPF_ABS && 40058c2ecf20Sopenharmony_ci BPF_MODE(insn.code) != BPF_IND) 40068c2ecf20Sopenharmony_ci continue; 40078c2ecf20Sopenharmony_ci if (BPF_SIZE(insn.code) != BPF_W) 40088c2ecf20Sopenharmony_ci continue; 40098c2ecf20Sopenharmony_ci 40108c2ecf20Sopenharmony_ci if (!(next1.code == (BPF_LSH | BPF_K | BPF_ALU64) && 40118c2ecf20Sopenharmony_ci next2.code == (BPF_RSH | BPF_K | BPF_ALU64)) && 40128c2ecf20Sopenharmony_ci !(next1.code == (BPF_RSH | BPF_K | BPF_ALU64) && 40138c2ecf20Sopenharmony_ci next2.code == (BPF_LSH | BPF_K | BPF_ALU64))) 40148c2ecf20Sopenharmony_ci continue; 40158c2ecf20Sopenharmony_ci 40168c2ecf20Sopenharmony_ci if (next1.src_reg || next1.dst_reg || 40178c2ecf20Sopenharmony_ci next2.src_reg || next2.dst_reg) 40188c2ecf20Sopenharmony_ci continue; 40198c2ecf20Sopenharmony_ci 40208c2ecf20Sopenharmony_ci if (next1.imm != 0x20 || next2.imm != 0x20) 40218c2ecf20Sopenharmony_ci continue; 40228c2ecf20Sopenharmony_ci 40238c2ecf20Sopenharmony_ci if (meta2->flags & FLAG_INSN_IS_JUMP_DST || 40248c2ecf20Sopenharmony_ci meta3->flags & FLAG_INSN_IS_JUMP_DST) 40258c2ecf20Sopenharmony_ci continue; 40268c2ecf20Sopenharmony_ci 40278c2ecf20Sopenharmony_ci meta2->flags |= FLAG_INSN_SKIP_PREC_DEPENDENT; 40288c2ecf20Sopenharmony_ci meta3->flags |= FLAG_INSN_SKIP_PREC_DEPENDENT; 40298c2ecf20Sopenharmony_ci } 40308c2ecf20Sopenharmony_ci} 40318c2ecf20Sopenharmony_ci 40328c2ecf20Sopenharmony_ci/* load/store pair that forms memory copy sould look like the following: 40338c2ecf20Sopenharmony_ci * 40348c2ecf20Sopenharmony_ci * ld_width R, [addr_src + offset_src] 40358c2ecf20Sopenharmony_ci * st_width [addr_dest + offset_dest], R 40368c2ecf20Sopenharmony_ci * 40378c2ecf20Sopenharmony_ci * The destination register of load and source register of store should 40388c2ecf20Sopenharmony_ci * be the same, load and store should also perform at the same width. 40398c2ecf20Sopenharmony_ci * If either of addr_src or addr_dest is stack pointer, we don't do the 40408c2ecf20Sopenharmony_ci * CPP optimization as stack is modelled by registers on NFP. 40418c2ecf20Sopenharmony_ci */ 40428c2ecf20Sopenharmony_cistatic bool 40438c2ecf20Sopenharmony_cicurr_pair_is_memcpy(struct nfp_insn_meta *ld_meta, 40448c2ecf20Sopenharmony_ci struct nfp_insn_meta *st_meta) 40458c2ecf20Sopenharmony_ci{ 40468c2ecf20Sopenharmony_ci struct bpf_insn *ld = &ld_meta->insn; 40478c2ecf20Sopenharmony_ci struct bpf_insn *st = &st_meta->insn; 40488c2ecf20Sopenharmony_ci 40498c2ecf20Sopenharmony_ci if (!is_mbpf_load(ld_meta) || !is_mbpf_store(st_meta)) 40508c2ecf20Sopenharmony_ci return false; 40518c2ecf20Sopenharmony_ci 40528c2ecf20Sopenharmony_ci if (ld_meta->ptr.type != PTR_TO_PACKET && 40538c2ecf20Sopenharmony_ci ld_meta->ptr.type != PTR_TO_MAP_VALUE) 40548c2ecf20Sopenharmony_ci return false; 40558c2ecf20Sopenharmony_ci 40568c2ecf20Sopenharmony_ci if (st_meta->ptr.type != PTR_TO_PACKET) 40578c2ecf20Sopenharmony_ci return false; 40588c2ecf20Sopenharmony_ci 40598c2ecf20Sopenharmony_ci if (BPF_SIZE(ld->code) != BPF_SIZE(st->code)) 40608c2ecf20Sopenharmony_ci return false; 40618c2ecf20Sopenharmony_ci 40628c2ecf20Sopenharmony_ci if (ld->dst_reg != st->src_reg) 40638c2ecf20Sopenharmony_ci return false; 40648c2ecf20Sopenharmony_ci 40658c2ecf20Sopenharmony_ci /* There is jump to the store insn in this pair. */ 40668c2ecf20Sopenharmony_ci if (st_meta->flags & FLAG_INSN_IS_JUMP_DST) 40678c2ecf20Sopenharmony_ci return false; 40688c2ecf20Sopenharmony_ci 40698c2ecf20Sopenharmony_ci return true; 40708c2ecf20Sopenharmony_ci} 40718c2ecf20Sopenharmony_ci 40728c2ecf20Sopenharmony_ci/* Currently, we only support chaining load/store pairs if: 40738c2ecf20Sopenharmony_ci * 40748c2ecf20Sopenharmony_ci * - Their address base registers are the same. 40758c2ecf20Sopenharmony_ci * - Their address offsets are in the same order. 40768c2ecf20Sopenharmony_ci * - They operate at the same memory width. 40778c2ecf20Sopenharmony_ci * - There is no jump into the middle of them. 40788c2ecf20Sopenharmony_ci */ 40798c2ecf20Sopenharmony_cistatic bool 40808c2ecf20Sopenharmony_cicurr_pair_chain_with_previous(struct nfp_insn_meta *ld_meta, 40818c2ecf20Sopenharmony_ci struct nfp_insn_meta *st_meta, 40828c2ecf20Sopenharmony_ci struct bpf_insn *prev_ld, 40838c2ecf20Sopenharmony_ci struct bpf_insn *prev_st) 40848c2ecf20Sopenharmony_ci{ 40858c2ecf20Sopenharmony_ci u8 prev_size, curr_size, prev_ld_base, prev_st_base, prev_ld_dst; 40868c2ecf20Sopenharmony_ci struct bpf_insn *ld = &ld_meta->insn; 40878c2ecf20Sopenharmony_ci struct bpf_insn *st = &st_meta->insn; 40888c2ecf20Sopenharmony_ci s16 prev_ld_off, prev_st_off; 40898c2ecf20Sopenharmony_ci 40908c2ecf20Sopenharmony_ci /* This pair is the start pair. */ 40918c2ecf20Sopenharmony_ci if (!prev_ld) 40928c2ecf20Sopenharmony_ci return true; 40938c2ecf20Sopenharmony_ci 40948c2ecf20Sopenharmony_ci prev_size = BPF_LDST_BYTES(prev_ld); 40958c2ecf20Sopenharmony_ci curr_size = BPF_LDST_BYTES(ld); 40968c2ecf20Sopenharmony_ci prev_ld_base = prev_ld->src_reg; 40978c2ecf20Sopenharmony_ci prev_st_base = prev_st->dst_reg; 40988c2ecf20Sopenharmony_ci prev_ld_dst = prev_ld->dst_reg; 40998c2ecf20Sopenharmony_ci prev_ld_off = prev_ld->off; 41008c2ecf20Sopenharmony_ci prev_st_off = prev_st->off; 41018c2ecf20Sopenharmony_ci 41028c2ecf20Sopenharmony_ci if (ld->dst_reg != prev_ld_dst) 41038c2ecf20Sopenharmony_ci return false; 41048c2ecf20Sopenharmony_ci 41058c2ecf20Sopenharmony_ci if (ld->src_reg != prev_ld_base || st->dst_reg != prev_st_base) 41068c2ecf20Sopenharmony_ci return false; 41078c2ecf20Sopenharmony_ci 41088c2ecf20Sopenharmony_ci if (curr_size != prev_size) 41098c2ecf20Sopenharmony_ci return false; 41108c2ecf20Sopenharmony_ci 41118c2ecf20Sopenharmony_ci /* There is jump to the head of this pair. */ 41128c2ecf20Sopenharmony_ci if (ld_meta->flags & FLAG_INSN_IS_JUMP_DST) 41138c2ecf20Sopenharmony_ci return false; 41148c2ecf20Sopenharmony_ci 41158c2ecf20Sopenharmony_ci /* Both in ascending order. */ 41168c2ecf20Sopenharmony_ci if (prev_ld_off + prev_size == ld->off && 41178c2ecf20Sopenharmony_ci prev_st_off + prev_size == st->off) 41188c2ecf20Sopenharmony_ci return true; 41198c2ecf20Sopenharmony_ci 41208c2ecf20Sopenharmony_ci /* Both in descending order. */ 41218c2ecf20Sopenharmony_ci if (ld->off + curr_size == prev_ld_off && 41228c2ecf20Sopenharmony_ci st->off + curr_size == prev_st_off) 41238c2ecf20Sopenharmony_ci return true; 41248c2ecf20Sopenharmony_ci 41258c2ecf20Sopenharmony_ci return false; 41268c2ecf20Sopenharmony_ci} 41278c2ecf20Sopenharmony_ci 41288c2ecf20Sopenharmony_ci/* Return TRUE if cross memory access happens. Cross memory access means 41298c2ecf20Sopenharmony_ci * store area is overlapping with load area that a later load might load 41308c2ecf20Sopenharmony_ci * the value from previous store, for this case we can't treat the sequence 41318c2ecf20Sopenharmony_ci * as an memory copy. 41328c2ecf20Sopenharmony_ci */ 41338c2ecf20Sopenharmony_cistatic bool 41348c2ecf20Sopenharmony_cicross_mem_access(struct bpf_insn *ld, struct nfp_insn_meta *head_ld_meta, 41358c2ecf20Sopenharmony_ci struct nfp_insn_meta *head_st_meta) 41368c2ecf20Sopenharmony_ci{ 41378c2ecf20Sopenharmony_ci s16 head_ld_off, head_st_off, ld_off; 41388c2ecf20Sopenharmony_ci 41398c2ecf20Sopenharmony_ci /* Different pointer types does not overlap. */ 41408c2ecf20Sopenharmony_ci if (head_ld_meta->ptr.type != head_st_meta->ptr.type) 41418c2ecf20Sopenharmony_ci return false; 41428c2ecf20Sopenharmony_ci 41438c2ecf20Sopenharmony_ci /* load and store are both PTR_TO_PACKET, check ID info. */ 41448c2ecf20Sopenharmony_ci if (head_ld_meta->ptr.id != head_st_meta->ptr.id) 41458c2ecf20Sopenharmony_ci return true; 41468c2ecf20Sopenharmony_ci 41478c2ecf20Sopenharmony_ci /* Canonicalize the offsets. Turn all of them against the original 41488c2ecf20Sopenharmony_ci * base register. 41498c2ecf20Sopenharmony_ci */ 41508c2ecf20Sopenharmony_ci head_ld_off = head_ld_meta->insn.off + head_ld_meta->ptr.off; 41518c2ecf20Sopenharmony_ci head_st_off = head_st_meta->insn.off + head_st_meta->ptr.off; 41528c2ecf20Sopenharmony_ci ld_off = ld->off + head_ld_meta->ptr.off; 41538c2ecf20Sopenharmony_ci 41548c2ecf20Sopenharmony_ci /* Ascending order cross. */ 41558c2ecf20Sopenharmony_ci if (ld_off > head_ld_off && 41568c2ecf20Sopenharmony_ci head_ld_off < head_st_off && ld_off >= head_st_off) 41578c2ecf20Sopenharmony_ci return true; 41588c2ecf20Sopenharmony_ci 41598c2ecf20Sopenharmony_ci /* Descending order cross. */ 41608c2ecf20Sopenharmony_ci if (ld_off < head_ld_off && 41618c2ecf20Sopenharmony_ci head_ld_off > head_st_off && ld_off <= head_st_off) 41628c2ecf20Sopenharmony_ci return true; 41638c2ecf20Sopenharmony_ci 41648c2ecf20Sopenharmony_ci return false; 41658c2ecf20Sopenharmony_ci} 41668c2ecf20Sopenharmony_ci 41678c2ecf20Sopenharmony_ci/* This pass try to identify the following instructoin sequences. 41688c2ecf20Sopenharmony_ci * 41698c2ecf20Sopenharmony_ci * load R, [regA + offA] 41708c2ecf20Sopenharmony_ci * store [regB + offB], R 41718c2ecf20Sopenharmony_ci * load R, [regA + offA + const_imm_A] 41728c2ecf20Sopenharmony_ci * store [regB + offB + const_imm_A], R 41738c2ecf20Sopenharmony_ci * load R, [regA + offA + 2 * const_imm_A] 41748c2ecf20Sopenharmony_ci * store [regB + offB + 2 * const_imm_A], R 41758c2ecf20Sopenharmony_ci * ... 41768c2ecf20Sopenharmony_ci * 41778c2ecf20Sopenharmony_ci * Above sequence is typically generated by compiler when lowering 41788c2ecf20Sopenharmony_ci * memcpy. NFP prefer using CPP instructions to accelerate it. 41798c2ecf20Sopenharmony_ci */ 41808c2ecf20Sopenharmony_cistatic void nfp_bpf_opt_ldst_gather(struct nfp_prog *nfp_prog) 41818c2ecf20Sopenharmony_ci{ 41828c2ecf20Sopenharmony_ci struct nfp_insn_meta *head_ld_meta = NULL; 41838c2ecf20Sopenharmony_ci struct nfp_insn_meta *head_st_meta = NULL; 41848c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta1, *meta2; 41858c2ecf20Sopenharmony_ci struct bpf_insn *prev_ld = NULL; 41868c2ecf20Sopenharmony_ci struct bpf_insn *prev_st = NULL; 41878c2ecf20Sopenharmony_ci u8 count = 0; 41888c2ecf20Sopenharmony_ci 41898c2ecf20Sopenharmony_ci nfp_for_each_insn_walk2(nfp_prog, meta1, meta2) { 41908c2ecf20Sopenharmony_ci struct bpf_insn *ld = &meta1->insn; 41918c2ecf20Sopenharmony_ci struct bpf_insn *st = &meta2->insn; 41928c2ecf20Sopenharmony_ci 41938c2ecf20Sopenharmony_ci /* Reset record status if any of the following if true: 41948c2ecf20Sopenharmony_ci * - The current insn pair is not load/store. 41958c2ecf20Sopenharmony_ci * - The load/store pair doesn't chain with previous one. 41968c2ecf20Sopenharmony_ci * - The chained load/store pair crossed with previous pair. 41978c2ecf20Sopenharmony_ci * - The chained load/store pair has a total size of memory 41988c2ecf20Sopenharmony_ci * copy beyond 128 bytes which is the maximum length a 41998c2ecf20Sopenharmony_ci * single NFP CPP command can transfer. 42008c2ecf20Sopenharmony_ci */ 42018c2ecf20Sopenharmony_ci if (!curr_pair_is_memcpy(meta1, meta2) || 42028c2ecf20Sopenharmony_ci !curr_pair_chain_with_previous(meta1, meta2, prev_ld, 42038c2ecf20Sopenharmony_ci prev_st) || 42048c2ecf20Sopenharmony_ci (head_ld_meta && (cross_mem_access(ld, head_ld_meta, 42058c2ecf20Sopenharmony_ci head_st_meta) || 42068c2ecf20Sopenharmony_ci head_ld_meta->ldst_gather_len >= 128))) { 42078c2ecf20Sopenharmony_ci if (!count) 42088c2ecf20Sopenharmony_ci continue; 42098c2ecf20Sopenharmony_ci 42108c2ecf20Sopenharmony_ci if (count > 1) { 42118c2ecf20Sopenharmony_ci s16 prev_ld_off = prev_ld->off; 42128c2ecf20Sopenharmony_ci s16 prev_st_off = prev_st->off; 42138c2ecf20Sopenharmony_ci s16 head_ld_off = head_ld_meta->insn.off; 42148c2ecf20Sopenharmony_ci 42158c2ecf20Sopenharmony_ci if (prev_ld_off < head_ld_off) { 42168c2ecf20Sopenharmony_ci head_ld_meta->insn.off = prev_ld_off; 42178c2ecf20Sopenharmony_ci head_st_meta->insn.off = prev_st_off; 42188c2ecf20Sopenharmony_ci head_ld_meta->ldst_gather_len = 42198c2ecf20Sopenharmony_ci -head_ld_meta->ldst_gather_len; 42208c2ecf20Sopenharmony_ci } 42218c2ecf20Sopenharmony_ci 42228c2ecf20Sopenharmony_ci head_ld_meta->paired_st = &head_st_meta->insn; 42238c2ecf20Sopenharmony_ci head_st_meta->flags |= 42248c2ecf20Sopenharmony_ci FLAG_INSN_SKIP_PREC_DEPENDENT; 42258c2ecf20Sopenharmony_ci } else { 42268c2ecf20Sopenharmony_ci head_ld_meta->ldst_gather_len = 0; 42278c2ecf20Sopenharmony_ci } 42288c2ecf20Sopenharmony_ci 42298c2ecf20Sopenharmony_ci /* If the chain is ended by an load/store pair then this 42308c2ecf20Sopenharmony_ci * could serve as the new head of the the next chain. 42318c2ecf20Sopenharmony_ci */ 42328c2ecf20Sopenharmony_ci if (curr_pair_is_memcpy(meta1, meta2)) { 42338c2ecf20Sopenharmony_ci head_ld_meta = meta1; 42348c2ecf20Sopenharmony_ci head_st_meta = meta2; 42358c2ecf20Sopenharmony_ci head_ld_meta->ldst_gather_len = 42368c2ecf20Sopenharmony_ci BPF_LDST_BYTES(ld); 42378c2ecf20Sopenharmony_ci meta1 = nfp_meta_next(meta1); 42388c2ecf20Sopenharmony_ci meta2 = nfp_meta_next(meta2); 42398c2ecf20Sopenharmony_ci prev_ld = ld; 42408c2ecf20Sopenharmony_ci prev_st = st; 42418c2ecf20Sopenharmony_ci count = 1; 42428c2ecf20Sopenharmony_ci } else { 42438c2ecf20Sopenharmony_ci head_ld_meta = NULL; 42448c2ecf20Sopenharmony_ci head_st_meta = NULL; 42458c2ecf20Sopenharmony_ci prev_ld = NULL; 42468c2ecf20Sopenharmony_ci prev_st = NULL; 42478c2ecf20Sopenharmony_ci count = 0; 42488c2ecf20Sopenharmony_ci } 42498c2ecf20Sopenharmony_ci 42508c2ecf20Sopenharmony_ci continue; 42518c2ecf20Sopenharmony_ci } 42528c2ecf20Sopenharmony_ci 42538c2ecf20Sopenharmony_ci if (!head_ld_meta) { 42548c2ecf20Sopenharmony_ci head_ld_meta = meta1; 42558c2ecf20Sopenharmony_ci head_st_meta = meta2; 42568c2ecf20Sopenharmony_ci } else { 42578c2ecf20Sopenharmony_ci meta1->flags |= FLAG_INSN_SKIP_PREC_DEPENDENT; 42588c2ecf20Sopenharmony_ci meta2->flags |= FLAG_INSN_SKIP_PREC_DEPENDENT; 42598c2ecf20Sopenharmony_ci } 42608c2ecf20Sopenharmony_ci 42618c2ecf20Sopenharmony_ci head_ld_meta->ldst_gather_len += BPF_LDST_BYTES(ld); 42628c2ecf20Sopenharmony_ci meta1 = nfp_meta_next(meta1); 42638c2ecf20Sopenharmony_ci meta2 = nfp_meta_next(meta2); 42648c2ecf20Sopenharmony_ci prev_ld = ld; 42658c2ecf20Sopenharmony_ci prev_st = st; 42668c2ecf20Sopenharmony_ci count++; 42678c2ecf20Sopenharmony_ci } 42688c2ecf20Sopenharmony_ci} 42698c2ecf20Sopenharmony_ci 42708c2ecf20Sopenharmony_cistatic void nfp_bpf_opt_pkt_cache(struct nfp_prog *nfp_prog) 42718c2ecf20Sopenharmony_ci{ 42728c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta, *range_node = NULL; 42738c2ecf20Sopenharmony_ci s16 range_start = 0, range_end = 0; 42748c2ecf20Sopenharmony_ci bool cache_avail = false; 42758c2ecf20Sopenharmony_ci struct bpf_insn *insn; 42768c2ecf20Sopenharmony_ci s32 range_ptr_off = 0; 42778c2ecf20Sopenharmony_ci u32 range_ptr_id = 0; 42788c2ecf20Sopenharmony_ci 42798c2ecf20Sopenharmony_ci list_for_each_entry(meta, &nfp_prog->insns, l) { 42808c2ecf20Sopenharmony_ci if (meta->flags & FLAG_INSN_IS_JUMP_DST) 42818c2ecf20Sopenharmony_ci cache_avail = false; 42828c2ecf20Sopenharmony_ci 42838c2ecf20Sopenharmony_ci if (meta->flags & FLAG_INSN_SKIP_MASK) 42848c2ecf20Sopenharmony_ci continue; 42858c2ecf20Sopenharmony_ci 42868c2ecf20Sopenharmony_ci insn = &meta->insn; 42878c2ecf20Sopenharmony_ci 42888c2ecf20Sopenharmony_ci if (is_mbpf_store_pkt(meta) || 42898c2ecf20Sopenharmony_ci insn->code == (BPF_JMP | BPF_CALL) || 42908c2ecf20Sopenharmony_ci is_mbpf_classic_store_pkt(meta) || 42918c2ecf20Sopenharmony_ci is_mbpf_classic_load(meta)) { 42928c2ecf20Sopenharmony_ci cache_avail = false; 42938c2ecf20Sopenharmony_ci continue; 42948c2ecf20Sopenharmony_ci } 42958c2ecf20Sopenharmony_ci 42968c2ecf20Sopenharmony_ci if (!is_mbpf_load(meta)) 42978c2ecf20Sopenharmony_ci continue; 42988c2ecf20Sopenharmony_ci 42998c2ecf20Sopenharmony_ci if (meta->ptr.type != PTR_TO_PACKET || meta->ldst_gather_len) { 43008c2ecf20Sopenharmony_ci cache_avail = false; 43018c2ecf20Sopenharmony_ci continue; 43028c2ecf20Sopenharmony_ci } 43038c2ecf20Sopenharmony_ci 43048c2ecf20Sopenharmony_ci if (!cache_avail) { 43058c2ecf20Sopenharmony_ci cache_avail = true; 43068c2ecf20Sopenharmony_ci if (range_node) 43078c2ecf20Sopenharmony_ci goto end_current_then_start_new; 43088c2ecf20Sopenharmony_ci goto start_new; 43098c2ecf20Sopenharmony_ci } 43108c2ecf20Sopenharmony_ci 43118c2ecf20Sopenharmony_ci /* Check ID to make sure two reads share the same 43128c2ecf20Sopenharmony_ci * variable offset against PTR_TO_PACKET, and check OFF 43138c2ecf20Sopenharmony_ci * to make sure they also share the same constant 43148c2ecf20Sopenharmony_ci * offset. 43158c2ecf20Sopenharmony_ci * 43168c2ecf20Sopenharmony_ci * OFFs don't really need to be the same, because they 43178c2ecf20Sopenharmony_ci * are the constant offsets against PTR_TO_PACKET, so 43188c2ecf20Sopenharmony_ci * for different OFFs, we could canonicalize them to 43198c2ecf20Sopenharmony_ci * offsets against original packet pointer. We don't 43208c2ecf20Sopenharmony_ci * support this. 43218c2ecf20Sopenharmony_ci */ 43228c2ecf20Sopenharmony_ci if (meta->ptr.id == range_ptr_id && 43238c2ecf20Sopenharmony_ci meta->ptr.off == range_ptr_off) { 43248c2ecf20Sopenharmony_ci s16 new_start = range_start; 43258c2ecf20Sopenharmony_ci s16 end, off = insn->off; 43268c2ecf20Sopenharmony_ci s16 new_end = range_end; 43278c2ecf20Sopenharmony_ci bool changed = false; 43288c2ecf20Sopenharmony_ci 43298c2ecf20Sopenharmony_ci if (off < range_start) { 43308c2ecf20Sopenharmony_ci new_start = off; 43318c2ecf20Sopenharmony_ci changed = true; 43328c2ecf20Sopenharmony_ci } 43338c2ecf20Sopenharmony_ci 43348c2ecf20Sopenharmony_ci end = off + BPF_LDST_BYTES(insn); 43358c2ecf20Sopenharmony_ci if (end > range_end) { 43368c2ecf20Sopenharmony_ci new_end = end; 43378c2ecf20Sopenharmony_ci changed = true; 43388c2ecf20Sopenharmony_ci } 43398c2ecf20Sopenharmony_ci 43408c2ecf20Sopenharmony_ci if (!changed) 43418c2ecf20Sopenharmony_ci continue; 43428c2ecf20Sopenharmony_ci 43438c2ecf20Sopenharmony_ci if (new_end - new_start <= 64) { 43448c2ecf20Sopenharmony_ci /* Install new range. */ 43458c2ecf20Sopenharmony_ci range_start = new_start; 43468c2ecf20Sopenharmony_ci range_end = new_end; 43478c2ecf20Sopenharmony_ci continue; 43488c2ecf20Sopenharmony_ci } 43498c2ecf20Sopenharmony_ci } 43508c2ecf20Sopenharmony_ci 43518c2ecf20Sopenharmony_ciend_current_then_start_new: 43528c2ecf20Sopenharmony_ci range_node->pkt_cache.range_start = range_start; 43538c2ecf20Sopenharmony_ci range_node->pkt_cache.range_end = range_end; 43548c2ecf20Sopenharmony_cistart_new: 43558c2ecf20Sopenharmony_ci range_node = meta; 43568c2ecf20Sopenharmony_ci range_node->pkt_cache.do_init = true; 43578c2ecf20Sopenharmony_ci range_ptr_id = range_node->ptr.id; 43588c2ecf20Sopenharmony_ci range_ptr_off = range_node->ptr.off; 43598c2ecf20Sopenharmony_ci range_start = insn->off; 43608c2ecf20Sopenharmony_ci range_end = insn->off + BPF_LDST_BYTES(insn); 43618c2ecf20Sopenharmony_ci } 43628c2ecf20Sopenharmony_ci 43638c2ecf20Sopenharmony_ci if (range_node) { 43648c2ecf20Sopenharmony_ci range_node->pkt_cache.range_start = range_start; 43658c2ecf20Sopenharmony_ci range_node->pkt_cache.range_end = range_end; 43668c2ecf20Sopenharmony_ci } 43678c2ecf20Sopenharmony_ci 43688c2ecf20Sopenharmony_ci list_for_each_entry(meta, &nfp_prog->insns, l) { 43698c2ecf20Sopenharmony_ci if (meta->flags & FLAG_INSN_SKIP_MASK) 43708c2ecf20Sopenharmony_ci continue; 43718c2ecf20Sopenharmony_ci 43728c2ecf20Sopenharmony_ci if (is_mbpf_load_pkt(meta) && !meta->ldst_gather_len) { 43738c2ecf20Sopenharmony_ci if (meta->pkt_cache.do_init) { 43748c2ecf20Sopenharmony_ci range_start = meta->pkt_cache.range_start; 43758c2ecf20Sopenharmony_ci range_end = meta->pkt_cache.range_end; 43768c2ecf20Sopenharmony_ci } else { 43778c2ecf20Sopenharmony_ci meta->pkt_cache.range_start = range_start; 43788c2ecf20Sopenharmony_ci meta->pkt_cache.range_end = range_end; 43798c2ecf20Sopenharmony_ci } 43808c2ecf20Sopenharmony_ci } 43818c2ecf20Sopenharmony_ci } 43828c2ecf20Sopenharmony_ci} 43838c2ecf20Sopenharmony_ci 43848c2ecf20Sopenharmony_cistatic int nfp_bpf_optimize(struct nfp_prog *nfp_prog) 43858c2ecf20Sopenharmony_ci{ 43868c2ecf20Sopenharmony_ci nfp_bpf_opt_reg_init(nfp_prog); 43878c2ecf20Sopenharmony_ci 43888c2ecf20Sopenharmony_ci nfp_bpf_opt_neg_add_sub(nfp_prog); 43898c2ecf20Sopenharmony_ci nfp_bpf_opt_ld_mask(nfp_prog); 43908c2ecf20Sopenharmony_ci nfp_bpf_opt_ld_shift(nfp_prog); 43918c2ecf20Sopenharmony_ci nfp_bpf_opt_ldst_gather(nfp_prog); 43928c2ecf20Sopenharmony_ci nfp_bpf_opt_pkt_cache(nfp_prog); 43938c2ecf20Sopenharmony_ci 43948c2ecf20Sopenharmony_ci return 0; 43958c2ecf20Sopenharmony_ci} 43968c2ecf20Sopenharmony_ci 43978c2ecf20Sopenharmony_cistatic int nfp_bpf_replace_map_ptrs(struct nfp_prog *nfp_prog) 43988c2ecf20Sopenharmony_ci{ 43998c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta1, *meta2; 44008c2ecf20Sopenharmony_ci struct nfp_bpf_map *nfp_map; 44018c2ecf20Sopenharmony_ci struct bpf_map *map; 44028c2ecf20Sopenharmony_ci u32 id; 44038c2ecf20Sopenharmony_ci 44048c2ecf20Sopenharmony_ci nfp_for_each_insn_walk2(nfp_prog, meta1, meta2) { 44058c2ecf20Sopenharmony_ci if (meta1->flags & FLAG_INSN_SKIP_MASK || 44068c2ecf20Sopenharmony_ci meta2->flags & FLAG_INSN_SKIP_MASK) 44078c2ecf20Sopenharmony_ci continue; 44088c2ecf20Sopenharmony_ci 44098c2ecf20Sopenharmony_ci if (meta1->insn.code != (BPF_LD | BPF_IMM | BPF_DW) || 44108c2ecf20Sopenharmony_ci meta1->insn.src_reg != BPF_PSEUDO_MAP_FD) 44118c2ecf20Sopenharmony_ci continue; 44128c2ecf20Sopenharmony_ci 44138c2ecf20Sopenharmony_ci map = (void *)(unsigned long)((u32)meta1->insn.imm | 44148c2ecf20Sopenharmony_ci (u64)meta2->insn.imm << 32); 44158c2ecf20Sopenharmony_ci if (bpf_map_offload_neutral(map)) { 44168c2ecf20Sopenharmony_ci id = map->id; 44178c2ecf20Sopenharmony_ci } else { 44188c2ecf20Sopenharmony_ci nfp_map = map_to_offmap(map)->dev_priv; 44198c2ecf20Sopenharmony_ci id = nfp_map->tid; 44208c2ecf20Sopenharmony_ci } 44218c2ecf20Sopenharmony_ci 44228c2ecf20Sopenharmony_ci meta1->insn.imm = id; 44238c2ecf20Sopenharmony_ci meta2->insn.imm = 0; 44248c2ecf20Sopenharmony_ci } 44258c2ecf20Sopenharmony_ci 44268c2ecf20Sopenharmony_ci return 0; 44278c2ecf20Sopenharmony_ci} 44288c2ecf20Sopenharmony_ci 44298c2ecf20Sopenharmony_cistatic int nfp_bpf_ustore_calc(u64 *prog, unsigned int len) 44308c2ecf20Sopenharmony_ci{ 44318c2ecf20Sopenharmony_ci __le64 *ustore = (__force __le64 *)prog; 44328c2ecf20Sopenharmony_ci int i; 44338c2ecf20Sopenharmony_ci 44348c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 44358c2ecf20Sopenharmony_ci int err; 44368c2ecf20Sopenharmony_ci 44378c2ecf20Sopenharmony_ci err = nfp_ustore_check_valid_no_ecc(prog[i]); 44388c2ecf20Sopenharmony_ci if (err) 44398c2ecf20Sopenharmony_ci return err; 44408c2ecf20Sopenharmony_ci 44418c2ecf20Sopenharmony_ci ustore[i] = cpu_to_le64(nfp_ustore_calc_ecc_insn(prog[i])); 44428c2ecf20Sopenharmony_ci } 44438c2ecf20Sopenharmony_ci 44448c2ecf20Sopenharmony_ci return 0; 44458c2ecf20Sopenharmony_ci} 44468c2ecf20Sopenharmony_ci 44478c2ecf20Sopenharmony_cistatic void nfp_bpf_prog_trim(struct nfp_prog *nfp_prog) 44488c2ecf20Sopenharmony_ci{ 44498c2ecf20Sopenharmony_ci void *prog; 44508c2ecf20Sopenharmony_ci 44518c2ecf20Sopenharmony_ci prog = kvmalloc_array(nfp_prog->prog_len, sizeof(u64), GFP_KERNEL); 44528c2ecf20Sopenharmony_ci if (!prog) 44538c2ecf20Sopenharmony_ci return; 44548c2ecf20Sopenharmony_ci 44558c2ecf20Sopenharmony_ci nfp_prog->__prog_alloc_len = nfp_prog->prog_len * sizeof(u64); 44568c2ecf20Sopenharmony_ci memcpy(prog, nfp_prog->prog, nfp_prog->__prog_alloc_len); 44578c2ecf20Sopenharmony_ci kvfree(nfp_prog->prog); 44588c2ecf20Sopenharmony_ci nfp_prog->prog = prog; 44598c2ecf20Sopenharmony_ci} 44608c2ecf20Sopenharmony_ci 44618c2ecf20Sopenharmony_ciint nfp_bpf_jit(struct nfp_prog *nfp_prog) 44628c2ecf20Sopenharmony_ci{ 44638c2ecf20Sopenharmony_ci int ret; 44648c2ecf20Sopenharmony_ci 44658c2ecf20Sopenharmony_ci ret = nfp_bpf_replace_map_ptrs(nfp_prog); 44668c2ecf20Sopenharmony_ci if (ret) 44678c2ecf20Sopenharmony_ci return ret; 44688c2ecf20Sopenharmony_ci 44698c2ecf20Sopenharmony_ci ret = nfp_bpf_optimize(nfp_prog); 44708c2ecf20Sopenharmony_ci if (ret) 44718c2ecf20Sopenharmony_ci return ret; 44728c2ecf20Sopenharmony_ci 44738c2ecf20Sopenharmony_ci ret = nfp_translate(nfp_prog); 44748c2ecf20Sopenharmony_ci if (ret) { 44758c2ecf20Sopenharmony_ci pr_err("Translation failed with error %d (translated: %u)\n", 44768c2ecf20Sopenharmony_ci ret, nfp_prog->n_translated); 44778c2ecf20Sopenharmony_ci return -EINVAL; 44788c2ecf20Sopenharmony_ci } 44798c2ecf20Sopenharmony_ci 44808c2ecf20Sopenharmony_ci nfp_bpf_prog_trim(nfp_prog); 44818c2ecf20Sopenharmony_ci 44828c2ecf20Sopenharmony_ci return ret; 44838c2ecf20Sopenharmony_ci} 44848c2ecf20Sopenharmony_ci 44858c2ecf20Sopenharmony_civoid nfp_bpf_jit_prepare(struct nfp_prog *nfp_prog) 44868c2ecf20Sopenharmony_ci{ 44878c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta; 44888c2ecf20Sopenharmony_ci 44898c2ecf20Sopenharmony_ci /* Another pass to record jump information. */ 44908c2ecf20Sopenharmony_ci list_for_each_entry(meta, &nfp_prog->insns, l) { 44918c2ecf20Sopenharmony_ci struct nfp_insn_meta *dst_meta; 44928c2ecf20Sopenharmony_ci u64 code = meta->insn.code; 44938c2ecf20Sopenharmony_ci unsigned int dst_idx; 44948c2ecf20Sopenharmony_ci bool pseudo_call; 44958c2ecf20Sopenharmony_ci 44968c2ecf20Sopenharmony_ci if (!is_mbpf_jmp(meta)) 44978c2ecf20Sopenharmony_ci continue; 44988c2ecf20Sopenharmony_ci if (BPF_OP(code) == BPF_EXIT) 44998c2ecf20Sopenharmony_ci continue; 45008c2ecf20Sopenharmony_ci if (is_mbpf_helper_call(meta)) 45018c2ecf20Sopenharmony_ci continue; 45028c2ecf20Sopenharmony_ci 45038c2ecf20Sopenharmony_ci /* If opcode is BPF_CALL at this point, this can only be a 45048c2ecf20Sopenharmony_ci * BPF-to-BPF call (a.k.a pseudo call). 45058c2ecf20Sopenharmony_ci */ 45068c2ecf20Sopenharmony_ci pseudo_call = BPF_OP(code) == BPF_CALL; 45078c2ecf20Sopenharmony_ci 45088c2ecf20Sopenharmony_ci if (pseudo_call) 45098c2ecf20Sopenharmony_ci dst_idx = meta->n + 1 + meta->insn.imm; 45108c2ecf20Sopenharmony_ci else 45118c2ecf20Sopenharmony_ci dst_idx = meta->n + 1 + meta->insn.off; 45128c2ecf20Sopenharmony_ci 45138c2ecf20Sopenharmony_ci dst_meta = nfp_bpf_goto_meta(nfp_prog, meta, dst_idx); 45148c2ecf20Sopenharmony_ci 45158c2ecf20Sopenharmony_ci if (pseudo_call) 45168c2ecf20Sopenharmony_ci dst_meta->flags |= FLAG_INSN_IS_SUBPROG_START; 45178c2ecf20Sopenharmony_ci 45188c2ecf20Sopenharmony_ci dst_meta->flags |= FLAG_INSN_IS_JUMP_DST; 45198c2ecf20Sopenharmony_ci meta->jmp_dst = dst_meta; 45208c2ecf20Sopenharmony_ci } 45218c2ecf20Sopenharmony_ci} 45228c2ecf20Sopenharmony_ci 45238c2ecf20Sopenharmony_cibool nfp_bpf_supported_opcode(u8 code) 45248c2ecf20Sopenharmony_ci{ 45258c2ecf20Sopenharmony_ci return !!instr_cb[code]; 45268c2ecf20Sopenharmony_ci} 45278c2ecf20Sopenharmony_ci 45288c2ecf20Sopenharmony_civoid *nfp_bpf_relo_for_vnic(struct nfp_prog *nfp_prog, struct nfp_bpf_vnic *bv) 45298c2ecf20Sopenharmony_ci{ 45308c2ecf20Sopenharmony_ci unsigned int i; 45318c2ecf20Sopenharmony_ci u64 *prog; 45328c2ecf20Sopenharmony_ci int err; 45338c2ecf20Sopenharmony_ci 45348c2ecf20Sopenharmony_ci prog = kmemdup(nfp_prog->prog, nfp_prog->prog_len * sizeof(u64), 45358c2ecf20Sopenharmony_ci GFP_KERNEL); 45368c2ecf20Sopenharmony_ci if (!prog) 45378c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 45388c2ecf20Sopenharmony_ci 45398c2ecf20Sopenharmony_ci for (i = 0; i < nfp_prog->prog_len; i++) { 45408c2ecf20Sopenharmony_ci enum nfp_relo_type special; 45418c2ecf20Sopenharmony_ci u32 val; 45428c2ecf20Sopenharmony_ci u16 off; 45438c2ecf20Sopenharmony_ci 45448c2ecf20Sopenharmony_ci special = FIELD_GET(OP_RELO_TYPE, prog[i]); 45458c2ecf20Sopenharmony_ci switch (special) { 45468c2ecf20Sopenharmony_ci case RELO_NONE: 45478c2ecf20Sopenharmony_ci continue; 45488c2ecf20Sopenharmony_ci case RELO_BR_REL: 45498c2ecf20Sopenharmony_ci br_add_offset(&prog[i], bv->start_off); 45508c2ecf20Sopenharmony_ci break; 45518c2ecf20Sopenharmony_ci case RELO_BR_GO_OUT: 45528c2ecf20Sopenharmony_ci br_set_offset(&prog[i], 45538c2ecf20Sopenharmony_ci nfp_prog->tgt_out + bv->start_off); 45548c2ecf20Sopenharmony_ci break; 45558c2ecf20Sopenharmony_ci case RELO_BR_GO_ABORT: 45568c2ecf20Sopenharmony_ci br_set_offset(&prog[i], 45578c2ecf20Sopenharmony_ci nfp_prog->tgt_abort + bv->start_off); 45588c2ecf20Sopenharmony_ci break; 45598c2ecf20Sopenharmony_ci case RELO_BR_GO_CALL_PUSH_REGS: 45608c2ecf20Sopenharmony_ci if (!nfp_prog->tgt_call_push_regs) { 45618c2ecf20Sopenharmony_ci pr_err("BUG: failed to detect subprogram registers needs\n"); 45628c2ecf20Sopenharmony_ci err = -EINVAL; 45638c2ecf20Sopenharmony_ci goto err_free_prog; 45648c2ecf20Sopenharmony_ci } 45658c2ecf20Sopenharmony_ci off = nfp_prog->tgt_call_push_regs + bv->start_off; 45668c2ecf20Sopenharmony_ci br_set_offset(&prog[i], off); 45678c2ecf20Sopenharmony_ci break; 45688c2ecf20Sopenharmony_ci case RELO_BR_GO_CALL_POP_REGS: 45698c2ecf20Sopenharmony_ci if (!nfp_prog->tgt_call_pop_regs) { 45708c2ecf20Sopenharmony_ci pr_err("BUG: failed to detect subprogram registers needs\n"); 45718c2ecf20Sopenharmony_ci err = -EINVAL; 45728c2ecf20Sopenharmony_ci goto err_free_prog; 45738c2ecf20Sopenharmony_ci } 45748c2ecf20Sopenharmony_ci off = nfp_prog->tgt_call_pop_regs + bv->start_off; 45758c2ecf20Sopenharmony_ci br_set_offset(&prog[i], off); 45768c2ecf20Sopenharmony_ci break; 45778c2ecf20Sopenharmony_ci case RELO_BR_NEXT_PKT: 45788c2ecf20Sopenharmony_ci br_set_offset(&prog[i], bv->tgt_done); 45798c2ecf20Sopenharmony_ci break; 45808c2ecf20Sopenharmony_ci case RELO_BR_HELPER: 45818c2ecf20Sopenharmony_ci val = br_get_offset(prog[i]); 45828c2ecf20Sopenharmony_ci val -= BR_OFF_RELO; 45838c2ecf20Sopenharmony_ci switch (val) { 45848c2ecf20Sopenharmony_ci case BPF_FUNC_map_lookup_elem: 45858c2ecf20Sopenharmony_ci val = nfp_prog->bpf->helpers.map_lookup; 45868c2ecf20Sopenharmony_ci break; 45878c2ecf20Sopenharmony_ci case BPF_FUNC_map_update_elem: 45888c2ecf20Sopenharmony_ci val = nfp_prog->bpf->helpers.map_update; 45898c2ecf20Sopenharmony_ci break; 45908c2ecf20Sopenharmony_ci case BPF_FUNC_map_delete_elem: 45918c2ecf20Sopenharmony_ci val = nfp_prog->bpf->helpers.map_delete; 45928c2ecf20Sopenharmony_ci break; 45938c2ecf20Sopenharmony_ci case BPF_FUNC_perf_event_output: 45948c2ecf20Sopenharmony_ci val = nfp_prog->bpf->helpers.perf_event_output; 45958c2ecf20Sopenharmony_ci break; 45968c2ecf20Sopenharmony_ci default: 45978c2ecf20Sopenharmony_ci pr_err("relocation of unknown helper %d\n", 45988c2ecf20Sopenharmony_ci val); 45998c2ecf20Sopenharmony_ci err = -EINVAL; 46008c2ecf20Sopenharmony_ci goto err_free_prog; 46018c2ecf20Sopenharmony_ci } 46028c2ecf20Sopenharmony_ci br_set_offset(&prog[i], val); 46038c2ecf20Sopenharmony_ci break; 46048c2ecf20Sopenharmony_ci case RELO_IMMED_REL: 46058c2ecf20Sopenharmony_ci immed_add_value(&prog[i], bv->start_off); 46068c2ecf20Sopenharmony_ci break; 46078c2ecf20Sopenharmony_ci } 46088c2ecf20Sopenharmony_ci 46098c2ecf20Sopenharmony_ci prog[i] &= ~OP_RELO_TYPE; 46108c2ecf20Sopenharmony_ci } 46118c2ecf20Sopenharmony_ci 46128c2ecf20Sopenharmony_ci err = nfp_bpf_ustore_calc(prog, nfp_prog->prog_len); 46138c2ecf20Sopenharmony_ci if (err) 46148c2ecf20Sopenharmony_ci goto err_free_prog; 46158c2ecf20Sopenharmony_ci 46168c2ecf20Sopenharmony_ci return prog; 46178c2ecf20Sopenharmony_ci 46188c2ecf20Sopenharmony_cierr_free_prog: 46198c2ecf20Sopenharmony_ci kfree(prog); 46208c2ecf20Sopenharmony_ci return ERR_PTR(err); 46218c2ecf20Sopenharmony_ci} 4622