From 805fe9c4fabcd9abb976ca7375d9b620a45d1a66 Mon Sep 17 00:00:00 2001 From: Srinivas Ramachandran Date: Mon, 30 Nov 2020 15:12:48 -0800 Subject: [PATCH] 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 --- .../net/ethernet/nvidia/nvethernet/Makefile | 6 +- .../ethernet/nvidia/nvethernet/ether_linux.c | 82 +- .../ethernet/nvidia/nvethernet/ether_linux.h | 14 +- .../net/ethernet/nvidia/nvethernet/macsec.c | 957 +++++++++ .../net/ethernet/nvidia/nvethernet/macsec.h | 147 ++ .../net/ethernet/nvidia/nvethernet/sysfs.c | 1759 ++++++++++++++++- 6 files changed, 2958 insertions(+), 7 deletions(-) create mode 100644 drivers/net/ethernet/nvidia/nvethernet/macsec.c create mode 100644 drivers/net/ethernet/nvidia/nvethernet/macsec.h diff --git a/drivers/net/ethernet/nvidia/nvethernet/Makefile b/drivers/net/ethernet/nvidia/nvethernet/Makefile index 9b47ddae..eb08e80b 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/Makefile +++ b/drivers/net/ethernet/nvidia/nvethernet/Makefile @@ -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 \ diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index 8b7bf7d5..9d6dc2fa 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -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// */ diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h index 630892d3..1dd63623 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h @@ -52,7 +52,9 @@ #include #include #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 diff --git a/drivers/net/ethernet/nvidia/nvethernet/macsec.c b/drivers/net/ethernet/nvidia/nvethernet/macsec.c new file mode 100644 index 00000000..87863149 --- /dev/null +++ b/drivers/net/ethernet/nvidia/nvethernet/macsec.c @@ -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 . + */ + +#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; +} diff --git a/drivers/net/ethernet/nvidia/nvethernet/macsec.h b/drivers/net/ethernet/nvidia/nvethernet/macsec.h new file mode 100644 index 00000000..742e147e --- /dev/null +++ b/drivers/net/ethernet/nvidia/nvethernet/macsec.h @@ -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 +#include +#include +#include + +//#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 */ + diff --git a/drivers/net/ethernet/nvidia/nvethernet/sysfs.c b/drivers/net/ethernet/nvidia/nvethernet/sysfs.c index 9b261a7e..7c62e081 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/sysfs.c +++ b/drivers/net/ethernet/nvidia/nvethernet/sysfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-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, @@ -113,6 +113,1747 @@ static ssize_t ether_mac_loopback_store(struct device *dev, return size; } +#ifdef MACSEC_SUPPORT +/** + * @brief Shows the current setting of MACsec controllers enabled + * + * Algorithm: Display the current MACsec controllers enabled (Tx/Rx). + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer to store the current MACsec loopback setting + */ +static ssize_t macsec_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct macsec_priv_data *macsec_pdata = pdata->macsec_pdata; + unsigned int enabled = macsec_pdata->enabled; + return scnprintf(buf, PAGE_SIZE, "%s\n", + (enabled == (OSI_MACSEC_TX_EN | OSI_MACSEC_RX_EN)) + ? "txrx" : + (enabled == OSI_MACSEC_TX_EN) ? "tx" : + (enabled == OSI_MACSEC_RX_EN) ? "rx" : + "None"); +} + +extern int macsec_open(struct macsec_priv_data *macsec_pdata); +extern int macsec_close(struct macsec_priv_data *macsec_pdata); + +/** + * @brief Set the MACsec controller enabled (Tx/Rx) + * + * Algorithm: This is used to set the Tx/Rx MACsec controller enabled. + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer which contains the user settings of MACsec loopback + * @param[in] size: size of buffer + * + * @return size of buffer. + */ +static ssize_t macsec_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + struct macsec_priv_data *macsec_pdata = pdata->macsec_pdata; + unsigned int enable = 0; + int ret = 0; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return size; + } + + if (strncmp(buf, "0", 1) == OSI_NONE) { + ret = macsec_close(macsec_pdata); + } else if (strncmp(buf, "txrx", 4) == OSI_NONE) { + ret = macsec_open(macsec_pdata); + } else if (strncmp(buf, "tx", 2) == OSI_NONE) { + if (macsec_pdata->enabled == OSI_NONE) { + ret = macsec_open(macsec_pdata); + } + enable |= (OSI_MACSEC_TX_EN); + ret = osi_macsec_en(osi_core, enable); + if (ret < 0) { + dev_err(dev, "%s: Failed to enable macsec Tx\n", + __func__); + } + macsec_pdata->enabled = OSI_MACSEC_TX_EN; + } else if (strncmp(buf, "rx", 2) == OSI_NONE) { + if (macsec_pdata->enabled == OSI_NONE) { + ret = macsec_open(macsec_pdata); + } + enable |= (OSI_MACSEC_RX_EN); + ret = osi_macsec_en(osi_core, enable); + if (ret < 0) { + dev_err(dev, "%s: Failed to enable macsec Rx\n", + __func__); + } + macsec_pdata->enabled = OSI_MACSEC_RX_EN; + } else { + dev_err(pdata->dev, + "Invalid. Valid inputs are 0/tx/rx/txrx\n"); + } + + return size; +} + +/** + * @brief Sysfs attribute for MACsec enable + * + */ +static DEVICE_ATTR(macsec_enable, (S_IRUGO | S_IWUSR), + macsec_enable_show, + macsec_enable_store); + +/** + * @brief Shows the current setting of MACsec loopback + * + * Algorithm: Display the current MACsec loopback setting. + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer to store the current MACsec loopback setting + */ +static ssize_t macsec_loopback_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct macsec_priv_data *macsec_pdata = pdata->macsec_pdata; + return scnprintf(buf, PAGE_SIZE, "%s\n", + (macsec_pdata->loopback_mode == OSI_ENABLE) ? + "enabled" : "disabled"); +} + +/** + * @brief Set the user setting of MACsec loopback mode + * + * Algorithm: This is used to set the user mode settings of MACsec loopback. + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer which contains the user settings of MACsec loopback + * @param[in] size: size of buffer + * + * @return size of buffer. + */ +static ssize_t macsec_loopback_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct macsec_priv_data *macsec_pdata = pdata->macsec_pdata; + int ret = 0; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return size; + } + + if (strncmp(buf, "enable", 6) == OSI_NONE) { + ret = osi_macsec_loopback(pdata->osi_core, OSI_ENABLE); + if (ret < 0) { + dev_err(pdata->dev, + "Failed to enable macsec loopback\n"); + } else { + macsec_pdata->loopback_mode = OSI_ENABLE; + dev_info(pdata->dev, "Enabled macsec Loopback\n"); + } + } else if (strncmp(buf, "disable", 7) == OSI_NONE) { + ret = osi_macsec_loopback(pdata->osi_core, OSI_DISABLE); + if (ret < 0) { + dev_err(pdata->dev, + "Failed to Disable macsec loopback\n"); + } else { + macsec_pdata->loopback_mode = OSI_DISABLE; + dev_info(pdata->dev, "Disabled macsec Loopback\n"); + } + } else if (strncmp(buf, "carrier_on", 10) == OSI_NONE) { + netif_carrier_on(ndev); + } else if (strncmp(buf, "carrier_off", 11) == OSI_NONE) { + netif_carrier_off(ndev); + } else { + dev_err(pdata->dev, + "Invalid entry. Valid Entries are " + "enable/disable/carrier_on/carrier_off\n"); + } + + return size; +} + +/** + * @brief Sysfs attribute for MACsec loopback + * + */ +static DEVICE_ATTR(macsec_loopback, (S_IRUGO | S_IWUSR), + macsec_loopback_show, + macsec_loopback_store); + +#define MAC_ADDR_FMT "%02x:%02x:%02x:%02x:%02x:%02x" +#define ETHTYPE_FMT "%02x%02x" +#define SCI_FMT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x" + +static void format_output(char **buf_p, + struct osi_macsec_lut_config *lut_config) +{ + char *buf = *buf_p; + unsigned int flags = lut_config->flags; + struct lut_inputs entry = lut_config->lut_in; + + if ((flags & LUT_FLAGS_DA_VALID) == LUT_FLAGS_DA_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "DA: " MAC_ADDR_FMT " ", + entry.da[5], entry.da[4], entry.da[3], + entry.da[2], entry.da[1], entry.da[0]); + } else { + buf += scnprintf(buf, PAGE_SIZE, "DA: X "); + } + + if ((flags & LUT_FLAGS_SA_VALID) == LUT_FLAGS_SA_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "SA: " MAC_ADDR_FMT " ", + entry.sa[5], entry.sa[4], entry.sa[3], + entry.sa[2], entry.sa[1], entry.sa[0]); + } else { + buf += scnprintf(buf, PAGE_SIZE, "SA: X "); + } + + if ((flags & LUT_FLAGS_ETHTYPE_VALID) == LUT_FLAGS_ETHTYPE_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "ethtype: " ETHTYPE_FMT " ", + entry.ethtype[1], entry.ethtype[0]); + } else { + buf += scnprintf(buf, PAGE_SIZE, "ethtype: X "); + } + + if ((flags & LUT_FLAGS_VLAN_VALID) == LUT_FLAGS_VLAN_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "vlan: "); + if ((flags & LUT_FLAGS_VLAN_PCP_VALID) == + LUT_FLAGS_VLAN_PCP_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "prio: %u ", + entry.vlan_pcp); + } else { + buf += scnprintf(buf, PAGE_SIZE, "prio: X "); + } + if ((flags & LUT_FLAGS_VLAN_ID_VALID) == + LUT_FLAGS_VLAN_ID_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "id: %u ", + entry.vlan_id); + } else { + buf += scnprintf(buf, PAGE_SIZE, "id: X "); + } + } else { + buf += scnprintf(buf, PAGE_SIZE, "vlan: X "); + } + + if ((flags & LUT_FLAGS_DVLAN_PKT) == LUT_FLAGS_DVLAN_PKT) { + buf += scnprintf(buf, PAGE_SIZE, "dvlan: 1 "); + if ((flags & LUT_FLAGS_DVLAN_OUTER_INNER_TAG_SEL) == + LUT_FLAGS_DVLAN_OUTER_INNER_TAG_SEL) { + buf += scnprintf(buf, PAGE_SIZE, "dvlan_outer_tag: 1 "); + } else { + buf += scnprintf(buf, PAGE_SIZE, "dvlan_outer_tag: 0 "); + } + } else { + buf += scnprintf(buf, PAGE_SIZE, "dvlan: X "); + } + + if ((flags & LUT_FLAGS_BYTE0_PATTERN_VALID) == + LUT_FLAGS_BYTE0_PATTERN_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "Byte0: Pattern: %x " + "offset: %u ", + entry.byte_pattern[0], + entry.byte_pattern_offset[0]); + } else { + buf += scnprintf(buf, PAGE_SIZE, "Byte0: X "); + } + + if ((flags & LUT_FLAGS_BYTE1_PATTERN_VALID) == + LUT_FLAGS_BYTE1_PATTERN_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "Byte1: Pattern: %x " + "offset: %u ", + entry.byte_pattern[1], + entry.byte_pattern_offset[1]); + } else { + buf += scnprintf(buf, PAGE_SIZE, "Byte1: X "); + } + + if ((flags & LUT_FLAGS_BYTE2_PATTERN_VALID) == + LUT_FLAGS_BYTE2_PATTERN_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "Byte2: Pattern: %x " + "offset: %u ", + entry.byte_pattern[2], + entry.byte_pattern_offset[2]); + } else { + buf += scnprintf(buf, PAGE_SIZE, "Byte2: X "); + } + + if ((flags & LUT_FLAGS_BYTE3_PATTERN_VALID) == + LUT_FLAGS_BYTE3_PATTERN_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "Byte3: Pattern: %x " + "offset: %u ", + entry.byte_pattern[3], + entry.byte_pattern_offset[3]); + } else { + buf += scnprintf(buf, PAGE_SIZE, "Byte3: X "); + } + + if ((flags & LUT_FLAGS_PREEMPT_VALID) == LUT_FLAGS_PREEMPT_VALID) { + if ((flags & LUT_FLAGS_PREEMPT) == LUT_FLAGS_PREEMPT) { + buf += scnprintf(buf, PAGE_SIZE, "prempt: 1 "); + } else { + buf += scnprintf(buf, PAGE_SIZE, "prempt: 0 "); + } + } else { + buf += scnprintf(buf, PAGE_SIZE, "prempt: X "); + } + + *buf_p = buf; + + return; +} + +#define LUT_INPUTS_LEN 37 + +static int parse_inputs(const char *buf, + struct osi_macsec_lut_config *lut_config, int *bufp) +{ + struct lut_inputs *lut_in; + int temp[OSI_ETH_ALEN] = {0}; + int temp2[OSI_ETH_ALEN] = {0}; + int temp3[LUT_BYTE_PATTERN_MAX] = {0}; + int temp4[ETHTYPE_LEN] = {0}; + unsigned char byte[LUT_BYTE_PATTERN_MAX] = {0}; + unsigned char mac_da[OSI_ETH_ALEN] = {0}; + unsigned char mac_sa[OSI_ETH_ALEN] = {0}; + unsigned char ethtype[ETHTYPE_LEN] = {0}; + unsigned int byte_offset[LUT_BYTE_PATTERN_MAX] = {0}; + unsigned int vlan_pcp = 0, vlan_id = 0, flags = 0; + unsigned short controller; + int mac_da_valid, mac_sa_valid, ethtype_valid, vlan_valid; + int dvlan, dvlan_outer_tag; + int byte_valid[LUT_BYTE_PATTERN_MAX]; + int i, valid, index;; + + + i = sscanf(buf, "%d %d %hu " + "%x:%x:%x:%x:%x:%x %d " + "%x:%x:%x:%x:%x:%x %d " + "%2x%2x %d " + "%x %u %d " + "%x %u %d " + "%x %u %d " + "%x %u %d " + "%u %u %d %d %d%n", + &valid, &index, &controller, + &temp[0], &temp[1], &temp[2], + &temp[3], &temp[4], &temp[5], &mac_da_valid, + &temp2[0], &temp2[1], &temp2[2], + &temp2[3], &temp2[4], &temp2[5], &mac_sa_valid, + &temp4[0], &temp4[1], ðtype_valid, + &temp3[0], &byte_offset[0], &byte_valid[0], + &temp3[1], &byte_offset[1], &byte_valid[1], + &temp3[2], &byte_offset[2], &byte_valid[2], + &temp3[3], &byte_offset[3], &byte_valid[3], + &vlan_pcp, &vlan_id, &vlan_valid, &dvlan, &dvlan_outer_tag, bufp); + + if (i != LUT_INPUTS_LEN) { + pr_err("%s: Invalid LUT inputs(read %d)", __func__, i); + goto err; + } + + for (i = 0; i < OSI_ETH_ALEN; i++) { + mac_da[i] = (unsigned char)temp[i]; + mac_sa[i] = (unsigned char)temp2[i]; + } + + for (i = 0; i < ETHTYPE_LEN; i++) { + ethtype[i] = (unsigned char)temp4[i]; + } + + for (i = 0; i < LUT_BYTE_PATTERN_MAX; i++) { + byte[i] = (unsigned char)temp3[i]; + } + + if (mac_da_valid && !is_valid_ether_addr(mac_da)) { + pr_err("%s: Invalid mac DA\n", __func__); + goto err; + } + + if (mac_sa_valid && !is_valid_ether_addr(mac_sa)) { + pr_err("%s: Invalid mac SA\n", __func__); + goto err; + } + + memset(lut_config, 0, sizeof(*lut_config)); + lut_config->table_config.ctlr_sel = controller; + lut_config->table_config.index = index; + lut_in = &lut_config->lut_in; + + if (mac_da_valid) { + /* Reverse endianess for HW */ + for (i = 0; i < OSI_ETH_ALEN; i++) { + lut_in->da[i] = mac_da[OSI_ETH_ALEN - 1 - i]; + } + flags |= LUT_FLAGS_DA_VALID; + } + + if (mac_sa_valid) { + /* Reverse endianess for HW */ + for (i = 0; i < OSI_ETH_ALEN; i++) { + lut_in->sa[i] = mac_sa[OSI_ETH_ALEN - 1 - i]; + } + flags |= LUT_FLAGS_SA_VALID; + } + + if (ethtype_valid) { + /* Reverse endianess for HW */ + for (i = 0; i < ETHTYPE_LEN; i++) { + lut_in->ethtype[i] = ethtype[ETHTYPE_LEN - 1 - i]; + } + flags |= LUT_FLAGS_ETHTYPE_VALID; + + } + + for (i = 0; i < LUT_BYTE_PATTERN_MAX; i++) { + if (byte_valid[i]) { + switch (i) { + case 0: + flags |= LUT_FLAGS_BYTE0_PATTERN_VALID; + break; + case 1: + flags |= LUT_FLAGS_BYTE1_PATTERN_VALID; + break; + case 2: + flags |= LUT_FLAGS_BYTE2_PATTERN_VALID; + break; + case 3: + flags |= LUT_FLAGS_BYTE3_PATTERN_VALID; + break; + default: + break; + } + lut_in->byte_pattern[i] = byte[i]; + lut_in->byte_pattern_offset[i] = byte_offset[i]; + } + } + + if (vlan_valid) { + lut_in->vlan_pcp = vlan_pcp; + lut_in->vlan_id = vlan_id; + flags |= (LUT_FLAGS_VLAN_ID_VALID | LUT_FLAGS_VLAN_PCP_VALID | + LUT_FLAGS_VLAN_VALID); + } + + if (dvlan) { + flags |= LUT_FLAGS_DVLAN_PKT; + if (dvlan_outer_tag) { + flags |= LUT_FLAGS_DVLAN_OUTER_INNER_TAG_SEL; + } + } + + if (valid) { + flags |= LUT_FLAGS_ENTRY_VALID; + } + + lut_config->flags = flags; + + return 0; + +err: + return -1; +} + +static void dump_byp_lut(char **buf_p, unsigned short ctlr_sel, + struct osi_core_priv_data *osi_core) +{ + struct osi_macsec_lut_config lut_config = {0}; + char *buf = *buf_p; + int i; + + for (i = 0; i <= BYP_LUT_MAX_INDEX; i++) { + memset(&lut_config, OSI_NONE, sizeof(lut_config)); + lut_config.table_config.ctlr_sel = ctlr_sel; + lut_config.lut_sel = LUT_SEL_BYPASS; + lut_config.table_config.rw = LUT_READ; + lut_config.table_config.index = i; + if (osi_macsec_lut_config(osi_core, &lut_config) < 0) { + pr_err("%s: Failed to read BYP LUT\n", __func__); + *buf_p = buf; + return; + } else { + buf += scnprintf(buf, PAGE_SIZE, "%d.\t", i); + if ((lut_config.flags & LUT_FLAGS_ENTRY_VALID) != + LUT_FLAGS_ENTRY_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "Invalid\n"); + memset(&lut_config, 0, sizeof(lut_config)); + continue; + } + + format_output(&buf, &lut_config); + /* BYP LUT output field */ + if ((lut_config.flags & LUT_FLAGS_CONTROLLED_PORT) == + LUT_FLAGS_CONTROLLED_PORT) { + buf += scnprintf(buf, + PAGE_SIZE, "ctrl port: 1\n"); + } else { + buf += scnprintf(buf, + PAGE_SIZE, "ctrl port: 0\n"); + } + } + } + + *buf_p = buf; +} + +/** + * @brief Shows the current BYP LUT configuration + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer to print the current LUT configuration + */ +static ssize_t macsec_byp_lut_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + char *start = buf; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return 0; + } + + buf += scnprintf(buf, PAGE_SIZE, "Tx:\n"); + dump_byp_lut(&buf, CTLR_SEL_TX, osi_core); + + buf += scnprintf(buf, PAGE_SIZE, "Rx:\n"); + dump_byp_lut(&buf, CTLR_SEL_RX, osi_core); + + return (buf - start); +} + +/** + * @brief Set the BYP LUT configuration + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer which contains the desired LUT configuration + * @param[in] size: size of buffer + * + * @return size of buffer. + */ +static ssize_t macsec_byp_lut_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + struct osi_macsec_lut_config lut_config; + int ret, bufp; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return size; + } + + ret = parse_inputs(buf, &lut_config, &bufp); + if (ret < 0) { + dev_err(pdata->dev, "Failed to parse inputs"); + goto exit; + } + + //TODO - need to lock. Since lut_status is updated. + lut_config.lut_sel = LUT_SEL_BYPASS; + lut_config.table_config.rw = LUT_WRITE; + /* Rest of LUT attributes are filled by parse_inputs() */ + if (lut_config.table_config.index > BYP_LUT_MAX_INDEX) { + dev_err(dev, "%s: Index can't be > %d\n", __func__, + BYP_LUT_MAX_INDEX); + goto exit; + } + + if (osi_macsec_lut_config(osi_core, &lut_config) < 0) { + dev_err(dev, "%s: Failed to config BYP LUT\n", __func__); + goto exit; + } else { + dev_err(dev, "%s: Added BYP LUT idx: %d", __func__, + lut_config.table_config.index); + } + +exit: + return size; +} + +/** + * @brief Sysfs attribute for MACsec BYP LUT config + * + */ +static DEVICE_ATTR(macsec_byp_lut, (S_IRUGO | S_IWUSR), + macsec_byp_lut_show, + macsec_byp_lut_store); + +/** + * @brief Shows the current macsec statitics counters + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer to print the current counters + */ +static ssize_t macsec_mmc_counters_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + struct osi_macsec_mmc_counters *mmc = &osi_core->macsec_mmc; + unsigned short i; + char *start = buf; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return 0; + } + osi_macsec_read_mmc(osi_core); + + buf += scnprintf(buf, PAGE_SIZE, "tx_pkts_untaged:\t%llu\n", + mmc->tx_pkts_untaged); + buf += scnprintf(buf, PAGE_SIZE, "tx_pkts_too_long:\t%llu\n", + mmc->tx_pkts_too_long); + buf += scnprintf(buf, PAGE_SIZE, "tx_octets_protected:\t%llu\n", + mmc->tx_octets_protected); + for (i = 0; i < OSI_MACSEC_SC_INDEX_MAX; i++) { + buf += scnprintf(buf, PAGE_SIZE, "tx_pkts_protected sc%d:\t%llu\n", + i, mmc->tx_pkts_protected[i]); + } + + buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_no_tag: \t%llu\n", + mmc->rx_pkts_no_tag); + buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_untagged:\t%llu\n", + mmc->rx_pkts_untagged); + buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_bad_tag:\t%llu\n", + mmc->rx_pkts_bad_tag); + buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_no_sa_err:\t%llu\n", + mmc->rx_pkts_no_sa_err); + buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_no_sa: \t%llu\n", + mmc->rx_pkts_no_sa); + buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_overrun:\t%llu\n", + mmc->rx_pkts_overrun); + buf += scnprintf(buf, PAGE_SIZE, "rx_octets_validated:\t%llu\n", + mmc->rx_octets_validated); + + for (i = 0; i < OSI_MACSEC_SC_INDEX_MAX; i++) { + buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_invalid sc%d:\t%llu\n", + i, mmc->in_pkts_invalid[i]); + } + for (i = 0; i < OSI_MACSEC_SC_INDEX_MAX; i++) { + buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_delayed sc%d:\t%llu\n", + i, mmc->rx_pkts_delayed[i]); + } + for (i = 0; i < OSI_MACSEC_SC_INDEX_MAX; i++) { + buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_ok sc%d: \t%llu\n", + i, mmc->rx_pkts_ok[i]); + } + return (buf - start); +} + +/** + * @brief Sysfs attribute for MACsec irq stats + * + */ +static DEVICE_ATTR(macsec_mmc_counters, (S_IRUGO | S_IWUSR), + macsec_mmc_counters_show, + NULL); + + +static void dump_dbg_buffers(char **buf_p, unsigned short ctlr_sel, + struct osi_core_priv_data *osi_core) +{ + struct osi_macsec_dbg_buf_config dbg_buf_config = {0}; + char *buf = *buf_p; + int i; + unsigned int idx_max; + + if (ctlr_sel == CTLR_SEL_TX) { + idx_max = TX_DBG_BUF_IDX_MAX; + } else { + idx_max = RX_DBG_BUF_IDX_MAX; + } + for (i = 0; i < idx_max; i++) { + memset(&dbg_buf_config, OSI_NONE, sizeof(dbg_buf_config)); + dbg_buf_config.rw = DBG_TBL_READ; + dbg_buf_config.ctlr_sel = ctlr_sel; + dbg_buf_config.index = i; + if (osi_macsec_dbg_buf_config(osi_core, &dbg_buf_config) < 0) { + pr_err("%s: Failed to read debug buffers\n", __func__); + *buf_p = buf; + return; + } else { + buf += scnprintf(buf, PAGE_SIZE, "%d.\t", i); + buf += scnprintf(buf, PAGE_SIZE, " 0x%08X\t 0x%08X\t 0x%08X\t 0x%08X\n", + dbg_buf_config.dbg_buf[3], dbg_buf_config.dbg_buf[2], + dbg_buf_config.dbg_buf[1], dbg_buf_config.dbg_buf[0]); + } + } + *buf_p = buf; +} + +/** + * @brief Shows the current tx/rx debug buffers + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer to print the current debug buffers + */ +static ssize_t macsec_dbg_buffer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + char *start = buf; + struct osi_macsec_dbg_buf_config dbg_buf_config = {0}; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return 0; + } + + dbg_buf_config.ctlr_sel = CTLR_SEL_TX; + dbg_buf_config.rw = DBG_TBL_READ; + if (osi_macsec_dbg_events_config(osi_core, &dbg_buf_config) < 0) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + } + + buf += scnprintf(buf, PAGE_SIZE, "Tx flags: 0x%x\n", dbg_buf_config.flags); + dump_dbg_buffers(&buf, CTLR_SEL_TX, osi_core); + + dbg_buf_config.ctlr_sel = CTLR_SEL_RX; + dbg_buf_config.rw = DBG_TBL_READ; + if (osi_macsec_dbg_events_config(osi_core, &dbg_buf_config) < 0) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + } + buf += scnprintf(buf, PAGE_SIZE, "Rx flags: 0x%x\n", dbg_buf_config.flags); + dump_dbg_buffers(&buf, CTLR_SEL_RX, osi_core); + + return (buf - start); +} + +/** + * @brief Sysfs attribute for MACsec irq stats + * + */ +static DEVICE_ATTR(macsec_dbg_buffers, (S_IRUGO | S_IWUSR), + macsec_dbg_buffer_show, + NULL); + +#define DBG_EVENTS_LEN 13 + +/** + * @brief Set the debug buffer trigger events + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer which contains the desired debug buffer events + * configuration + * @param[in] size: size of buffer + * + * @return size of buffer. + */ +static ssize_t macsec_dbg_events_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + struct osi_macsec_dbg_buf_config dbg_buf_config = {0}; + int ret, events[12], i; + unsigned short controller; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return size; + } + + ret = sscanf(buf, "%hu %1x%1x%1x%1x%1x%1x%1x%1x%1x%1x%1x%1x", + &controller, &events[11], &events[10], &events[9], &events[8], + &events[7], &events[6], &events[5], &events[4], + &events[3], &events[2], &events[1], &events[0]); + + if (ret != DBG_EVENTS_LEN) { + pr_err("%s: Invalid DBG inputs(read %d)", __func__, ret); + goto exit; + } + /** parse all 12 trigger events */ + for (i = 0; i < 12; i++) { + if (events[i] > OSI_ENABLE) { + dev_err(dev, "%s: events bitmap error\n", __func__); + goto exit; + } else { + dbg_buf_config.flags |= (events[i] << i); + } + } + dbg_buf_config.ctlr_sel = controller; + dbg_buf_config.rw = DBG_TBL_WRITE; + + if (osi_macsec_dbg_events_config(osi_core, &dbg_buf_config) < 0) { + dev_err(dev, "%s: Failed to config dbg trigger events\n", __func__); + goto exit; + } else { + dev_err(dev, "%s: Updated dbg trigger events: %x", __func__, + dbg_buf_config.flags); + } + +exit: + return size; + +} + +/** + * @brief Sysfs attribute for MACsec debug events config + * + */ +static DEVICE_ATTR(macsec_dbg_events, (S_IRUGO | S_IWUSR), + NULL, + macsec_dbg_events_store); + +/** + * @brief Shows the current SCI LUT configuration + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer to print the current SCI LUT configuration + */ +static ssize_t macsec_sci_lut_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + struct osi_macsec_lut_config lut_config = {0}; + unsigned int an_valid; + int i; + char *start = buf; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return 0; + } + + buf += scnprintf(buf, PAGE_SIZE, "Tx:\n"); + + for (i = 0; i <= SC_LUT_MAX_INDEX; i++) { + memset(&lut_config, OSI_NONE, sizeof(lut_config)); + lut_config.table_config.ctlr_sel = CTLR_SEL_TX; + lut_config.lut_sel = LUT_SEL_SCI; + lut_config.table_config.rw = LUT_READ; + lut_config.table_config.index = i; + if (osi_macsec_lut_config(osi_core, &lut_config) < 0) { + dev_err(dev, "%s: Failed to read BYP LUT\n", __func__); + goto exit; + } else { + buf += scnprintf(buf, PAGE_SIZE, "%d.\t", i); + if ((lut_config.flags & LUT_FLAGS_ENTRY_VALID) != + LUT_FLAGS_ENTRY_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "Invalid\n"); + memset(&lut_config, 0, sizeof(lut_config)); + continue; + } + format_output(&buf, &lut_config); + /* Tx SCI LUT output field */ + an_valid = lut_config.sci_lut_out.an_valid; + buf += scnprintf(buf, PAGE_SIZE, "AN3: %d AN2: %d " + "AN1: %d AN0: %d ", + an_valid & AN3_VALID ? 1 : 0, + an_valid & AN2_VALID ? 1 : 0, + an_valid & AN1_VALID ? 1 : 0, + an_valid & AN0_VALID ? 1 : 0); + buf += scnprintf(buf, PAGE_SIZE, "sc_index: %d\n", + lut_config.sci_lut_out.sc_index); + memset(&lut_config, 0, sizeof(lut_config)); + } + } + + buf += scnprintf(buf, PAGE_SIZE, "Rx:\n"); + + for (i = 0; i <= SC_LUT_MAX_INDEX; i++) { + memset(&lut_config, OSI_NONE, sizeof(lut_config)); + lut_config.table_config.ctlr_sel = CTLR_SEL_RX; + lut_config.lut_sel = LUT_SEL_SCI; + lut_config.table_config.rw = LUT_READ; + lut_config.table_config.index = i; + if (osi_macsec_lut_config(osi_core, &lut_config) < 0) { + dev_err(dev, "%s: Failed to read BYP LUT\n", __func__); + goto exit; + } else { + buf += scnprintf(buf, PAGE_SIZE, "%d.\t", i); + if ((lut_config.flags & LUT_FLAGS_ENTRY_VALID) != + LUT_FLAGS_ENTRY_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "Invalid\n"); + memset(&lut_config, 0, sizeof(lut_config)); + continue; + } + + buf += scnprintf(buf, PAGE_SIZE, + "SCI: " SCI_FMT + " sc_index: %d\n", + lut_config.sci_lut_out.sci[7], + lut_config.sci_lut_out.sci[6], + lut_config.sci_lut_out.sci[5], + lut_config.sci_lut_out.sci[4], + lut_config.sci_lut_out.sci[3], + lut_config.sci_lut_out.sci[2], + lut_config.sci_lut_out.sci[1], + lut_config.sci_lut_out.sci[0], + lut_config.sci_lut_out.sc_index); + memset(&lut_config, 0, sizeof(lut_config)); + } + } + +exit: + return (buf - start); +} + +#define SCI_LUT_INPUTS 13 + +/** + * @brief Set the SCI LUT configuration + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer which contains the desired LUT configuration + * @param[in] size: size of buffer + * + * @return size of buffer. + */ +static ssize_t macsec_sci_lut_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + struct osi_macsec_lut_config lut_config; + int an_valid[MAX_NUM_SA] = {0}; + int ret, bufp; + int temp[SCI_LEN]; + int i; + int sc_index; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return size; + } + + ret = parse_inputs(buf, &lut_config, &bufp); + if (ret < 0) { + dev_err(pdata->dev, "Failed to parse inputs"); + goto exit; + } + + ret = sscanf(buf+bufp, " %1x%1x%1x%1x %x:%x:%x:%x:%x:%x:%x:%x %d", + &an_valid[3], &an_valid[2], &an_valid[1], &an_valid[0], + &temp[7], &temp[6], &temp[5], &temp[4], + &temp[3], &temp[2], &temp[1], &temp[0], + &sc_index); + if (ret != SCI_LUT_INPUTS) { + dev_err(pdata->dev, "Failed to parse SCI LUT arguments"); + goto exit; + } + + lut_config.lut_sel = LUT_SEL_SCI; + lut_config.table_config.rw = LUT_WRITE; + /* Rest of LUT attributes are filled by parse_inputs() */ + if (lut_config.table_config.index > SC_LUT_MAX_INDEX) { + dev_err(dev, "%s: Index can't be > %d\n", __func__, + SC_LUT_MAX_INDEX); + goto exit; + } + if (sc_index > SC_LUT_MAX_INDEX) { + dev_err(dev, "%s: SC Index can't be > %d\n", __func__, + SC_LUT_MAX_INDEX); + goto exit; + } + + /* Configure the outputs */ + for (i = 0; i < SCI_LEN; i++) { + lut_config.sci_lut_out.sci[i] = (unsigned char)temp[i]; + } + for (i = 0; i < MAX_NUM_SA; i++) { + if (an_valid[i] > OSI_ENABLE) { + dev_err(dev, "%s: an_valid bitmap error\n", __func__); + goto exit; + } else { + lut_config.sci_lut_out.an_valid |= (an_valid[i] << i); + } + } + lut_config.sci_lut_out.sc_index = sc_index; + + if (osi_macsec_lut_config(osi_core, &lut_config) < 0) { + dev_err(dev, "%s: Failed to config BYP LUT\n", __func__); + goto exit; + } else { + dev_err(dev, "%s: Added SCI LUT idx: %d", __func__, + lut_config.table_config.index); + } + +exit: + return size; +} + +/** + * @brief Sysfs attribute for MACsec SCI LUT config + * + */ +static DEVICE_ATTR(macsec_sci_lut, (S_IRUGO | S_IWUSR), + macsec_sci_lut_show, + macsec_sci_lut_store); + +static void dump_kt(char **buf_p, unsigned short ctlr_sel, + struct osi_core_priv_data *osi_core) +{ + struct osi_macsec_kt_config kt_config = {0}; + char *buf = *buf_p; + int i, j; + + for (i = 0; i <= TABLE_INDEX_MAX; i++) { + memset(&kt_config, OSI_NONE, sizeof(kt_config)); + kt_config.table_config.ctlr_sel = ctlr_sel; + kt_config.table_config.rw = LUT_READ; + kt_config.table_config.index = i; + if (osi_macsec_kt_config(osi_core, &kt_config) < 0) { + pr_err("%s: Failed to read KT\n", __func__); + *buf_p = buf; + return; + } else { + buf += scnprintf(buf, PAGE_SIZE, "%d.\t", i); + if ((kt_config.flags & LUT_FLAGS_ENTRY_VALID) != + LUT_FLAGS_ENTRY_VALID) { + buf += scnprintf(buf, PAGE_SIZE, "Invalid\n"); + memset(&kt_config, 0, sizeof(kt_config)); + continue; + } + + buf += scnprintf(buf, PAGE_SIZE, "SAK: 0x"); + for (j = 0; j < KEY_LEN_256; j++) { + buf += scnprintf(buf, PAGE_SIZE, "%02x", + kt_config.entry.sak[KEY_LEN_256 - 1 - j]); + } + buf += scnprintf(buf, PAGE_SIZE, " H: 0x"); + for (j = 0; j < KEY_LEN_128; j++) { + buf += scnprintf(buf, PAGE_SIZE, "%02x", + kt_config.entry.h[KEY_LEN_128 - 1 - j]); + } + buf += scnprintf(buf, PAGE_SIZE, "\n"); + } + } + + *buf_p = buf; +} + +/** + * @brief Shows the current macsec Tx key table + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer to print the current LUT configuration + */ +static ssize_t macsec_tx_kt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + char *start = buf; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return 0; + } + + buf += scnprintf(buf, PAGE_SIZE, "Tx:\n"); + dump_kt(&buf, CTLR_SEL_TX, osi_core); + + return (buf - start); +} + +/** + * @brief Shows the current macsec Rx key table + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer to print the current LUT configuration + */ +static ssize_t macsec_rx_kt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + char *start = buf; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return 0; + } + + buf += scnprintf(buf, PAGE_SIZE, "Rx:\n"); + dump_kt(&buf, CTLR_SEL_RX, osi_core); + + return (buf - start); +} + +#define KEY_FMT "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x" + +/** + * @brief Set the Key table + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer which contains the desired LUT configuration + * @param[in] size: size of buffer + * + * @return size of buffer. + */ +static ssize_t macsec_kt_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; +#if 1 /* HKEY GENERATION */ + struct crypto_cipher *tfm; + unsigned char hkey[KEY_LEN_128]; + unsigned char zeros[KEY_LEN_128] = {0}; +#endif + struct osi_macsec_kt_config kt_config = {0}; + int temp[KEY_LEN_256] = {0}; + unsigned char sak[KEY_LEN_256] = {0}; + + int valid, index, ctlr, key256bit; + int i, ret, bufp = 0; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return size; + } + + ret = sscanf(buf, "%d %d %d %d" KEY_FMT"%n", + &valid, &index, &ctlr, &key256bit, + &temp[0], &temp[1], &temp[2], &temp[3], + &temp[4], &temp[5], &temp[6], &temp[7], + &temp[8], &temp[9], &temp[10], &temp[11], + &temp[12], &temp[13], &temp[14], &temp[15], &bufp); + + if (ret != 20) { + dev_err(pdata->dev, "Failed to parse key table arguments\n"); + goto exit; + } + + if (key256bit == 1) { + ret = sscanf(buf+bufp, KEY_FMT, + &temp[16], &temp[17], &temp[18], &temp[19], + &temp[20], &temp[21], &temp[22], &temp[23], + &temp[24], &temp[25], &temp[26], &temp[27], + &temp[28], &temp[29], &temp[30], &temp[31]); + if (ret != 16) { + dev_err(pdata->dev, "Failed to parse key table arguments\n"); + goto exit; + } + } + + if ((index > TABLE_INDEX_MAX) || + (valid != OSI_ENABLE && valid != OSI_DISABLE) || + (ctlr != CTLR_SEL_TX && ctlr != CTLR_SEL_RX)) { + dev_err(pdata->dev, "%s: Invalid inputs\n", __func__); + goto exit; + } + + kt_config.table_config.ctlr_sel = ctlr; + kt_config.table_config.rw = LUT_WRITE; + kt_config.table_config.index = index; + +#if 1 /* HKEY GENERATION */ + //TODO - move to OSD and use ether_linux.h/macsec.c for this + tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); + if (crypto_cipher_setkey(tfm, sak, KEY_LEN_128)) { + pr_err("%s: Failed to set cipher key for H generation", + __func__); + goto exit; + } + crypto_cipher_encrypt_one(tfm, hkey, zeros); + crypto_free_cipher(tfm); +#endif /* HKEY GENERATION */ + + for (i = 0; i < KEY_LEN_128; i++) { + sak[i] = (unsigned char)temp[i]; + } + if (key256bit == 1) { + for (i = KEY_LEN_128; i < KEY_LEN_256; i++) { + sak[i] = (unsigned char)temp[i]; + } + } + + for (i = 0; i < KEY_LEN_128; i++) { + kt_config.entry.h[i] = hkey[KEY_LEN_128 - 1 - i]; + } + + if (key256bit == 1) { + for (i = 0; i < KEY_LEN_256; i++) { + kt_config.entry.sak[i] = sak[KEY_LEN_256 - 1 - i]; + } + } else { + for (i = 0; i < KEY_LEN_128; i++) { + kt_config.entry.sak[i] = sak[KEY_LEN_128 - 1 - i]; + } + } + + if (valid) { + kt_config.flags |= LUT_FLAGS_ENTRY_VALID; + } + + ret = osi_macsec_kt_config(osi_core, &kt_config); + if (ret < 0) { + pr_err("%s: Failed to set SAK", __func__); + goto exit; + } + +exit: + return size; +} + +/** + * @brief Sysfs attribute for MACsec key table (Store new key) + * + */ +static DEVICE_ATTR(macsec_kt, (S_IRUGO | S_IWUSR), + NULL, + macsec_kt_store); + +/** + * @brief Sysfs attribute for MACsec key table (show Tx table) + * + */ +static DEVICE_ATTR(macsec_tx_kt, (S_IRUGO | S_IWUSR), + macsec_tx_kt_show, + NULL); + +/** + * @brief Sysfs attribute for MACsec key table (show Rx table) + * + */ +static DEVICE_ATTR(macsec_rx_kt, (S_IRUGO | S_IWUSR), + macsec_rx_kt_show, + NULL); + +static void dump_sc_state_lut(char **buf_p, unsigned short ctlr_sel, + struct osi_core_priv_data *osi_core) +{ + struct osi_macsec_lut_config lut_config = {0}; + char *buf = *buf_p; + int i; + + for (i = 0; i <= SC_LUT_MAX_INDEX; i++) { + memset(&lut_config, OSI_NONE, sizeof(lut_config)); + lut_config.table_config.ctlr_sel = ctlr_sel; + lut_config.table_config.rw = LUT_READ; + lut_config.table_config.index = i; + lut_config.lut_sel = LUT_SEL_SC_STATE; + if (osi_macsec_lut_config(osi_core, &lut_config) < 0) { + pr_err("%s: Failed to read BYP LUT\n", __func__); + *buf_p = buf; + return; + } else { + buf += scnprintf(buf, PAGE_SIZE, "%d.\tcurr_an: %d\n", + i, lut_config.sc_state_out.curr_an); + } + } + + *buf_p = buf; +} + +/** + * @brief Shows the current SC state LUT configuration + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer to print the current LUT configuration + */ +static ssize_t macsec_sc_state_lut_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + char *start = buf; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return 0; + } + + buf += scnprintf(buf, PAGE_SIZE, "Tx:\n"); + dump_sc_state_lut(&buf, CTLR_SEL_TX, osi_core); + + buf += scnprintf(buf, PAGE_SIZE, "Rx:\n"); + dump_sc_state_lut(&buf, CTLR_SEL_RX, osi_core); + + return (buf - start); +} + +/** + * @brief Set the SC state LUT configuration + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer which contains the desired LUT configuration + * @param[in] size: size of buffer + * + * @return size of buffer. + */ +static ssize_t macsec_sc_state_lut_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + struct osi_macsec_lut_config lut_config; + int index, ctlr; + int ret, curr_an; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return size; + } + + ret = sscanf(buf, "%d %d %d", &index, &ctlr, &curr_an); + if (ret < 3) { + dev_err(pdata->dev, "%s: Failed to parse inputs", __func__); + goto exit; + } + + if ((index > SC_LUT_MAX_INDEX) || + (ctlr != CTLR_SEL_TX && ctlr != CTLR_SEL_RX) || + (curr_an > CURR_AN_MAX)) { + dev_err(pdata->dev, "%s:Invalid inputs", __func__); + goto exit; + } + + //TODO - need to lock. Since lut_status is updated. + lut_config.table_config.ctlr_sel = ctlr; + lut_config.table_config.rw = LUT_WRITE; + lut_config.table_config.index = index; + lut_config.lut_sel = LUT_SEL_SC_STATE; + lut_config.sc_state_out.curr_an = curr_an; + + if (osi_macsec_lut_config(osi_core, &lut_config) < 0) { + dev_err(dev, "%s: Failed to config SC STATE LUT\n", __func__); + goto exit; + } else { + dev_err(dev, "%s: Added SC STATE LUT idx: %d", __func__, + lut_config.table_config.index); + } + +exit: + return size; +} + +/** + * @brief Sysfs attribute for SC state LUT configuration + * + */ +static DEVICE_ATTR(macsec_sc_state_lut, (S_IRUGO | S_IWUSR), + macsec_sc_state_lut_show, + macsec_sc_state_lut_store); + +static void dump_sa_state_lut(char **buf_p, unsigned short ctlr_sel, + struct osi_core_priv_data *osi_core) +{ + struct osi_macsec_lut_config lut_config = {0}; + char *buf = *buf_p; + int i; + + for (i = 0; i <= SA_LUT_MAX_INDEX; i++) { + memset(&lut_config, OSI_NONE, sizeof(lut_config)); + lut_config.table_config.ctlr_sel = ctlr_sel; + lut_config.table_config.rw = LUT_READ; + lut_config.table_config.index = i; + lut_config.lut_sel = LUT_SEL_SA_STATE; + if (osi_macsec_lut_config(osi_core, &lut_config) < 0) { + pr_err("%s: Failed to read BYP LUT\n", __func__); + goto exit; + } + + switch (ctlr_sel) { + case CTLR_SEL_TX: + if ((lut_config.flags & LUT_FLAGS_ENTRY_VALID) == + LUT_FLAGS_ENTRY_VALID) { + buf += scnprintf(buf, PAGE_SIZE, + "%d.\tnext_pn: %d\n", i, + lut_config.sa_state_out.next_pn); + } else { + buf += scnprintf(buf, PAGE_SIZE, + "%d.\tInvalid\n", i); + } + break; + case CTLR_SEL_RX: + buf += scnprintf(buf, PAGE_SIZE, + "%d.\tnext_pn: %d lowest_pn: %d\n", i, + lut_config.sa_state_out.next_pn, + lut_config.sa_state_out.lowest_pn); + break; + default: + goto exit; + } + } + +exit: + *buf_p = buf; +} + +/** + * @brief Shows the current SA state LUT configuration + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer to print the current LUT configuration + */ +static ssize_t macsec_sa_state_lut_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + char *start = buf; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return 0; + } + + buf += scnprintf(buf, PAGE_SIZE, "Tx:\n"); + dump_sa_state_lut(&buf, CTLR_SEL_TX, osi_core); + + buf += scnprintf(buf, PAGE_SIZE, "Rx:\n"); + dump_sa_state_lut(&buf, CTLR_SEL_RX, osi_core); + + return (buf - start); +} + +/** + * @brief Set the SA state LUT configuration + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer which contains the desired LUT configuration + * @param[in] size: size of buffer + * + * @return size of buffer. + */ +static ssize_t macsec_sa_state_lut_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + struct osi_macsec_lut_config lut_config; + int index, ctlr; + int ret; + unsigned int next_pn, lowest_pn; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return size; + } + + ret = sscanf(buf, "%d %d %u %u", &index, &ctlr, &next_pn, &lowest_pn); + if (ret < 4) { + dev_err(pdata->dev, "%s: Failed to parse inputs", __func__); + goto exit; + } + + if ((index > SA_LUT_MAX_INDEX) || + (ctlr != CTLR_SEL_TX && ctlr != CTLR_SEL_RX)) { + dev_err(pdata->dev, "%s:Invalid inputs", __func__); + goto exit; + } + + //TODO - need to lock. Since lut_status is updated. + lut_config.flags = LUT_FLAGS_ENTRY_VALID; + lut_config.table_config.ctlr_sel = ctlr; + lut_config.table_config.rw = LUT_WRITE; + lut_config.table_config.index = index; + lut_config.sa_state_out.next_pn = next_pn; + lut_config.sa_state_out.lowest_pn = lowest_pn; + lut_config.lut_sel = LUT_SEL_SA_STATE; + + if (osi_macsec_lut_config(osi_core, &lut_config) < 0) { + dev_err(dev, "%s: Failed to config SA STATE LUT\n", __func__); + goto exit; + } else { + dev_err(dev, "%s: Added SA STATE LUT idx: %d", __func__, + lut_config.table_config.index); + } + +exit: + return size; +} + +/** + * @brief Sysfs attribute for SA state LUT configuration + * + */ +static DEVICE_ATTR(macsec_sa_state_lut, (S_IRUGO | S_IWUSR), + macsec_sa_state_lut_show, + macsec_sa_state_lut_store); + + +static void dump_sc_param_lut(char **buf_p, unsigned short ctlr_sel, + struct osi_core_priv_data *osi_core) +{ + struct osi_macsec_lut_config lut_config = {0}; + char *buf = *buf_p; + int i; + + for (i = 0; i <= SC_LUT_MAX_INDEX; i++) { + memset(&lut_config, OSI_NONE, sizeof(lut_config)); + lut_config.table_config.ctlr_sel = ctlr_sel; + lut_config.table_config.rw = LUT_READ; + lut_config.table_config.index = i; + lut_config.lut_sel = LUT_SEL_SC_PARAM; + if (osi_macsec_lut_config(osi_core, &lut_config) < 0) { + pr_err("%s: Failed to read BYP LUT\n", __func__); + goto exit; + } + + switch (ctlr_sel) { + case CTLR_SEL_TX: + buf += scnprintf(buf, PAGE_SIZE, + "%d.\tkey_idx_start: %d pn_max: %u " + "pn_threshold: %u tci %01x vlan_clear %01x sci: " SCI_FMT, + i, lut_config.sc_param_out.key_index_start, + lut_config.sc_param_out.pn_max, + lut_config.sc_param_out.pn_threshold, + lut_config.sc_param_out.tci, + lut_config.sc_param_out.vlan_in_clear, + lut_config.sc_param_out.sci[7], + lut_config.sc_param_out.sci[6], + lut_config.sc_param_out.sci[5], + lut_config.sc_param_out.sci[4], + lut_config.sc_param_out.sci[3], + lut_config.sc_param_out.sci[2], + lut_config.sc_param_out.sci[1], + lut_config.sc_param_out.sci[0]); + buf += scnprintf(buf, PAGE_SIZE, "\n"); + break; + case CTLR_SEL_RX: + buf += scnprintf(buf, PAGE_SIZE, + "%d.\tkey_idx_start: %d pn_max: %u pn_window: %u\n", i, + lut_config.sc_param_out.key_index_start, + lut_config.sc_param_out.pn_max, + lut_config.sc_param_out.pn_window); + break; + default: + goto exit; + } + } + +exit: + *buf_p = buf; +} + +/** + * @brief Shows the current SC parameters LUT configuration + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer to print the current LUT configuration + */ +static ssize_t macsec_sc_param_lut_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + char *start = buf; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return 0; + } + + buf += scnprintf(buf, PAGE_SIZE, "Tx:\n"); + dump_sc_param_lut(&buf, CTLR_SEL_TX, osi_core); + + buf += scnprintf(buf, PAGE_SIZE, "Rx:\n"); + dump_sc_param_lut(&buf, CTLR_SEL_RX, osi_core); + + return (buf - start); +} + +#define SC_PARAM_INPUTS_LEN 16 + +/** + * @brief Set the SC parameters LUT configuration + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer which contains the desired LUT configuration + * @param[in] size: size of buffer + * + * @return size of buffer. + */ +static ssize_t macsec_sc_param_lut_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + struct osi_macsec_lut_config lut_config = {0}; + int index, ctlr; + int ret, i, tci, vlan_clear; + int sci[SCI_LEN] = {0}; + unsigned int pn_max, pn_threshold, key_index_start, pn_window; + + if (!netif_running(ndev)) { + dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n"); + return size; + } + + ret = sscanf(buf, "%d %d %u %u %u %u %d %d" SCI_FMT, + &index, &ctlr, + &key_index_start, &pn_max, &pn_threshold, &pn_window, + &tci, &vlan_clear, + &sci[7], &sci[6], &sci[5], &sci[4], + &sci[3], &sci[2], &sci[1], &sci[0]); + if (ret < SC_PARAM_INPUTS_LEN) { + dev_err(pdata->dev, "%s: Failed to parse inputs", __func__); + goto exit; + } + + if ((index > SC_LUT_MAX_INDEX) || + (ctlr != CTLR_SEL_TX && ctlr != CTLR_SEL_RX) || + (key_index_start > KEY_INDEX_MAX) || + (pn_threshold > pn_max)) { + dev_err(pdata->dev, "%s:Invalid inputs", __func__); + goto exit; + } + + //TODO - need to lock. Since lut_status is updated. + lut_config.table_config.ctlr_sel = ctlr; + lut_config.table_config.rw = LUT_WRITE; + lut_config.table_config.index = index; + lut_config.lut_sel = LUT_SEL_SC_PARAM; + lut_config.sc_param_out.key_index_start = key_index_start; + lut_config.sc_param_out.pn_max = pn_max; + lut_config.sc_param_out.pn_threshold = pn_threshold; + lut_config.sc_param_out.pn_window = pn_window; + lut_config.sc_param_out.tci = (unsigned char)tci; + lut_config.sc_param_out.vlan_in_clear = (unsigned char)vlan_clear; + for (i = 0; i < SCI_LEN; i++) { + lut_config.sc_param_out.sci[i] = (unsigned char)sci[i]; + } + + if (osi_macsec_lut_config(osi_core, &lut_config) < 0) { + dev_err(dev, "%s: Failed to config SC PARAM LUT\n", __func__); + goto exit; + } else { + dev_err(dev, "%s: Added SC PARAM LUT idx: %d", __func__, + lut_config.table_config.index); + } + +exit: + return size; +} + +/** + * @brief Sysfs attribute for SC param LUT configuration + * + */ +static DEVICE_ATTR(macsec_sc_param_lut, (S_IRUGO | S_IWUSR), + macsec_sc_param_lut_show, + macsec_sc_param_lut_store); + +/** + * @brief Shows the current MACsec irq stats + * + * @param[in] dev: Device data. + * @param[in] attr: Device attribute + * @param[in] buf: Buffer to print the current MACsec irq stats + */ +static ssize_t macsec_irq_stats_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct osi_core_priv_data *osi_core = pdata->osi_core; + struct osi_macsec_irq_stats *stats = &osi_core->macsec_irq_stats; + + return scnprintf(buf, PAGE_SIZE, + "tx_dbg_capture_done:\t%lu\n" + "tx_mtu_check_fail :\t%lu\n" + "tx_mac_crc_error :\t%lu\n" + "tx_sc_an_not_valid :\t%lu\n" + "tx_aes_gcm_buf_ovf :\t%lu\n" + "tx_lkup_miss :\t%lu\n" + "tx_uninit_key_slot :\t%lu\n" + "tx_pn_threshold :\t%lu\n" + "tx_pn_exhausted :\t%lu\n" + "rx_dbg_capture_done:\t%lu\n" + "rx_icv_err_threshold :\t%lu\n" + "rx_replay_error :\t%lu\n" + "rx_mtu_check_fail :\t%lu\n" + "rx_mac_crc_error :\t%lu\n" + "rx_aes_gcm_buf_ovf :\t%lu\n" + "rx_lkup_miss :\t%lu\n" + "rx_uninit_key_slot :\t%lu\n" + "rx_pn_exhausted :\t%lu\n" + "secure_reg_viol :\t%lu\n", + stats->tx_dbg_capture_done, + stats->tx_mtu_check_fail, + stats->tx_mac_crc_error, + stats->tx_sc_an_not_valid, + stats->tx_aes_gcm_buf_ovf, + stats->tx_lkup_miss, + stats->tx_uninit_key_slot, + stats->tx_pn_threshold, + stats->tx_pn_exhausted, + stats->rx_dbg_capture_done, + stats->rx_icv_err_threshold, + stats->rx_replay_error, + stats->rx_mtu_check_fail, + stats->rx_mac_crc_error, + stats->rx_aes_gcm_buf_ovf, + stats->rx_lkup_miss, + stats->rx_uninit_key_slot, + stats->rx_pn_exhausted, + stats->secure_reg_viol); +} + +/** + * @brief Sysfs attribute for MACsec irq stats + * + */ +static DEVICE_ATTR(macsec_irq_stats, (S_IRUGO | S_IWUSR), + macsec_irq_stats_show, + NULL); +#endif /* MACSEC_SUPPORT */ + /** * @brief Sysfs attribute for MAC loopback * @@ -319,6 +2060,22 @@ static struct attribute *ether_sysfs_attrs[] = { &dev_attr_ptp_mode.attr, &dev_attr_ptp_sync.attr, &dev_attr_frp.attr, +#ifdef MACSEC_SUPPORT + &dev_attr_macsec_irq_stats.attr, + &dev_attr_macsec_byp_lut.attr, + &dev_attr_macsec_sci_lut.attr, + &dev_attr_macsec_kt.attr, + &dev_attr_macsec_tx_kt.attr, + &dev_attr_macsec_rx_kt.attr, + &dev_attr_macsec_sc_state_lut.attr, + &dev_attr_macsec_sa_state_lut.attr, + &dev_attr_macsec_sc_param_lut.attr, + &dev_attr_macsec_loopback.attr, + &dev_attr_macsec_enable.attr, + &dev_attr_macsec_mmc_counters.attr, + &dev_attr_macsec_dbg_buffers.attr, + &dev_attr_macsec_dbg_events.attr, +#endif /* MACSEC_SUPPORT */ NULL };