18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
28c2ecf20Sopenharmony_ci/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include "../nfpcore/nfp_cpp.h"
78c2ecf20Sopenharmony_ci#include "../nfpcore/nfp_nffw.h"
88c2ecf20Sopenharmony_ci#include "../nfpcore/nfp_nsp.h"
98c2ecf20Sopenharmony_ci#include "../nfp_app.h"
108c2ecf20Sopenharmony_ci#include "../nfp_main.h"
118c2ecf20Sopenharmony_ci#include "../nfp_net.h"
128c2ecf20Sopenharmony_ci#include "../nfp_port.h"
138c2ecf20Sopenharmony_ci#include "fw.h"
148c2ecf20Sopenharmony_ci#include "main.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciconst struct rhashtable_params nfp_bpf_maps_neutral_params = {
178c2ecf20Sopenharmony_ci	.nelem_hint		= 4,
188c2ecf20Sopenharmony_ci	.key_len		= sizeof_field(struct bpf_map, id),
198c2ecf20Sopenharmony_ci	.key_offset		= offsetof(struct nfp_bpf_neutral_map, map_id),
208c2ecf20Sopenharmony_ci	.head_offset		= offsetof(struct nfp_bpf_neutral_map, l),
218c2ecf20Sopenharmony_ci	.automatic_shrinking	= true,
228c2ecf20Sopenharmony_ci};
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic bool nfp_net_ebpf_capable(struct nfp_net *nn)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN
278c2ecf20Sopenharmony_ci	struct nfp_app_bpf *bpf = nn->app->priv;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	return nn->cap & NFP_NET_CFG_CTRL_BPF &&
308c2ecf20Sopenharmony_ci	       bpf->abi_version &&
318c2ecf20Sopenharmony_ci	       nn_readb(nn, NFP_NET_CFG_BPF_ABI) == bpf->abi_version;
328c2ecf20Sopenharmony_ci#else
338c2ecf20Sopenharmony_ci	return false;
348c2ecf20Sopenharmony_ci#endif
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic int
388c2ecf20Sopenharmony_cinfp_bpf_xdp_offload(struct nfp_app *app, struct nfp_net *nn,
398c2ecf20Sopenharmony_ci		    struct bpf_prog *prog, struct netlink_ext_ack *extack)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	bool running, xdp_running;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	if (!nfp_net_ebpf_capable(nn))
448c2ecf20Sopenharmony_ci		return -EINVAL;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	running = nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF;
478c2ecf20Sopenharmony_ci	xdp_running = running && nn->xdp_hw.prog;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (!prog && !xdp_running)
508c2ecf20Sopenharmony_ci		return 0;
518c2ecf20Sopenharmony_ci	if (prog && running && !xdp_running)
528c2ecf20Sopenharmony_ci		return -EBUSY;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return nfp_net_bpf_offload(nn, prog, running, extack);
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic const char *nfp_bpf_extra_cap(struct nfp_app *app, struct nfp_net *nn)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	return nfp_net_ebpf_capable(nn) ? "BPF" : "";
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic int
638c2ecf20Sopenharmony_cinfp_bpf_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct nfp_pf *pf = app->pf;
668c2ecf20Sopenharmony_ci	struct nfp_bpf_vnic *bv;
678c2ecf20Sopenharmony_ci	int err;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (!pf->eth_tbl) {
708c2ecf20Sopenharmony_ci		nfp_err(pf->cpp, "No ETH table\n");
718c2ecf20Sopenharmony_ci		return -EINVAL;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci	if (pf->max_data_vnics != pf->eth_tbl->count) {
748c2ecf20Sopenharmony_ci		nfp_err(pf->cpp, "ETH entries don't match vNICs (%d vs %d)\n",
758c2ecf20Sopenharmony_ci			pf->max_data_vnics, pf->eth_tbl->count);
768c2ecf20Sopenharmony_ci		return -EINVAL;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	bv = kzalloc(sizeof(*bv), GFP_KERNEL);
808c2ecf20Sopenharmony_ci	if (!bv)
818c2ecf20Sopenharmony_ci		return -ENOMEM;
828c2ecf20Sopenharmony_ci	nn->app_priv = bv;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	err = nfp_app_nic_vnic_alloc(app, nn, id);
858c2ecf20Sopenharmony_ci	if (err)
868c2ecf20Sopenharmony_ci		goto err_free_priv;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	bv->start_off = nn_readw(nn, NFP_NET_CFG_BPF_START);
898c2ecf20Sopenharmony_ci	bv->tgt_done = nn_readw(nn, NFP_NET_CFG_BPF_DONE);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_cierr_free_priv:
938c2ecf20Sopenharmony_ci	kfree(nn->app_priv);
948c2ecf20Sopenharmony_ci	return err;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic void nfp_bpf_vnic_free(struct nfp_app *app, struct nfp_net *nn)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct nfp_bpf_vnic *bv = nn->app_priv;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	WARN_ON(bv->tc_prog);
1028c2ecf20Sopenharmony_ci	kfree(bv);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int nfp_bpf_setup_tc_block_cb(enum tc_setup_type type,
1068c2ecf20Sopenharmony_ci				     void *type_data, void *cb_priv)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct tc_cls_bpf_offload *cls_bpf = type_data;
1098c2ecf20Sopenharmony_ci	struct nfp_net *nn = cb_priv;
1108c2ecf20Sopenharmony_ci	struct bpf_prog *oldprog;
1118c2ecf20Sopenharmony_ci	struct nfp_bpf_vnic *bv;
1128c2ecf20Sopenharmony_ci	int err;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (type != TC_SETUP_CLSBPF) {
1158c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(cls_bpf->common.extack,
1168c2ecf20Sopenharmony_ci				   "only offload of BPF classifiers supported");
1178c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci	if (!tc_cls_can_offload_and_chain0(nn->dp.netdev, &cls_bpf->common))
1208c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1218c2ecf20Sopenharmony_ci	if (!nfp_net_ebpf_capable(nn)) {
1228c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(cls_bpf->common.extack,
1238c2ecf20Sopenharmony_ci				   "NFP firmware does not support eBPF offload");
1248c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci	if (cls_bpf->common.protocol != htons(ETH_P_ALL)) {
1278c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(cls_bpf->common.extack,
1288c2ecf20Sopenharmony_ci				   "only ETH_P_ALL supported as filter protocol");
1298c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* Only support TC direct action */
1338c2ecf20Sopenharmony_ci	if (!cls_bpf->exts_integrated ||
1348c2ecf20Sopenharmony_ci	    tcf_exts_has_actions(cls_bpf->exts)) {
1358c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(cls_bpf->common.extack,
1368c2ecf20Sopenharmony_ci				   "only direct action with no legacy actions supported");
1378c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (cls_bpf->command != TC_CLSBPF_OFFLOAD)
1418c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	bv = nn->app_priv;
1448c2ecf20Sopenharmony_ci	oldprog = cls_bpf->oldprog;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* Don't remove if oldprog doesn't match driver's state */
1478c2ecf20Sopenharmony_ci	if (bv->tc_prog != oldprog) {
1488c2ecf20Sopenharmony_ci		oldprog = NULL;
1498c2ecf20Sopenharmony_ci		if (!cls_bpf->prog)
1508c2ecf20Sopenharmony_ci			return 0;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	err = nfp_net_bpf_offload(nn, cls_bpf->prog, oldprog,
1548c2ecf20Sopenharmony_ci				  cls_bpf->common.extack);
1558c2ecf20Sopenharmony_ci	if (err)
1568c2ecf20Sopenharmony_ci		return err;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	bv->tc_prog = cls_bpf->prog;
1598c2ecf20Sopenharmony_ci	nn->port->tc_offload_cnt = !!bv->tc_prog;
1608c2ecf20Sopenharmony_ci	return 0;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic LIST_HEAD(nfp_bpf_block_cb_list);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev,
1668c2ecf20Sopenharmony_ci			    enum tc_setup_type type, void *type_data)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct nfp_net *nn = netdev_priv(netdev);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	switch (type) {
1718c2ecf20Sopenharmony_ci	case TC_SETUP_BLOCK:
1728c2ecf20Sopenharmony_ci		return flow_block_cb_setup_simple(type_data,
1738c2ecf20Sopenharmony_ci						  &nfp_bpf_block_cb_list,
1748c2ecf20Sopenharmony_ci						  nfp_bpf_setup_tc_block_cb,
1758c2ecf20Sopenharmony_ci						  nn, nn, true);
1768c2ecf20Sopenharmony_ci	default:
1778c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int
1828c2ecf20Sopenharmony_cinfp_bpf_check_mtu(struct nfp_app *app, struct net_device *netdev, int new_mtu)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct nfp_net *nn = netdev_priv(netdev);
1858c2ecf20Sopenharmony_ci	struct nfp_bpf_vnic *bv;
1868c2ecf20Sopenharmony_ci	struct bpf_prog *prog;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (~nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF)
1898c2ecf20Sopenharmony_ci		return 0;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (nn->xdp_hw.prog) {
1928c2ecf20Sopenharmony_ci		prog = nn->xdp_hw.prog;
1938c2ecf20Sopenharmony_ci	} else {
1948c2ecf20Sopenharmony_ci		bv = nn->app_priv;
1958c2ecf20Sopenharmony_ci		prog = bv->tc_prog;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (nfp_bpf_offload_check_mtu(nn, prog, new_mtu)) {
1998c2ecf20Sopenharmony_ci		nn_info(nn, "BPF offload active, potential packet access beyond hardware packet boundary");
2008c2ecf20Sopenharmony_ci		return -EBUSY;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic int
2068c2ecf20Sopenharmony_cinfp_bpf_parse_cap_adjust_head(struct nfp_app_bpf *bpf, void __iomem *value,
2078c2ecf20Sopenharmony_ci			      u32 length)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct nfp_bpf_cap_tlv_adjust_head __iomem *cap = value;
2108c2ecf20Sopenharmony_ci	struct nfp_cpp *cpp = bpf->app->pf->cpp;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (length < sizeof(*cap)) {
2138c2ecf20Sopenharmony_ci		nfp_err(cpp, "truncated adjust_head TLV: %d\n", length);
2148c2ecf20Sopenharmony_ci		return -EINVAL;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	bpf->adjust_head.flags = readl(&cap->flags);
2188c2ecf20Sopenharmony_ci	bpf->adjust_head.off_min = readl(&cap->off_min);
2198c2ecf20Sopenharmony_ci	bpf->adjust_head.off_max = readl(&cap->off_max);
2208c2ecf20Sopenharmony_ci	bpf->adjust_head.guaranteed_sub = readl(&cap->guaranteed_sub);
2218c2ecf20Sopenharmony_ci	bpf->adjust_head.guaranteed_add = readl(&cap->guaranteed_add);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (bpf->adjust_head.off_min > bpf->adjust_head.off_max) {
2248c2ecf20Sopenharmony_ci		nfp_err(cpp, "invalid adjust_head TLV: min > max\n");
2258c2ecf20Sopenharmony_ci		return -EINVAL;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci	if (!FIELD_FIT(UR_REG_IMM_MAX, bpf->adjust_head.off_min) ||
2288c2ecf20Sopenharmony_ci	    !FIELD_FIT(UR_REG_IMM_MAX, bpf->adjust_head.off_max)) {
2298c2ecf20Sopenharmony_ci		nfp_warn(cpp, "disabling adjust_head - driver expects min/max to fit in as immediates\n");
2308c2ecf20Sopenharmony_ci		memset(&bpf->adjust_head, 0, sizeof(bpf->adjust_head));
2318c2ecf20Sopenharmony_ci		return 0;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return 0;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic int
2388c2ecf20Sopenharmony_cinfp_bpf_parse_cap_func(struct nfp_app_bpf *bpf, void __iomem *value, u32 length)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct nfp_bpf_cap_tlv_func __iomem *cap = value;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (length < sizeof(*cap)) {
2438c2ecf20Sopenharmony_ci		nfp_err(bpf->app->cpp, "truncated function TLV: %d\n", length);
2448c2ecf20Sopenharmony_ci		return -EINVAL;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	switch (readl(&cap->func_id)) {
2488c2ecf20Sopenharmony_ci	case BPF_FUNC_map_lookup_elem:
2498c2ecf20Sopenharmony_ci		bpf->helpers.map_lookup = readl(&cap->func_addr);
2508c2ecf20Sopenharmony_ci		break;
2518c2ecf20Sopenharmony_ci	case BPF_FUNC_map_update_elem:
2528c2ecf20Sopenharmony_ci		bpf->helpers.map_update = readl(&cap->func_addr);
2538c2ecf20Sopenharmony_ci		break;
2548c2ecf20Sopenharmony_ci	case BPF_FUNC_map_delete_elem:
2558c2ecf20Sopenharmony_ci		bpf->helpers.map_delete = readl(&cap->func_addr);
2568c2ecf20Sopenharmony_ci		break;
2578c2ecf20Sopenharmony_ci	case BPF_FUNC_perf_event_output:
2588c2ecf20Sopenharmony_ci		bpf->helpers.perf_event_output = readl(&cap->func_addr);
2598c2ecf20Sopenharmony_ci		break;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return 0;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic int
2668c2ecf20Sopenharmony_cinfp_bpf_parse_cap_maps(struct nfp_app_bpf *bpf, void __iomem *value, u32 length)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct nfp_bpf_cap_tlv_maps __iomem *cap = value;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (length < sizeof(*cap)) {
2718c2ecf20Sopenharmony_ci		nfp_err(bpf->app->cpp, "truncated maps TLV: %d\n", length);
2728c2ecf20Sopenharmony_ci		return -EINVAL;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	bpf->maps.types = readl(&cap->types);
2768c2ecf20Sopenharmony_ci	bpf->maps.max_maps = readl(&cap->max_maps);
2778c2ecf20Sopenharmony_ci	bpf->maps.max_elems = readl(&cap->max_elems);
2788c2ecf20Sopenharmony_ci	bpf->maps.max_key_sz = readl(&cap->max_key_sz);
2798c2ecf20Sopenharmony_ci	bpf->maps.max_val_sz = readl(&cap->max_val_sz);
2808c2ecf20Sopenharmony_ci	bpf->maps.max_elem_sz = readl(&cap->max_elem_sz);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	return 0;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic int
2868c2ecf20Sopenharmony_cinfp_bpf_parse_cap_random(struct nfp_app_bpf *bpf, void __iomem *value,
2878c2ecf20Sopenharmony_ci			 u32 length)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	bpf->pseudo_random = true;
2908c2ecf20Sopenharmony_ci	return 0;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic int
2948c2ecf20Sopenharmony_cinfp_bpf_parse_cap_qsel(struct nfp_app_bpf *bpf, void __iomem *value, u32 length)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	bpf->queue_select = true;
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int
3018c2ecf20Sopenharmony_cinfp_bpf_parse_cap_adjust_tail(struct nfp_app_bpf *bpf, void __iomem *value,
3028c2ecf20Sopenharmony_ci			      u32 length)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	bpf->adjust_tail = true;
3058c2ecf20Sopenharmony_ci	return 0;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic int
3098c2ecf20Sopenharmony_cinfp_bpf_parse_cap_cmsg_multi_ent(struct nfp_app_bpf *bpf, void __iomem *value,
3108c2ecf20Sopenharmony_ci				 u32 length)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	bpf->cmsg_multi_ent = true;
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic int
3178c2ecf20Sopenharmony_cinfp_bpf_parse_cap_abi_version(struct nfp_app_bpf *bpf, void __iomem *value,
3188c2ecf20Sopenharmony_ci			      u32 length)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	if (length < 4) {
3218c2ecf20Sopenharmony_ci		nfp_err(bpf->app->cpp, "truncated ABI version TLV: %d\n",
3228c2ecf20Sopenharmony_ci			length);
3238c2ecf20Sopenharmony_ci		return -EINVAL;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	bpf->abi_version = readl(value);
3278c2ecf20Sopenharmony_ci	if (bpf->abi_version < 2 || bpf->abi_version > 3) {
3288c2ecf20Sopenharmony_ci		nfp_warn(bpf->app->cpp, "unsupported BPF ABI version: %d\n",
3298c2ecf20Sopenharmony_ci			 bpf->abi_version);
3308c2ecf20Sopenharmony_ci		bpf->abi_version = 0;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	return 0;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic int nfp_bpf_parse_capabilities(struct nfp_app *app)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct nfp_cpp *cpp = app->pf->cpp;
3398c2ecf20Sopenharmony_ci	struct nfp_cpp_area *area;
3408c2ecf20Sopenharmony_ci	u8 __iomem *mem, *start;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	mem = nfp_rtsym_map(app->pf->rtbl, "_abi_bpf_capabilities", "bpf.cap",
3438c2ecf20Sopenharmony_ci			    8, &area);
3448c2ecf20Sopenharmony_ci	if (IS_ERR(mem))
3458c2ecf20Sopenharmony_ci		return PTR_ERR(mem) == -ENOENT ? 0 : PTR_ERR(mem);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	start = mem;
3488c2ecf20Sopenharmony_ci	while (mem - start + 8 <= nfp_cpp_area_size(area)) {
3498c2ecf20Sopenharmony_ci		u8 __iomem *value;
3508c2ecf20Sopenharmony_ci		u32 type, length;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		type = readl(mem);
3538c2ecf20Sopenharmony_ci		length = readl(mem + 4);
3548c2ecf20Sopenharmony_ci		value = mem + 8;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci		mem += 8 + length;
3578c2ecf20Sopenharmony_ci		if (mem - start > nfp_cpp_area_size(area))
3588c2ecf20Sopenharmony_ci			goto err_release_free;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci		switch (type) {
3618c2ecf20Sopenharmony_ci		case NFP_BPF_CAP_TYPE_FUNC:
3628c2ecf20Sopenharmony_ci			if (nfp_bpf_parse_cap_func(app->priv, value, length))
3638c2ecf20Sopenharmony_ci				goto err_release_free;
3648c2ecf20Sopenharmony_ci			break;
3658c2ecf20Sopenharmony_ci		case NFP_BPF_CAP_TYPE_ADJUST_HEAD:
3668c2ecf20Sopenharmony_ci			if (nfp_bpf_parse_cap_adjust_head(app->priv, value,
3678c2ecf20Sopenharmony_ci							  length))
3688c2ecf20Sopenharmony_ci				goto err_release_free;
3698c2ecf20Sopenharmony_ci			break;
3708c2ecf20Sopenharmony_ci		case NFP_BPF_CAP_TYPE_MAPS:
3718c2ecf20Sopenharmony_ci			if (nfp_bpf_parse_cap_maps(app->priv, value, length))
3728c2ecf20Sopenharmony_ci				goto err_release_free;
3738c2ecf20Sopenharmony_ci			break;
3748c2ecf20Sopenharmony_ci		case NFP_BPF_CAP_TYPE_RANDOM:
3758c2ecf20Sopenharmony_ci			if (nfp_bpf_parse_cap_random(app->priv, value, length))
3768c2ecf20Sopenharmony_ci				goto err_release_free;
3778c2ecf20Sopenharmony_ci			break;
3788c2ecf20Sopenharmony_ci		case NFP_BPF_CAP_TYPE_QUEUE_SELECT:
3798c2ecf20Sopenharmony_ci			if (nfp_bpf_parse_cap_qsel(app->priv, value, length))
3808c2ecf20Sopenharmony_ci				goto err_release_free;
3818c2ecf20Sopenharmony_ci			break;
3828c2ecf20Sopenharmony_ci		case NFP_BPF_CAP_TYPE_ADJUST_TAIL:
3838c2ecf20Sopenharmony_ci			if (nfp_bpf_parse_cap_adjust_tail(app->priv, value,
3848c2ecf20Sopenharmony_ci							  length))
3858c2ecf20Sopenharmony_ci				goto err_release_free;
3868c2ecf20Sopenharmony_ci			break;
3878c2ecf20Sopenharmony_ci		case NFP_BPF_CAP_TYPE_ABI_VERSION:
3888c2ecf20Sopenharmony_ci			if (nfp_bpf_parse_cap_abi_version(app->priv, value,
3898c2ecf20Sopenharmony_ci							  length))
3908c2ecf20Sopenharmony_ci				goto err_release_free;
3918c2ecf20Sopenharmony_ci			break;
3928c2ecf20Sopenharmony_ci		case NFP_BPF_CAP_TYPE_CMSG_MULTI_ENT:
3938c2ecf20Sopenharmony_ci			if (nfp_bpf_parse_cap_cmsg_multi_ent(app->priv, value,
3948c2ecf20Sopenharmony_ci							     length))
3958c2ecf20Sopenharmony_ci				goto err_release_free;
3968c2ecf20Sopenharmony_ci			break;
3978c2ecf20Sopenharmony_ci		default:
3988c2ecf20Sopenharmony_ci			nfp_dbg(cpp, "unknown BPF capability: %d\n", type);
3998c2ecf20Sopenharmony_ci			break;
4008c2ecf20Sopenharmony_ci		}
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci	if (mem - start != nfp_cpp_area_size(area)) {
4038c2ecf20Sopenharmony_ci		nfp_err(cpp, "BPF capabilities left after parsing, parsed:%zd total length:%zu\n",
4048c2ecf20Sopenharmony_ci			mem - start, nfp_cpp_area_size(area));
4058c2ecf20Sopenharmony_ci		goto err_release_free;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	nfp_cpp_area_release_free(area);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cierr_release_free:
4138c2ecf20Sopenharmony_ci	nfp_err(cpp, "invalid BPF capabilities at offset:%zd\n", mem - start);
4148c2ecf20Sopenharmony_ci	nfp_cpp_area_release_free(area);
4158c2ecf20Sopenharmony_ci	return -EINVAL;
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cistatic void nfp_bpf_init_capabilities(struct nfp_app_bpf *bpf)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	bpf->abi_version = 2; /* Original BPF ABI version */
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic int nfp_bpf_ndo_init(struct nfp_app *app, struct net_device *netdev)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct nfp_app_bpf *bpf = app->priv;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	return bpf_offload_dev_netdev_register(bpf->bpf_dev, netdev);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic void nfp_bpf_ndo_uninit(struct nfp_app *app, struct net_device *netdev)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct nfp_app_bpf *bpf = app->priv;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	bpf_offload_dev_netdev_unregister(bpf->bpf_dev, netdev);
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic int nfp_bpf_start(struct nfp_app *app)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	struct nfp_app_bpf *bpf = app->priv;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (app->ctrl->dp.mtu < nfp_bpf_ctrl_cmsg_min_mtu(bpf)) {
4428c2ecf20Sopenharmony_ci		nfp_err(bpf->app->cpp,
4438c2ecf20Sopenharmony_ci			"ctrl channel MTU below min required %u < %u\n",
4448c2ecf20Sopenharmony_ci			app->ctrl->dp.mtu, nfp_bpf_ctrl_cmsg_min_mtu(bpf));
4458c2ecf20Sopenharmony_ci		return -EINVAL;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (bpf->cmsg_multi_ent)
4498c2ecf20Sopenharmony_ci		bpf->cmsg_cache_cnt = nfp_bpf_ctrl_cmsg_cache_cnt(bpf);
4508c2ecf20Sopenharmony_ci	else
4518c2ecf20Sopenharmony_ci		bpf->cmsg_cache_cnt = 1;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	return 0;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic int nfp_bpf_init(struct nfp_app *app)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	struct nfp_app_bpf *bpf;
4598c2ecf20Sopenharmony_ci	int err;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	bpf = kzalloc(sizeof(*bpf), GFP_KERNEL);
4628c2ecf20Sopenharmony_ci	if (!bpf)
4638c2ecf20Sopenharmony_ci		return -ENOMEM;
4648c2ecf20Sopenharmony_ci	bpf->app = app;
4658c2ecf20Sopenharmony_ci	app->priv = bpf;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&bpf->map_list);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	err = nfp_ccm_init(&bpf->ccm, app);
4708c2ecf20Sopenharmony_ci	if (err)
4718c2ecf20Sopenharmony_ci		goto err_free_bpf;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	err = rhashtable_init(&bpf->maps_neutral, &nfp_bpf_maps_neutral_params);
4748c2ecf20Sopenharmony_ci	if (err)
4758c2ecf20Sopenharmony_ci		goto err_clean_ccm;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	nfp_bpf_init_capabilities(bpf);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	err = nfp_bpf_parse_capabilities(app);
4808c2ecf20Sopenharmony_ci	if (err)
4818c2ecf20Sopenharmony_ci		goto err_free_neutral_maps;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (bpf->abi_version < 3) {
4848c2ecf20Sopenharmony_ci		bpf->cmsg_key_sz = CMSG_MAP_KEY_LW * 4;
4858c2ecf20Sopenharmony_ci		bpf->cmsg_val_sz = CMSG_MAP_VALUE_LW * 4;
4868c2ecf20Sopenharmony_ci	} else {
4878c2ecf20Sopenharmony_ci		bpf->cmsg_key_sz = bpf->maps.max_key_sz;
4888c2ecf20Sopenharmony_ci		bpf->cmsg_val_sz = bpf->maps.max_val_sz;
4898c2ecf20Sopenharmony_ci		app->ctrl_mtu = nfp_bpf_ctrl_cmsg_mtu(bpf);
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	bpf->bpf_dev = bpf_offload_dev_create(&nfp_bpf_dev_ops, bpf);
4938c2ecf20Sopenharmony_ci	err = PTR_ERR_OR_ZERO(bpf->bpf_dev);
4948c2ecf20Sopenharmony_ci	if (err)
4958c2ecf20Sopenharmony_ci		goto err_free_neutral_maps;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	return 0;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cierr_free_neutral_maps:
5008c2ecf20Sopenharmony_ci	rhashtable_destroy(&bpf->maps_neutral);
5018c2ecf20Sopenharmony_cierr_clean_ccm:
5028c2ecf20Sopenharmony_ci	nfp_ccm_clean(&bpf->ccm);
5038c2ecf20Sopenharmony_cierr_free_bpf:
5048c2ecf20Sopenharmony_ci	kfree(bpf);
5058c2ecf20Sopenharmony_ci	return err;
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic void nfp_bpf_clean(struct nfp_app *app)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	struct nfp_app_bpf *bpf = app->priv;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	bpf_offload_dev_destroy(bpf->bpf_dev);
5138c2ecf20Sopenharmony_ci	nfp_ccm_clean(&bpf->ccm);
5148c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&bpf->map_list));
5158c2ecf20Sopenharmony_ci	WARN_ON(bpf->maps_in_use || bpf->map_elems_in_use);
5168c2ecf20Sopenharmony_ci	rhashtable_free_and_destroy(&bpf->maps_neutral,
5178c2ecf20Sopenharmony_ci				    nfp_check_rhashtable_empty, NULL);
5188c2ecf20Sopenharmony_ci	kfree(bpf);
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ciconst struct nfp_app_type app_bpf = {
5228c2ecf20Sopenharmony_ci	.id		= NFP_APP_BPF_NIC,
5238c2ecf20Sopenharmony_ci	.name		= "ebpf",
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	.ctrl_cap_mask	= 0,
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	.init		= nfp_bpf_init,
5288c2ecf20Sopenharmony_ci	.clean		= nfp_bpf_clean,
5298c2ecf20Sopenharmony_ci	.start		= nfp_bpf_start,
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	.check_mtu	= nfp_bpf_check_mtu,
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	.extra_cap	= nfp_bpf_extra_cap,
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	.ndo_init	= nfp_bpf_ndo_init,
5368c2ecf20Sopenharmony_ci	.ndo_uninit	= nfp_bpf_ndo_uninit,
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	.vnic_alloc	= nfp_bpf_vnic_alloc,
5398c2ecf20Sopenharmony_ci	.vnic_free	= nfp_bpf_vnic_free,
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	.ctrl_msg_rx	= nfp_bpf_ctrl_msg_rx,
5428c2ecf20Sopenharmony_ci	.ctrl_msg_rx_raw	= nfp_bpf_ctrl_msg_rx_raw,
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	.setup_tc	= nfp_bpf_setup_tc,
5458c2ecf20Sopenharmony_ci	.bpf		= nfp_ndo_bpf,
5468c2ecf20Sopenharmony_ci	.xdp_offload	= nfp_bpf_xdp_offload,
5478c2ecf20Sopenharmony_ci};
548