blob: bd791470adf8bffedd23c7c00ec03a709432f050 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2023 MediaTek Inc.
*
* Author: Chris.Chou <chris.chou@mediatek.com>
* Ren-Ting Wang <ren-ting.wang@mediatek.com>
*/
#include <linux/bitops.h>
#include <mtk_eth_soc.h>
#include <mtk_hnat/hnat.h>
#include <mtk_hnat/nf_hnat_mtk.h>
#include <pce/cdrt.h>
#include <pce/cls.h>
#include <pce/netsys.h>
#include <crypto-eip/ddk/configs/cs_hwpal_ext.h>
#include <crypto-eip/ddk/kit/iotoken/iotoken.h>
#include <crypto-eip/ddk/kit/iotoken/iotoken_ext.h>
#include "crypto-eip/crypto-eip.h"
#include "crypto-eip/ddk-wrapper.h"
#include "crypto-eip/internal.h"
static LIST_HEAD(xfrm_params_head);
static void mtk_xfrm_offload_cdrt_tear_down(struct mtk_xfrm_params *xfrm_params)
{
memset(&xfrm_params->cdrt->desc, 0, sizeof(struct cdrt_desc));
mtk_pce_cdrt_entry_write(xfrm_params->cdrt);
}
static int mtk_xfrm_offload_cdrt_setup(struct mtk_xfrm_params *xfrm_params)
{
struct cdrt_desc *cdesc = &xfrm_params->cdrt->desc;
cdesc->desc1.common.type = 3;
cdesc->desc1.token_len = 48;
cdesc->desc1.p_tr[0] = __pa(xfrm_params->p_tr) | 2;
cdesc->desc2.hw_srv = 2;
cdesc->desc2.allow_pad = 1;
cdesc->desc2.strip_pad = 1;
return mtk_pce_cdrt_entry_write(xfrm_params->cdrt);
}
static int mtk_xfrm_offload_cls_entry_setup(struct mtk_xfrm_params *xfrm_params)
{
struct cls_entry esp_cls_entry = {
.entry = xfrm_params->cdrt->idx + 10, /* TODO: need update */
.cdesc = {
.fport = 0x3,
.tport_idx = 0x2,
.cdrt_idx = xfrm_params->cdrt->idx,
.l4_udp_hdr_nez_m = 0x1,
.l4_udp_hdr_nez = 0x1,
.l4_hdr_usr_data_m = 0xffffffff,
.l4_hdr_usr_data = be32_to_cpu(xfrm_params->xs->id.spi),
.l4_valid_m = 0x3,
.l4_valid = 0x3,
.l4_dport_m = 0x0,
.l4_dport = 0x0,
.tag_m = 0x3,
.tag = 0x1,
.dip_match_m = 0x0,
.dip_match = 0x0,
.l4_type_m = 0xff,
.l4_type = 0x32,
},
};
return mtk_pce_cls_entry_register(&esp_cls_entry);
}
static void mtk_xfrm_offload_context_tear_down(struct mtk_xfrm_params *xfrm_params)
{
mtk_xfrm_offload_cdrt_tear_down(xfrm_params);
/* TODO: free context */
devm_kfree(crypto_dev, xfrm_params->p_tr);
/* TODO: transform record tear down */
}
static int mtk_xfrm_offload_context_setup(struct mtk_xfrm_params *xfrm_params)
{
u32 *tr;
int ret;
xfrm_params->p_tr = devm_kcalloc(crypto_dev, sizeof(u32),
TRANSFORM_RECORD_LEN, GFP_KERNEL);
if (unlikely(!xfrm_params->p_tr))
return -ENOMEM;
switch (xfrm_params->xs->outer_mode.encap) {
case XFRM_MODE_TUNNEL:
tr = mtk_ddk_tr_ipsec_build(xfrm_params, SAB_IPSEC_TUNNEL);
break;
case XFRM_MODE_TRANSPORT:
tr = mtk_ddk_tr_ipsec_build(xfrm_params, SAB_IPSEC_TRANSPORT);
break;
default:
ret = -ENOMEM;
goto err_out;
}
if (!tr) {
ret = -EINVAL;
goto err_out;
}
memcpy(xfrm_params->p_tr, tr, sizeof(u32) * TRANSFORM_RECORD_LEN);
/* TODO: free tr */
return mtk_xfrm_offload_cdrt_setup(xfrm_params);
err_out:
devm_kfree(crypto_dev, xfrm_params->p_tr);
return ret;
}
static int mtk_xfrm_offload_state_add_outbound(struct xfrm_state *xs,
struct mtk_xfrm_params *xfrm_params)
{
int ret;
xfrm_params->cdrt = mtk_pce_cdrt_entry_alloc(CDRT_ENCRYPT);
if (IS_ERR(xfrm_params->cdrt))
return PTR_ERR(xfrm_params->cdrt);
xfrm_params->dir = SAB_DIRECTION_OUTBOUND;
ret = mtk_xfrm_offload_context_setup(xfrm_params);
if (ret)
goto free_cdrt;
return ret;
free_cdrt:
mtk_pce_cdrt_entry_free(xfrm_params->cdrt);
return ret;
}
static int mtk_xfrm_offload_state_add_inbound(struct xfrm_state *xs,
struct mtk_xfrm_params *xfrm_params)
{
int ret;
xfrm_params->cdrt = mtk_pce_cdrt_entry_alloc(CDRT_DECRYPT);
if (IS_ERR(xfrm_params->cdrt))
return PTR_ERR(xfrm_params->cdrt);
xfrm_params->dir = SAB_DIRECTION_INBOUND;
ret = mtk_xfrm_offload_context_setup(xfrm_params);
if (ret)
goto free_cdrt;
ret = mtk_xfrm_offload_cls_entry_setup(xfrm_params);
if (ret)
goto tear_down_context;
return ret;
tear_down_context:
mtk_xfrm_offload_context_tear_down(xfrm_params);
free_cdrt:
mtk_pce_cdrt_entry_free(xfrm_params->cdrt);
return ret;
}
int mtk_xfrm_offload_state_add(struct xfrm_state *xs)
{
struct mtk_xfrm_params *xfrm_params;
int ret = 0;
/* TODO: maybe support IPv6 in the future? */
if (xs->props.family != AF_INET) {
CRYPTO_NOTICE("Only IPv4 xfrm states may be offloaded\n");
return -EINVAL;
}
/* only support ESP right now */
if (xs->id.proto != IPPROTO_ESP) {
CRYPTO_NOTICE("Unsupported protocol 0x%04x\n", xs->id.proto);
return -EINVAL;
}
/* only support tunnel mode or transport mode */
if (!(xs->outer_mode.encap == XFRM_MODE_TUNNEL
|| xs->outer_mode.encap == XFRM_MODE_TRANSPORT))
return -EINVAL;
xfrm_params = devm_kzalloc(crypto_dev,
sizeof(struct mtk_xfrm_params),
GFP_KERNEL);
if (!xfrm_params)
return -ENOMEM;
xfrm_params->xs = xs;
INIT_LIST_HEAD(&xfrm_params->node);
if (xs->xso.flags & XFRM_OFFLOAD_INBOUND)
/* rx path */
ret = mtk_xfrm_offload_state_add_inbound(xs, xfrm_params);
else
/* tx path */
ret = mtk_xfrm_offload_state_add_outbound(xs, xfrm_params);
if (ret) {
devm_kfree(crypto_dev, xfrm_params);
goto out;
}
xs->xso.offload_handle = (unsigned long)xfrm_params;
list_add_tail(&xfrm_params->node, &xfrm_params_head);
out:
return ret;
}
void mtk_xfrm_offload_state_delete(struct xfrm_state *xs)
{
}
void mtk_xfrm_offload_state_free(struct xfrm_state *xs)
{
struct mtk_xfrm_params *xfrm_params;
if (!xs->xso.offload_handle)
return;
xfrm_params = (struct mtk_xfrm_params *)xs->xso.offload_handle;
list_del(&xfrm_params->node);
if (xs->xso.flags & XFRM_OFFLOAD_INBOUND)
mtk_pce_cdrt_entry_free(xfrm_params->cdrt);
mtk_xfrm_offload_context_tear_down(xfrm_params);
mtk_pce_cdrt_entry_free(xfrm_params->cdrt);
devm_kfree(crypto_dev, xfrm_params);
}
void mtk_xfrm_offload_state_tear_down(void)
{
struct mtk_xfrm_params *xfrm_params, *tmp;
list_for_each_entry_safe(xfrm_params, tmp, &xfrm_params_head, node)
mtk_xfrm_offload_state_free(xfrm_params->xs);
}
int mtk_xfrm_offload_policy_add(struct xfrm_policy *xp)
{
return 0;
}
bool mtk_xfrm_offload_ok(struct sk_buff *skb,
struct xfrm_state *xs)
{
struct mtk_xfrm_params *xfrm_params;
xfrm_params = (struct mtk_xfrm_params *)xs->xso.offload_handle;
skb_hnat_cdrt(skb) = xfrm_params->cdrt->idx;
return false;
}