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 };