nvethernet: Add support for MACsec controller

This commit adds the driver interface for the
MACsec controller. The driver interface is
invoked using generic netlink messages from
userspace MACsec key agreement agent.

Currently sysfs node is added to check the irq
stats for default macsec controller operation.

Bug 2913560

Change-Id: I07b0b778ba1c6674e87b103a3e68e158fea61c2c
Signed-off-by: Srinivas Ramachandran <srinivasra@nvidia.com>
This commit is contained in:
Srinivas Ramachandran
2020-11-30 15:12:48 -08:00
committed by Revanth Kumar Uppala
parent c651869a94
commit 805fe9c4fa
6 changed files with 2958 additions and 7 deletions

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2018-2020, NVIDIA CORPORATION. All rights reserved.
# Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
@@ -21,13 +21,17 @@ obj-$(CONFIG_NVETHERNET) += nvethernet.o
ccflags-y += -DTHERMAL_CAL -DLINUX_IVC -I$(srctree.nvidia)/drivers/net/ethernet/nvidia/nvethernet/nvethernetrm/include \
-I$(srctree.nvidia)/drivers/net/ethernet/nvidia/nvethernet/nvethernetrm/osi/common/include
ccflags-y += -DMACSEC_SUPPORT -DNET30 -DMACSEC_DEBUG
nvethernet-objs:= ether_linux.o \
osd.o \
ethtool.o \
sysfs.o \
ioctl.o \
ptp.o \
macsec.o \
$(OSI_CORE)/osi_core.o \
$(OSI_CORE)/macsec.o \
$(OSI_COMMON)/osi_common.o \
$(OSI_COMMON)/eqos_common.o \
$(OSI_COMMON)/mgbe_common.o \

View File

@@ -804,7 +804,7 @@ static int ether_init_ivc(struct ether_priv_data *pdata)
}
dev_info(dev, "Reserved IVC channel #%u - frame_size=%d irq %d\n",
id, ictxt->ivck->frame_size, ictxt->ivck->irq);
id, ictxt->ivck->frame_size, ictxt->ivck->irq);
osi_core->osd_ops.ivc_send = osd_ivc_send_cmd;
init_completion(&ictxt->msg_complete);
ether_start_ivc(pdata);
@@ -1603,6 +1603,14 @@ static void ether_reset(struct ether_priv_data *pdata)
unsigned int val;
void __iomem *addr = devm_ioremap(pdata->dev, 0x21460018, 0x4);
#if 1 /* Rakesh WAR for AN 0 issue */
/* power ungate and ether_reset will be moved to bpmp fw so reset()
* api should be called. TODO HACK by writing oxffff to
* CLK_RST_CONTROLLER_RST_DEV_MGBE_0
*/
writel(0xffff, addr);
#endif
val = readl(addr);
val &= ~BIT(0);
writel(val, addr);
@@ -1614,6 +1622,15 @@ static void ether_reset(struct ether_priv_data *pdata)
val = readl(addr);
val &= ~BIT(8);
writel(val, addr);
#if 1
/* NET30 enables secure reset, WAR to reset macsec secure RST, this
* should be moved to bbmp fw
*/
val = readl(addr);
val &= ~BIT(12);
writel(val, addr);
#endif
}
static void power_ungate(struct ether_priv_data *pdata)
@@ -1776,6 +1793,17 @@ static int ether_open(struct net_device *dev)
/* Enable napi before requesting irq to be ready to handle it */
ether_napi_enable(pdata);
#ifdef MACSEC_SUPPORT
#ifdef DEBUG_MACSEC
ret = macsec_open(pdata->macsec_pdata);
if (ret < 0) {
dev_err(&dev->dev, "%s: failed to open macsec with reason %d\n",
__func__, ret);
goto err_macsec_open;
}
#endif
#endif /* MACSEC_SUPPORT */
/* request tx/rx/common irq */
ret = ether_request_irqs(pdata);
if (ret < 0) {
@@ -1802,6 +1830,11 @@ static int ether_open(struct net_device *dev)
return ret;
#ifdef MACSEC_SUPPORT
#ifdef DEBUG_MACSEC
err_macsec_open:
#endif
#endif /* MACSEC_SUPPORT */
err_r_irq:
ether_napi_disable(pdata);
ether_ptp_remove(pdata);
@@ -1944,6 +1977,15 @@ static int ether_close(struct net_device *ndev)
/* Disable clock */
ether_disable_clks(pdata);
#ifdef MACSEC_SUPPORT
#ifdef DEBUG_MACSEC
ret = macsec_close(pdata->macsec_pdata);
if (ret < 0) {
dev_err(pdata->dev, "Failed to close macsec");
}
#endif
#endif /* MACSEC_SUPPORT */
/* Reset stats since interface is going down */
ether_reset_stats(pdata);
@@ -4482,6 +4524,11 @@ static int ether_probe(struct platform_device *pdev)
struct net_device *ndev;
int ret = 0, i;
#ifdef TEST
macsec_genl_register();
return 0;
#endif /* TEST */
ether_get_num_dma_chan_mtl_q(pdev, &num_dma_chans,
&mac, &num_mtl_queues);
@@ -4619,6 +4666,28 @@ static int ether_probe(struct platform_device *pdev)
ether_tx_usecs_hrtimer;
}
#ifdef MACSEC_SUPPORT
ret = macsec_probe(pdata);
if (ret < 0) {
dev_err(&pdev->dev, "failed to setup macsec\n");
goto err_macsec;
} else if (ret == 0) {
/* Macsec is initialized, reduce MTU
* TODO: MTU_ADDONS also to be reduced ?
*/
pdata->max_platform_mtu -= MACSEC_TAG_ICV_LEN;
ndev->max_mtu = pdata->max_platform_mtu;
osi_core->mtu -= MACSEC_TAG_ICV_LEN;
osi_dma->mtu = osi_core->mtu;
ndev->mtu = osi_core->mtu;
dev_info(&pdev->dev, "Macsec: Reduced MTU: %d Max: %d\n",
ndev->mtu, ndev->max_mtu);
} else {
; //Nothing to do, macsec is not supported
dev_info(&pdev->dev, "Macsec not enabled - ignore\n");
}
#endif /* MACSEC_SUPPORT */
ret = register_netdev(ndev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register netdev\n");
@@ -4656,6 +4725,9 @@ static int ether_probe(struct platform_device *pdev)
err_sysfs:
unregister_netdev(ndev);
err_netdev:
#ifdef MACSEC_SUPPORT
err_macsec:
#endif /* MACSEC_SUPPORT */
err_napi:
mdiobus_unregister(pdata->mii);
err_dma_mask:
@@ -4688,6 +4760,14 @@ static int ether_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct ether_priv_data *pdata = netdev_priv(ndev);
#ifdef MACSEC_SUPPORT
#ifdef TEST
macsec_genl_unregister();
return 0;
#endif /* TEST */
macsec_remove(pdata);
#endif /* MACSEC_SUPPORT */
unregister_netdev(ndev);
/* remove nvethernet sysfs group under /sys/devices/<ether_device>/ */

View File

@@ -52,7 +52,9 @@
#include <osi_dma.h>
#include <mmc.h>
#include "ioctl.h"
#ifdef MACSEC_SUPPORT
#include "macsec.h"
#endif
/**
* @brief Max number of Ethernet IRQs supported in HW
*/
@@ -271,7 +273,7 @@ struct ether_ivc_ctxt {
* @brief Ethernet driver private data
*/
struct ether_priv_data {
/** OSI core private data */
/** OSI core private data */
struct osi_core_priv_data *osi_core;
/** OSI DMA private data */
struct osi_dma_priv_data *osi_dma;
@@ -342,7 +344,7 @@ struct ether_priv_data {
unsigned int txq_prio[OSI_MGBE_MAX_NUM_CHANS];
#ifdef THERMAL_CAL
/** Pointer to thermal cooling device which this driver registers
/** Pointer to thermal cooling device which this driver registers
* with the kernel. Kernel will invoke the callback ops for this
* cooling device when temperate in thermal zone defined in DT
* binding for this driver is tripped */
@@ -405,6 +407,10 @@ struct ether_priv_data {
/** Register dump debug fs pointer */
struct dentry *dbgfs_reg_dump;
#endif
#ifdef MACSEC_SUPPORT
/** MACsec priv data */
struct macsec_priv_data *macsec_pdata;
#endif /* MACSEC_SUPPORT */
};
/**
@@ -482,7 +488,7 @@ int ether_conf_eee(struct ether_priv_data *pdata, unsigned int tx_lpi_enable);
#if IS_ENABLED(CONFIG_NVETHERNET_SELFTESTS)
void ether_selftest_run(struct net_device *dev,
struct ethtool_test *etest, u64 *buf);
struct ethtool_test *etest, u64 *buf);
void ether_selftest_get_strings(struct ether_priv_data *pdata, u8 *data);
int ether_selftest_get_count(struct ether_priv_data *pdata);
#else

View File

@@ -0,0 +1,957 @@
/*
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ether_linux.h"
static irqreturn_t macsec_s_isr(int irq, void *data)
{
struct macsec_priv_data *macsec_pdata = (struct macsec_priv_data *)data;
struct ether_priv_data *pdata = macsec_pdata->ether_pdata;
osi_macsec_s_isr(pdata->osi_core);
return IRQ_HANDLED;
}
static irqreturn_t macsec_ns_isr(int irq, void *data)
{
struct macsec_priv_data *macsec_pdata = (struct macsec_priv_data *)data;
struct ether_priv_data *pdata = macsec_pdata->ether_pdata;
osi_macsec_ns_isr(pdata->osi_core);
return IRQ_HANDLED;
}
static int macsec_disable_car(struct macsec_priv_data *macsec_pdata)
{
struct ether_priv_data *pdata = macsec_pdata->ether_pdata;
struct device *dev = pdata->dev;
void __iomem *addr = NULL;
unsigned int val = 0;
PRINT_ENTRY();
if (!pdata->osi_core->pre_si) {
if (macsec_pdata->ns_rst) {
reset_control_assert(macsec_pdata->ns_rst);
}
if (!IS_ERR_OR_NULL(macsec_pdata->apb_pclk)) {
clk_disable_unprepare(macsec_pdata->apb_pclk);
}
if (!IS_ERR_OR_NULL(macsec_pdata->tx_clk)) {
clk_disable_unprepare(macsec_pdata->tx_clk);
}
if (!IS_ERR_OR_NULL(macsec_pdata->rx_clk)) {
clk_disable_unprepare(macsec_pdata->rx_clk);
}
} else {
/* For Pre-sil only, reset the MACsec controller directly.
* Assert the reset Bit 8 in CLK_RST_CONTROLLER_RST_DEV_MGBE_0.
*/
addr = devm_ioremap(dev, 0x21460018, 0x4);
if (addr) {
val = readl(addr);
val |= BIT(8);
writel(val, addr);
devm_iounmap(dev, addr);
}
addr = devm_ioremap(dev, 0x21460080, 0x4);
if (addr) {
val = readl(addr);
val &= ~BIT(2);
writel(val, addr);
devm_iounmap(dev, addr);
}
}
PRINT_EXIT();
return 0;
}
static int macsec_enable_car(struct macsec_priv_data *macsec_pdata)
{
struct ether_priv_data *pdata = macsec_pdata->ether_pdata;
struct device *dev = pdata->dev;
void __iomem *addr = NULL;
unsigned int val = 0;
int ret = 0;
PRINT_ENTRY();
if (!pdata->osi_core->pre_si) {
if (!IS_ERR_OR_NULL(macsec_pdata->apb_pclk)) {
ret = clk_prepare_enable(macsec_pdata->apb_pclk);
if (ret < 0) {
dev_err(dev, "failed to enable macsec pclk\n");
goto exit;
}
}
if (!IS_ERR_OR_NULL(macsec_pdata->tx_clk)) {
ret = clk_prepare_enable(macsec_pdata->tx_clk);
if (ret < 0) {
dev_err(dev,
"failed to enable macsec_tx_clk\n");
goto err_tx_clk;
}
}
if (!IS_ERR_OR_NULL(macsec_pdata->rx_clk)) {
ret = clk_prepare_enable(macsec_pdata->rx_clk);
if (ret < 0) {
dev_err(dev,
"failed to enable macsec_rx_clk\n");
goto err_rx_clk;
}
}
/* TODO:
* 1. Any delay needed in silicon for clocks to stabilize ?
*/
if (macsec_pdata->ns_rst) {
ret = reset_control_reset(macsec_pdata->ns_rst);
if (ret < 0) {
dev_err(dev, "failed to reset macsec\n");
goto err_ns_rst;
}
}
} else {
/* For Pre-sil only, reset the MACsec controller directly.
* clk ungate first, followed by disabling reset
* Bit 8 in CLK_RST_CONTROLLER_RST_DEV_MGBE_0 register.
*/
addr = devm_ioremap(dev, 0x21460080, 0x4);
if (addr) {
val = readl(addr);
val |= BIT(2);
writel(val, addr);
devm_iounmap(dev, addr);
}
/* Followed by disabling reset - Bit 8 in
* CLK_RST_CONTROLLER_RST_DEV_MGBE_0 register.
*/
addr = devm_ioremap(dev, 0x21460018, 0x4);
if (addr) {
val = readl(addr);
val &= ~BIT(8);
writel(val, addr);
devm_iounmap(dev, addr);
}
}
goto exit;
err_ns_rst:
if (!IS_ERR_OR_NULL(macsec_pdata->rx_clk)) {
clk_disable_unprepare(macsec_pdata->rx_clk);
}
err_rx_clk:
if (!IS_ERR_OR_NULL(macsec_pdata->tx_clk)) {
clk_disable_unprepare(macsec_pdata->tx_clk);
}
err_tx_clk:
if (!IS_ERR_OR_NULL(macsec_pdata->apb_pclk)) {
clk_disable_unprepare(macsec_pdata->apb_pclk);
}
exit:
PRINT_EXIT();
return ret;
}
int macsec_close(struct macsec_priv_data *macsec_pdata)
{
struct ether_priv_data *pdata = macsec_pdata->ether_pdata;
struct device *dev = pdata->dev;
int ret = 0;
PRINT_ENTRY();
#ifdef DEBUG_MACSEC
macsec_disable_car(macsec_pdata);
#endif
/* 1. Disable the macsec controller */
ret = osi_macsec_en(pdata->osi_core, OSI_DISABLE);
if (ret < 0) {
dev_err(dev, "%s: Failed to enable macsec Tx/Rx, %d\n",
__func__, ret);
return ret;
}
macsec_pdata->enabled = OSI_DISABLE;
osi_macsec_deinit(pdata->osi_core);
devm_free_irq(dev, macsec_pdata->ns_irq, macsec_pdata);
devm_free_irq(dev, macsec_pdata->s_irq, macsec_pdata);
PRINT_EXIT();
return ret;
}
int macsec_open(struct macsec_priv_data *macsec_pdata)
{
struct ether_priv_data *pdata = macsec_pdata->ether_pdata;
struct device *dev = pdata->dev;
int ret = 0;
PRINT_ENTRY();
/* 1. Request macsec irqs */
snprintf(macsec_pdata->irq_name[0], MACSEC_IRQ_NAME_SZ, "%s.macsec_s",
netdev_name(pdata->ndev));
ret = devm_request_irq(dev, macsec_pdata->s_irq, macsec_s_isr,
IRQF_TRIGGER_NONE, macsec_pdata->irq_name[0],
macsec_pdata);
if (ret < 0) {
dev_err(dev, "failed to request irq %d\n", __LINE__);
goto exit;
}
pr_err("%s: requested s_irq %d: %s\n", __func__, macsec_pdata->s_irq,
macsec_pdata->irq_name[0]);
snprintf(macsec_pdata->irq_name[1], MACSEC_IRQ_NAME_SZ, "%s.macsec_ns",
netdev_name(pdata->ndev));
ret = devm_request_irq(dev, macsec_pdata->ns_irq, macsec_ns_isr,
IRQF_TRIGGER_NONE, macsec_pdata->irq_name[1],
macsec_pdata);
if (ret < 0) {
dev_err(dev, "failed to request irq %d\n", __LINE__);
goto err_ns_irq;
}
pr_err("%s: requested ns_irq %d: %s\n", __func__, macsec_pdata->ns_irq,
macsec_pdata->irq_name[1]);
#ifdef DEBUG_MACSEC
/* 2. Enable CAR */
ret = macsec_enable_car(macsec_pdata);
if (ret < 0) {
dev_err(dev, "Unable to enable macsec clks & reset\n");
goto err_car;
}
#endif
/* 3. invoke OSI HW initialization, initialize standard BYP entries */
ret = osi_macsec_init(pdata->osi_core);
if (ret < 0) {
dev_err(dev, "osi_macsec_init failed, %d\n", ret);
goto err_osi_init;
}
/* 4. Enable the macsec controller */
ret = osi_macsec_en(pdata->osi_core,
(OSI_MACSEC_TX_EN | OSI_MACSEC_RX_EN));
if (ret < 0) {
dev_err(dev, "%s: Failed to enable macsec Tx/Rx, %d\n",
__func__, ret);
goto err_osi_init;
}
macsec_pdata->enabled = (OSI_MACSEC_TX_EN | OSI_MACSEC_RX_EN);
goto exit;
err_osi_init:
#ifdef DEBUG_MACSEC
macsec_disable_car(macsec_pdata);
err_car:
#endif
devm_free_irq(dev, macsec_pdata->ns_irq, macsec_pdata);
err_ns_irq:
devm_free_irq(dev, macsec_pdata->s_irq, macsec_pdata);
exit:
PRINT_EXIT();
return ret;
}
static int macsec_get_platform_res(struct macsec_priv_data *macsec_pdata)
{
struct ether_priv_data *pdata = macsec_pdata->ether_pdata;
struct device *dev = pdata->dev;
struct platform_device *pdev = to_platform_device(dev);
int ret = 0;
PRINT_ENTRY();
if (!pdata->osi_core->pre_si) {
/* 1. Get resets */
macsec_pdata->ns_rst = devm_reset_control_get(dev,
"macsec_rst");
if (IS_ERR_OR_NULL(macsec_pdata->ns_rst)) {
dev_err(dev, "Failed to get macsec_ns_rst\n");
ret = PTR_ERR(macsec_pdata->ns_rst);
goto exit;
}
/* 2. Get clks */
macsec_pdata->apb_pclk = devm_clk_get(dev, "macsec_pclk");
if (IS_ERR(macsec_pdata->apb_pclk)) {
dev_err(dev, "failed to get macsec_pclk\n");
ret = PTR_ERR(macsec_pdata->apb_pclk);
goto exit;
}
macsec_pdata->tx_clk = devm_clk_get(dev, "macsec_tx_clk");
if (IS_ERR(macsec_pdata->tx_clk)) {
dev_err(dev, "failed to get macsec_tx_clk\n");
ret = PTR_ERR(macsec_pdata->tx_clk);
goto exit;
}
macsec_pdata->rx_clk = devm_clk_get(dev, "macsec_rx_clk");
if (IS_ERR(macsec_pdata->rx_clk)) {
dev_err(dev, "failed to get macsec_rx_clk\n");
ret = PTR_ERR(macsec_pdata->rx_clk);
goto exit;
}
}
/* 3. Get irqs */
macsec_pdata->ns_irq = platform_get_irq_byname(pdev, "macsec-ns-irq");
if (macsec_pdata->ns_irq < 0) {
dev_err(dev, "failed to get macsec-ns-irq\n");
ret = macsec_pdata->ns_irq;
goto exit;
}
macsec_pdata->s_irq = platform_get_irq_byname(pdev, "macsec-s-irq");
if (macsec_pdata->s_irq < 0) {
dev_err(dev, "failed to get macsec-s-irq\n");
ret = macsec_pdata->s_irq;
goto exit;
}
/* Sucess */
exit:
PRINT_EXIT();
return ret;
}
static void macsec_release_platform_res(struct macsec_priv_data *macsec_pdata)
{
struct ether_priv_data *pdata = macsec_pdata->ether_pdata;
struct device *dev = pdata->dev;
PRINT_ENTRY();
if (!pdata->osi_core->pre_si) {
if (!IS_ERR_OR_NULL(macsec_pdata->apb_pclk)) {
devm_clk_put(dev, macsec_pdata->apb_pclk);
}
if (!IS_ERR_OR_NULL(macsec_pdata->tx_clk)) {
devm_clk_put(dev, macsec_pdata->tx_clk);
}
if (!IS_ERR_OR_NULL(macsec_pdata->rx_clk)) {
devm_clk_put(dev, macsec_pdata->rx_clk);
}
}
PRINT_EXIT();
}
static struct macsec_priv_data *genl_to_macsec_pdata(struct genl_info *info)
{
struct nlattr **attrs = info->attrs;
struct net_device *ndev;
struct ether_priv_data *pdata = NULL;
struct macsec_priv_data *macsec_pdata = NULL;
char ifname[IFNAMSIZ];
PRINT_ENTRY();
nla_strlcpy(ifname, attrs[NV_MACSEC_ATTR_IFNAME], sizeof(ifname));
ndev = dev_get_by_name(genl_info_net(info),
ifname);
if (!ndev) {
pr_err("%s: Unable to get netdev\n", __func__);
goto exit;
}
pdata = netdev_priv(ndev);
macsec_pdata = pdata->macsec_pdata;
dev_put(ndev);
exit:
PRINT_EXIT();
return macsec_pdata;
}
static int macsec_set_prot_frames(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr **attrs = info->attrs;
struct macsec_priv_data *macsec_pdata;
int ret = 0;
PRINT_ENTRY();
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
!attrs[NV_MACSEC_ATTR_PROT_FRAMES_EN]) {
ret = -EINVAL;
goto exit;
}
macsec_pdata = genl_to_macsec_pdata(info);
if (!macsec_pdata) {
ret = -EOPNOTSUPP;
goto exit;
}
macsec_pdata->protect_frames =
nla_get_u32(attrs[NV_MACSEC_ATTR_PROT_FRAMES_EN]);
exit:
PRINT_EXIT();
return ret;
}
static int macsec_set_cipher(struct sk_buff *skb, struct genl_info *info)
{
return -1;
}
static int macsec_set_controlled_port(struct sk_buff *skb,
struct genl_info *info)
{
struct nlattr **attrs = info->attrs;
struct macsec_priv_data *macsec_pdata;
unsigned int enable = 0;
unsigned int macsec_en = 0;
int ret = 0;
PRINT_ENTRY();
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
!attrs[NV_MACSEC_ATTR_CTRL_PORT_EN]) {
ret = -EINVAL;
goto exit;
}
macsec_pdata = genl_to_macsec_pdata(info);
if (!macsec_pdata) {
ret = -EOPNOTSUPP;
goto exit;
}
enable = nla_get_u32(attrs[NV_MACSEC_ATTR_CTRL_PORT_EN]);
if (enable) {
macsec_en |= OSI_MACSEC_RX_EN;
if (macsec_pdata->protect_frames)
macsec_en |= OSI_MACSEC_TX_EN;
}
ret = osi_macsec_en(macsec_pdata->ether_pdata->osi_core, macsec_en);
if (ret < 0) {
ret = -EOPNOTSUPP;
goto exit;
}
macsec_pdata->enabled = macsec_en;
exit:
PRINT_EXIT();
return ret;
}
static int parse_sa_config(struct nlattr **attrs, struct nlattr **tb_sa,
struct osi_macsec_sc_info *sc_info)
{
if (!attrs[NV_MACSEC_ATTR_SA_CONFIG])
return -EINVAL;
if (nla_parse_nested(tb_sa, NV_MACSEC_SA_ATTR_MAX,
attrs[NV_MACSEC_ATTR_SA_CONFIG],
nv_macsec_sa_genl_policy, NULL))
return -EINVAL;
if (tb_sa[NV_MACSEC_SA_ATTR_SCI]) {
memcpy(sc_info->sci, nla_data(tb_sa[NV_MACSEC_SA_ATTR_SCI]),
sizeof(sc_info->sci));
}
if (tb_sa[NV_MACSEC_SA_ATTR_AN]) {
sc_info->curr_an = nla_get_u8(tb_sa[NV_MACSEC_SA_ATTR_AN]);
}
if (tb_sa[NV_MACSEC_SA_ATTR_PN]) {
sc_info->next_pn = nla_get_u32(tb_sa[NV_MACSEC_SA_ATTR_PN]);
}
if (tb_sa[NV_MACSEC_SA_ATTR_KEY]) {
memcpy(sc_info->sak, nla_data(tb_sa[NV_MACSEC_SA_ATTR_KEY]),
sizeof(sc_info->sak));
}
return 0;
}
static int macsec_dis_rx_sa(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr **attrs = info->attrs;
struct macsec_priv_data *macsec_pdata;
struct ether_priv_data *pdata;
struct osi_macsec_sc_info rx_sa;
struct nlattr *tb_sa[NUM_NV_MACSEC_SA_ATTR];
int ret = 0, i = 0;
PRINT_ENTRY();
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
parse_sa_config(attrs, tb_sa, &rx_sa)) {
pr_err("%s: failed to parse nlattrs", __func__);
ret = -EINVAL;
goto exit;
}
macsec_pdata = genl_to_macsec_pdata(info);
if (macsec_pdata) {
pdata = macsec_pdata->ether_pdata;
} else {
#ifndef TEST
ret = -EOPNOTSUPP;
goto exit;
#endif
}
pr_err("%s:\n"
"\tsci: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n"
"\tan: %u\n"
"\tpn: %u",
__func__,
rx_sa.sci[0], rx_sa.sci[1], rx_sa.sci[2], rx_sa.sci[3],
rx_sa.sci[4], rx_sa.sci[5], rx_sa.sci[6], rx_sa.sci[7],
rx_sa.curr_an, rx_sa.next_pn);
pr_err("\tkey: ");
for (i = 0; i < 16; i++) {
pr_cont(" %02x", rx_sa.sak[i]);
}
pr_err("");
#ifndef TEST
ret = osi_macsec_config(pdata->osi_core, &rx_sa, OSI_DISABLE,
CTLR_SEL_RX);
if (ret < 0) {
pr_err("%s: failed to disable Rx SA", __func__);
}
#endif
exit:
PRINT_EXIT();
return ret;
}
static int macsec_en_rx_sa(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr **attrs = info->attrs;
struct macsec_priv_data *macsec_pdata;
struct ether_priv_data *pdata;
struct osi_macsec_sc_info rx_sa;
struct nlattr *tb_sa[NUM_NV_MACSEC_SA_ATTR];
int ret = 0, i = 0;
PRINT_ENTRY();
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
parse_sa_config(attrs, tb_sa, &rx_sa)) {
pr_err("%s: failed to parse nlattrs", __func__);
ret = -EINVAL;
goto exit;
}
macsec_pdata = genl_to_macsec_pdata(info);
if (macsec_pdata) {
pdata = macsec_pdata->ether_pdata;
} else {
#ifndef TEST
ret = -EOPNOTSUPP;
goto exit;
#endif
}
pr_err("%s:\n"
"\tsci: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n"
"\tan: %u\n"
"\tpn: %u",
__func__,
rx_sa.sci[0], rx_sa.sci[1], rx_sa.sci[2], rx_sa.sci[3],
rx_sa.sci[4], rx_sa.sci[5], rx_sa.sci[6], rx_sa.sci[7],
rx_sa.curr_an, rx_sa.next_pn);
pr_err("\tkey: ");
for (i = 0; i < 16; i++) {
pr_cont(" %02x", rx_sa.sak[i]);
}
pr_err("");
#ifndef TEST
ret = osi_macsec_config(pdata->osi_core, &rx_sa, OSI_ENABLE,
CTLR_SEL_RX);
if (ret < 0) {
pr_err("%s: failed to enable Rx SA", __func__);
}
#endif
exit:
PRINT_EXIT();
return ret;
}
static int macsec_dis_tx_sa(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr **attrs = info->attrs;
struct macsec_priv_data *macsec_pdata;
struct ether_priv_data *pdata;
struct osi_macsec_sc_info tx_sa;
struct nlattr *tb_sa[NUM_NV_MACSEC_SA_ATTR];
int ret = 0, i = 0;
PRINT_ENTRY();
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
parse_sa_config(attrs, tb_sa, &tx_sa)) {
pr_err("%s: failed to parse nlattrs", __func__);
ret = -EINVAL;
goto exit;
}
macsec_pdata = genl_to_macsec_pdata(info);
if (macsec_pdata) {
pdata = macsec_pdata->ether_pdata;
} else {
#ifndef TEST
ret = -EOPNOTSUPP;
goto exit;
#endif
}
pr_err("%s:\n"
"\tsci: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n"
"\tan: %u\n"
"\tpn: %u",
__func__,
tx_sa.sci[0], tx_sa.sci[1], tx_sa.sci[2], tx_sa.sci[3],
tx_sa.sci[4], tx_sa.sci[5], tx_sa.sci[6], tx_sa.sci[7],
tx_sa.curr_an, tx_sa.next_pn);
pr_err("\tkey: ");
for (i = 0; i < 16; i++) {
pr_cont(" %02x", tx_sa.sak[i]);
}
pr_err("");
#ifndef TEST
ret = osi_macsec_config(pdata->osi_core, &tx_sa, OSI_DISABLE,
CTLR_SEL_TX);
if (ret < 0) {
pr_err("%s: failed to disable Tx SA", __func__);
}
#endif
exit:
PRINT_EXIT();
return ret;
}
static int macsec_en_tx_sa(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr **attrs = info->attrs;
struct macsec_priv_data *macsec_pdata;
struct ether_priv_data *pdata;
struct osi_macsec_sc_info tx_sa;
struct nlattr *tb_sa[NUM_NV_MACSEC_SA_ATTR];
int ret = 0, i = 0;
PRINT_ENTRY();
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
parse_sa_config(attrs, tb_sa, &tx_sa)) {
pr_err("%s: failed to parse nlattrs", __func__);
ret = -EINVAL;
goto exit;
}
macsec_pdata = genl_to_macsec_pdata(info);
if (macsec_pdata) {
pdata = macsec_pdata->ether_pdata;
} else {
#ifndef TEST
ret = -EOPNOTSUPP;
goto exit;
#endif
}
pr_err("%s:\n"
"\tsci: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n"
"\tan: %u\n"
"\tpn: %u",
__func__,
tx_sa.sci[0], tx_sa.sci[1], tx_sa.sci[2], tx_sa.sci[3],
tx_sa.sci[4], tx_sa.sci[5], tx_sa.sci[6], tx_sa.sci[7],
tx_sa.curr_an, tx_sa.next_pn);
pr_err("\tkey: ");
for (i = 0; i < 16; i++) {
pr_cont(" %02x", tx_sa.sak[i]);
}
pr_err("");
#ifndef TEST
ret = osi_macsec_config(pdata->osi_core, &tx_sa, OSI_ENABLE,
CTLR_SEL_TX);
if (ret < 0) {
pr_err("%s: failed to enable Tx SA", __func__);
}
#endif
exit:
PRINT_EXIT();
return ret;
}
static int macsec_deinit(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr **attrs = info->attrs;
struct macsec_priv_data *macsec_pdata;
int ret = 0;
PRINT_ENTRY();
if (!attrs[NV_MACSEC_ATTR_IFNAME]) {
ret = -EINVAL;
goto exit;
}
macsec_pdata = genl_to_macsec_pdata(info);
if (!macsec_pdata) {
ret = -EOPNOTSUPP;
goto exit;
}
ret = macsec_close(macsec_pdata);
//TODO - check why needs -EOPNOTSUPP, why not pass ret val
if (ret < 0) {
ret = -EOPNOTSUPP;
}
exit:
PRINT_EXIT();
return ret;
}
static int macsec_init(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr **attrs = info->attrs;
struct macsec_priv_data *macsec_pdata;
int ret = 0;
PRINT_ENTRY();
if (!attrs[NV_MACSEC_ATTR_IFNAME]) {
ret = -EINVAL;
goto exit;
}
macsec_pdata = genl_to_macsec_pdata(info);
if (!macsec_pdata) {
ret = -EOPNOTSUPP;
goto exit;
}
ret = macsec_open(macsec_pdata);
//TODO - check why needs -EOPNOTSUPP, why not pass ret val
if (ret < 0) {
ret = -EOPNOTSUPP;
}
exit:
PRINT_EXIT();
return ret;
}
static int macsec_set_replay_prot(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr **attrs = info->attrs;
unsigned int replay_prot, window;
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
!attrs[NV_MACSEC_ATTR_REPLAY_PROT_EN] ||
!attrs[NV_MACSEC_ATTR_REPLAY_WINDOW]) {
return -EINVAL;
}
replay_prot = nla_get_u32(attrs[NV_MACSEC_ATTR_REPLAY_PROT_EN]);
window = nla_get_u32(attrs[NV_MACSEC_ATTR_REPLAY_WINDOW]);
pr_err("replay_prot(window): %u(%u)\n",
replay_prot, window);
/* TODO - set replay window for all active SA's.
* Store window in macsec_pdata so that future SA's can be updated
*/
return 0;
}
static const struct genl_ops nv_macsec_genl_ops[] = {
{
.cmd = NV_MACSEC_CMD_INIT,
.doit = macsec_init,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NV_MACSEC_CMD_SET_PROT_FRAMES,
.doit = macsec_set_prot_frames,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NV_MACSEC_CMD_SET_REPLAY_PROT,
.doit = macsec_set_replay_prot,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NV_MACSEC_CMD_SET_CIPHER,
.doit = macsec_set_cipher,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NV_MACSEC_CMD_SET_CONTROLLED_PORT,
.doit = macsec_set_controlled_port,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NV_MACSEC_CMD_DEINIT,
.doit = macsec_deinit,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NV_MACSEC_CMD_EN_TX_SA,
.doit = macsec_en_tx_sa,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NV_MACSEC_CMD_DIS_TX_SA,
.doit = macsec_dis_tx_sa,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NV_MACSEC_CMD_EN_RX_SA,
.doit = macsec_en_rx_sa,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NV_MACSEC_CMD_DIS_RX_SA,
.doit = macsec_dis_rx_sa,
.flags = GENL_ADMIN_PERM,
},
};
static struct genl_family nv_macsec_fam __ro_after_init = {
.name = NV_MACSEC_GENL_NAME,
.hdrsize = 0,
.version = NV_MACSEC_GENL_VERSION,
.maxattr = NV_MACSEC_ATTR_MAX,
.module = THIS_MODULE,
.ops = nv_macsec_genl_ops,
.n_ops = ARRAY_SIZE(nv_macsec_genl_ops),
};
void macsec_remove(struct ether_priv_data *pdata)
{
struct macsec_priv_data *macsec_pdata = NULL;
PRINT_ENTRY();
macsec_pdata = pdata->macsec_pdata;
if (macsec_pdata) {
/* 1. Unregister generic netlink */
genl_unregister_family(&nv_macsec_fam);
/* 2. Release platform resources */
macsec_release_platform_res(macsec_pdata);
}
PRINT_EXIT();
}
#ifdef TEST
int macsec_genl_register(void)
{
int ret = 0;
ret = genl_register_family(&nv_macsec_fam);
if (ret < 0) {
pr_err("Srini:failed to register genl\n");
}
return ret;
}
void macsec_genl_unregister(void)
{
genl_unregister_family(&nv_macsec_fam);
}
#endif /* TEST */
int macsec_probe(struct ether_priv_data *pdata)
{
struct device *dev = pdata->dev;
struct platform_device *pdev = to_platform_device(dev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct macsec_priv_data *macsec_pdata = NULL;
struct resource *res = NULL;
int ret = 0;
PRINT_ENTRY();
/* 1. Check if MACsec is enabled in DT, if so map the I/O base addr */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "macsec-base");
if (res) {
osi_core->macsec_base = devm_ioremap_resource(dev, res);
if (IS_ERR(osi_core->macsec_base)) {
dev_err(dev, "failed to ioremap MACsec base addr\n");
ret = PTR_ERR(osi_core->macsec_base);
goto exit;
}
} else {
/* MACsec not enabled in DT, nothing more to do */
osi_core->macsec_base = NULL;
osi_core->tz_base = NULL;
pdata->macsec_pdata = NULL;
/* Return positive value to indicate MACsec not enabled in DT */
ret = 1;
goto exit;
}
//TODO: Move to TZ window
osi_core->tz_base = devm_ioremap(dev, 0x68C0000, 0x10000);
if (IS_ERR(osi_core->tz_base)) {
dev_err(dev, "failed to ioremap TZ base addr\n");
ret = PTR_ERR(osi_core->tz_base);
goto exit;
}
/* 2. Alloc macsec priv data structure */
macsec_pdata = devm_kzalloc(dev, sizeof(struct macsec_priv_data),
GFP_KERNEL);
if (macsec_pdata == NULL) {
dev_err(dev, "failed to alloc macsec_priv_data\n");
ret = -ENOMEM;
goto exit;
}
macsec_pdata->ether_pdata = pdata;
pdata->macsec_pdata = macsec_pdata;
/* 3. Get OSI MACsec ops */
if (osi_init_macsec_ops(osi_core) != 0) {
dev_err(dev, "osi_init_macsec_ops failed\n");
ret = -1;
goto exit;
}
/* 4. Get platform resources - clks, resets, irqs.
* CAR is not enabled and irqs not requested until macsec_init()
*/
ret = macsec_get_platform_res(macsec_pdata);
if (ret < 0) {
dev_err(dev, "macsec_get_platform_res failed\n");
goto exit;
}
/* 2. Enable CAR */
ret = macsec_enable_car(macsec_pdata);
if (ret < 0) {
dev_err(dev, "Unable to enable macsec clks & reset\n");
goto exit;
}
/* 5. Register macsec sysfs node - done from sysfs.c */
/* 6. Register macsec generic netlink ops */
ret = genl_register_family(&nv_macsec_fam);
if (ret) {
dev_err(dev, "Failed to register GENL ops\n");
macsec_disable_car(macsec_pdata);
}
exit:
PRINT_EXIT();
return ret;
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef INCLUDED_MACSEC_H
#define INCLUDED_MACSEC_H
#include <osi_macsec.h>
#include <linux/random.h>
#include <net/genetlink.h>
#include <linux/crypto.h>
//#define TEST 1
/**
* @brief Size of Macsec IRQ name.
*/
#define MACSEC_IRQ_NAME_SZ 32
//TODO - include name of driver interface as well
#define NV_MACSEC_GENL_NAME "nv_macsec"
#define NV_MACSEC_GENL_VERSION 1
enum nv_macsec_sa_attrs {
NV_MACSEC_SA_ATTR_UNSPEC,
NV_MACSEC_SA_ATTR_SCI,
NV_MACSEC_SA_ATTR_AN,
NV_MACSEC_SA_ATTR_PN,
NV_MACSEC_SA_ATTR_KEY,
__NV_MACSEC_SA_ATTR_END,
NUM_NV_MACSEC_SA_ATTR = __NV_MACSEC_SA_ATTR_END,
NV_MACSEC_SA_ATTR_MAX = __NV_MACSEC_SA_ATTR_END - 1,
};
enum nv_macsec_attrs {
NV_MACSEC_ATTR_UNSPEC,
NV_MACSEC_ATTR_IFNAME,
NV_MACSEC_ATTR_TXSC_PORT,
NV_MACSEC_ATTR_PROT_FRAMES_EN,
NV_MACSEC_ATTR_REPLAY_PROT_EN,
NV_MACSEC_ATTR_REPLAY_WINDOW,
NV_MACSEC_ATTR_CIPHER_SUITE,
NV_MACSEC_ATTR_CTRL_PORT_EN,
NV_MACSEC_ATTR_SA_CONFIG, /* Nested SA config */
__NV_MACSEC_ATTR_END,
NUM_NV_MACSEC_ATTR = __NV_MACSEC_ATTR_END,
NV_MACSEC_ATTR_MAX = __NV_MACSEC_ATTR_END - 1,
};
static const struct nla_policy nv_macsec_sa_genl_policy[NUM_NV_MACSEC_SA_ATTR] = {
[NV_MACSEC_SA_ATTR_SCI] = { .type = NLA_BINARY,
.len = 8, }, /* SCI is 64bit */
[NV_MACSEC_SA_ATTR_AN] = { .type = NLA_U8 },
[NV_MACSEC_SA_ATTR_PN] = { .type = NLA_U32 },
[NV_MACSEC_SA_ATTR_KEY] = { .type = NLA_BINARY,
.len = KEY_LEN_128,},
};
static const struct nla_policy nv_macsec_genl_policy[NUM_NV_MACSEC_ATTR] = {
[NV_MACSEC_ATTR_IFNAME] = { .type = NLA_STRING },
[NV_MACSEC_ATTR_TXSC_PORT] = { .type = NLA_U16 },
[NV_MACSEC_ATTR_REPLAY_PROT_EN] = { .type = NLA_U32 },
[NV_MACSEC_ATTR_REPLAY_WINDOW] = { .type = NLA_U32 },
[NV_MACSEC_ATTR_SA_CONFIG] = { .type = NLA_NESTED },
};
enum nv_macsec_nl_commands {
NV_MACSEC_CMD_INIT,
NV_MACSEC_CMD_GET_TX_NEXT_PN,
NV_MACSEC_CMD_SET_PROT_FRAMES,
NV_MACSEC_CMD_SET_REPLAY_PROT,
NV_MACSEC_CMD_SET_CIPHER,
NV_MACSEC_CMD_SET_CONTROLLED_PORT,
NV_MACSEC_CMD_EN_TX_SA,
NV_MACSEC_CMD_DIS_TX_SA,
NV_MACSEC_CMD_EN_RX_SA,
NV_MACSEC_CMD_DIS_RX_SA,
NV_MACSEC_CMD_DEINIT,
};
/**
* @brief MACsec private data structure
*/
struct macsec_priv_data {
/** Non secure reset */
struct reset_control *ns_rst;
/** APB interface clk - macsec_pclk */
struct clk *apb_pclk;
/** macsec Tx clock */
struct clk *tx_clk;
/** macsec Rx clock */
struct clk *rx_clk;
/** Secure irq */
int s_irq;
/** Non secure irq */
int ns_irq;
/** pointer to ether private data struct */
struct ether_priv_data *ether_pdata;
/** macsec IRQ name strings */
char irq_name[2][MACSEC_IRQ_NAME_SZ];
/** loopback mode */
unsigned int loopback_mode;
/** MACsec protect frames variable */
unsigned int protect_frames;
/** MACsec enabled flags for Tx/Rx controller status */
unsigned int enabled;
/** MACsec controller initialized flag */
unsigned int init_done;
};
int macsec_probe(struct ether_priv_data *pdata);
void macsec_remove(struct ether_priv_data *pdata);
int macsec_open(struct macsec_priv_data *macsec_pdata);
int macsec_close(struct macsec_priv_data *macsec_pdata);
#ifdef TEST
int macsec_genl_register(void);
void macsec_genl_unregister(void);
#endif /* TEST */
#ifdef MACSEC_DEBUG
#define PRINT_ENTRY() (pr_info("-->%s()\n", __func__))
#define PRINT_EXIT() (pr_info("<--%s()\n", __func__))
#else
#define PRINT_ENTRY()
#define PRINT_EXIT()
#endif /* MACSEC_DEBUG */
#endif /* INCLUDED_MACSEC_H */

View File

File diff suppressed because it is too large Load Diff