mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-23 09:42:19 +03:00
Add support to accept multiple supplicant launch on same VF so that DUT can have multiple MACSEC links with different ethernet peers Pass Peer MACID to OSI such that the same is added to SCI LUT Bug 4754899 Change-Id: Iaf56eb4d7ebc3266a0d3ceb3dbc76a1547ed608c Signed-off-by: Sanath Kumar Gampa <sgampa@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3237500 Reviewed-by: Ashutosh Jha <ajha@nvidia.com> Reviewed-by: Mahesh Patil <maheshp@nvidia.com> GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
1718 lines
45 KiB
C
1718 lines
45 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Copyright (c) 2019-2024, NVIDIA CORPORATION. All rights reserved */
|
|
|
|
#ifdef MACSEC_SUPPORT
|
|
#include "ether_linux.h"
|
|
#ifdef MACSEC_KEY_PROGRAM
|
|
#include <linux/crypto.h>
|
|
#endif /* MACSEC_KEY_PROGRAM */
|
|
#ifdef HSI_SUPPORT
|
|
#include <linux/tegra-epl.h>
|
|
#endif
|
|
|
|
static bool macsec_enable = true;
|
|
module_param(macsec_enable, bool, 0644);
|
|
MODULE_PARM_DESC(macsec_enable, "Enable Macsec for nvethernet module");
|
|
|
|
static int macsec_get_tx_next_pn(struct sk_buff *skb, struct genl_info *info);
|
|
|
|
#ifndef MACSEC_KEY_PROGRAM
|
|
static int macsec_tz_kt_config(struct ether_priv_data *pdata,
|
|
unsigned char cmd,
|
|
struct osi_macsec_kt_config *const kt_config,
|
|
struct genl_info *const info, struct nvpkcs_data *pkcs);
|
|
#endif
|
|
|
|
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_isr(pdata->osi_core);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
#ifdef HSI_SUPPORT
|
|
static inline u64 rdtsc(void)
|
|
{
|
|
u64 val;
|
|
|
|
asm volatile("mrs %0, cntvct_el0" : "=r" (val));
|
|
|
|
return val;
|
|
}
|
|
|
|
static irqreturn_t macsec_ns_isr_thread(int irq, void *data)
|
|
{
|
|
struct macsec_priv_data *macsec_pdata = (struct macsec_priv_data *)data;
|
|
struct ether_priv_data *pdata = macsec_pdata->ether_pdata;
|
|
struct device *dev = pdata->dev;
|
|
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
|
int ret = 0;
|
|
int i = 0;
|
|
struct epl_error_report_frame error_report = {0};
|
|
|
|
mutex_lock(&pdata->hsi_lock);
|
|
if (osi_core->hsi.macsec_report_err) {
|
|
error_report.reporter_id = osi_core->hsi.reporter_id;
|
|
error_report.timestamp = lower_32_bits(rdtsc());
|
|
|
|
for (i = 0; i < HSI_MAX_MACSEC_ERROR_CODE; i++) {
|
|
if (osi_core->hsi.macsec_err_code[i] > 0 &&
|
|
osi_core->hsi.macsec_report_count_err[i] == OSI_ENABLE) {
|
|
error_report.error_code =
|
|
osi_core->hsi.macsec_err_code[i];
|
|
ret = epl_report_error(error_report);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to report error: reporter ID: 0x%x, Error code: 0x%x, return: %d\n",
|
|
osi_core->hsi.reporter_id,
|
|
osi_core->hsi.macsec_err_code[i], ret);
|
|
} else {
|
|
dev_info(dev, "EPL report error: reporter ID: 0x%x, Error code: 0x%x",
|
|
osi_core->hsi.reporter_id,
|
|
osi_core->hsi.macsec_err_code[i]);
|
|
}
|
|
osi_core->hsi.macsec_err_code[i] = 0;
|
|
osi_core->hsi.macsec_report_count_err[i] = OSI_DISABLE;
|
|
}
|
|
}
|
|
}
|
|
mutex_unlock(&pdata->hsi_lock);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
int irq_ret = IRQ_HANDLED;
|
|
|
|
osi_macsec_isr(pdata->osi_core);
|
|
|
|
#ifdef HSI_SUPPORT
|
|
if (pdata->osi_core->hsi.enabled == OSI_ENABLE &&
|
|
pdata->osi_core->hsi.macsec_report_err == OSI_ENABLE)
|
|
irq_ret = IRQ_WAKE_THREAD;
|
|
#endif
|
|
return irq_ret;
|
|
}
|
|
|
|
static int macsec_disable_car(struct macsec_priv_data *macsec_pdata)
|
|
{
|
|
struct ether_priv_data *pdata = macsec_pdata->ether_pdata;
|
|
|
|
PRINT_ENTRY();
|
|
if (pdata->osi_core->mac_ver != OSI_EQOS_MAC_5_30) {
|
|
if (!IS_ERR_OR_NULL(macsec_pdata->macsec_clk)) {
|
|
clk_disable_unprepare(macsec_pdata->macsec_clk);
|
|
}
|
|
} else {
|
|
if (!IS_ERR_OR_NULL(macsec_pdata->eqos_tx_clk)) {
|
|
clk_disable_unprepare(macsec_pdata->eqos_tx_clk);
|
|
}
|
|
|
|
if (!IS_ERR_OR_NULL(macsec_pdata->eqos_rx_clk)) {
|
|
clk_disable_unprepare(macsec_pdata->eqos_rx_clk);
|
|
}
|
|
}
|
|
|
|
if (macsec_pdata->ns_rst) {
|
|
reset_control_assert(macsec_pdata->ns_rst);
|
|
}
|
|
|
|
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;
|
|
int ret = 0;
|
|
|
|
PRINT_ENTRY();
|
|
if (pdata->osi_core->mac_ver != OSI_EQOS_MAC_5_30) {
|
|
if (!IS_ERR_OR_NULL(macsec_pdata->macsec_clk)) {
|
|
ret = clk_prepare_enable(macsec_pdata->macsec_clk);
|
|
if (ret < 0) {
|
|
dev_err(dev, "failed to enable macsec clk\n");
|
|
goto exit;
|
|
}
|
|
}
|
|
} else {
|
|
if (!IS_ERR_OR_NULL(macsec_pdata->eqos_tx_clk)) {
|
|
ret = clk_prepare_enable(macsec_pdata->eqos_tx_clk);
|
|
if (ret < 0) {
|
|
dev_err(dev, "failed to enable macsec tx clk\n");
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (!IS_ERR_OR_NULL(macsec_pdata->eqos_rx_clk)) {
|
|
ret = clk_prepare_enable(macsec_pdata->eqos_rx_clk);
|
|
if (ret < 0) {
|
|
dev_err(dev, "failed to enable macsec rx clk\n");
|
|
goto err_rx_clk;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
goto exit;
|
|
|
|
err_ns_rst:
|
|
if (pdata->osi_core->mac_ver != OSI_EQOS_MAC_5_30) {
|
|
if (!IS_ERR_OR_NULL(macsec_pdata->macsec_clk)) {
|
|
clk_disable_unprepare(macsec_pdata->macsec_clk);
|
|
}
|
|
} else {
|
|
if (!IS_ERR_OR_NULL(macsec_pdata->eqos_rx_clk)) {
|
|
clk_disable_unprepare(macsec_pdata->eqos_rx_clk);
|
|
}
|
|
err_rx_clk:
|
|
if (!IS_ERR_OR_NULL(macsec_pdata->eqos_tx_clk)) {
|
|
clk_disable_unprepare(macsec_pdata->eqos_tx_clk);
|
|
}
|
|
}
|
|
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();
|
|
osi_macsec_deinit(pdata->osi_core);
|
|
|
|
if (macsec_pdata->is_irq_allocated & OSI_BIT(1)) {
|
|
devm_free_irq(dev, macsec_pdata->ns_irq, macsec_pdata);
|
|
macsec_pdata->is_irq_allocated &= ~OSI_BIT(1);
|
|
}
|
|
if (macsec_pdata->is_irq_allocated & OSI_BIT(0)) {
|
|
devm_free_irq(dev, macsec_pdata->s_irq, macsec_pdata);
|
|
macsec_pdata->is_irq_allocated &= ~OSI_BIT(0);
|
|
}
|
|
macsec_pdata->enabled = OSI_DISABLE;
|
|
|
|
PRINT_EXIT();
|
|
|
|
return ret;
|
|
}
|
|
|
|
int macsec_open(struct macsec_priv_data *macsec_pdata,
|
|
void *const genl_info)
|
|
{
|
|
struct ether_priv_data *pdata = macsec_pdata->ether_pdata;
|
|
struct device *dev = pdata->dev;
|
|
int ret = 0;
|
|
|
|
PRINT_ENTRY();
|
|
if (pdata->osi_core->use_virtualization == OSI_DISABLE) {
|
|
/* 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", ret);
|
|
goto exit;
|
|
}
|
|
|
|
dev_info(dev, "%s: requested s_irq %d: %s\n", __func__,
|
|
macsec_pdata->s_irq, macsec_pdata->irq_name[0]);
|
|
macsec_pdata->is_irq_allocated |= OSI_BIT(0);
|
|
|
|
snprintf(macsec_pdata->irq_name[1], MACSEC_IRQ_NAME_SZ, "%s.macsec_ns",
|
|
netdev_name(pdata->ndev));
|
|
|
|
#ifdef HSI_SUPPORT
|
|
ret = devm_request_threaded_irq(dev, macsec_pdata->ns_irq, macsec_ns_isr,
|
|
macsec_ns_isr_thread,
|
|
IRQF_TRIGGER_NONE | IRQF_ONESHOT,
|
|
macsec_pdata->irq_name[1],
|
|
macsec_pdata);
|
|
#else
|
|
ret = devm_request_irq(dev, macsec_pdata->ns_irq, macsec_ns_isr,
|
|
IRQF_TRIGGER_NONE, macsec_pdata->irq_name[1],
|
|
macsec_pdata);
|
|
#endif
|
|
if (ret < 0) {
|
|
dev_err(dev, "failed to request irq %d\n", ret);
|
|
goto err_ns_irq;
|
|
}
|
|
|
|
dev_info(dev, "%s: requested ns_irq %d: %s\n", __func__,
|
|
macsec_pdata->ns_irq, macsec_pdata->irq_name[1]);
|
|
macsec_pdata->is_irq_allocated |= OSI_BIT(1);
|
|
}
|
|
/* Invoke OSI HW initialization, initialize standard BYP entries */
|
|
ret = osi_macsec_init(pdata->osi_core, pdata->osi_core->mtu,
|
|
(nveu8_t * const)pdata->ndev->dev_addr);
|
|
if (ret < 0) {
|
|
dev_err(dev, "osi_macsec_init failed, %d\n", ret);
|
|
goto err_osi_init;
|
|
}
|
|
|
|
#if !defined(MACSEC_KEY_PROGRAM) && !defined(NVPKCS_MACSEC)
|
|
/* Clear KT entries */
|
|
ret = macsec_tz_kt_config(pdata, NV_MACSEC_CMD_TZ_KT_RESET,
|
|
OSI_NULL, genl_info, NULL);
|
|
if (ret < 0) {
|
|
dev_err(dev, "TZ key config failed %d\n", ret);
|
|
goto err_osi_en;
|
|
}
|
|
#endif /* !MACSEC_KEY_PROGRAM */
|
|
|
|
macsec_pdata->enabled = OSI_ENABLE;
|
|
|
|
goto exit;
|
|
|
|
#if !defined(MACSEC_KEY_PROGRAM) && !defined(NVPKCS_MACSEC)
|
|
err_osi_en:
|
|
osi_macsec_deinit(pdata->osi_core);
|
|
#endif /* !MACSEC_KEY_PROGRAM */
|
|
err_osi_init:
|
|
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();
|
|
/* 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;
|
|
}
|
|
|
|
if (pdata->osi_core->pre_sil == 0x1U) {
|
|
dev_warn(dev, "%s: Pre-silicon simulation, skipping reset/clk config\n", __func__);
|
|
goto exit;
|
|
}
|
|
/* Get resets */
|
|
macsec_pdata->ns_rst = devm_reset_control_get(dev, "macsec_ns_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;
|
|
}
|
|
|
|
/* Get clks */
|
|
if (pdata->osi_core->mac_ver != OSI_EQOS_MAC_5_30) {
|
|
if (pdata->osi_core->mac_ver == OSI_MGBE_MAC_3_10) {
|
|
macsec_pdata->macsec_clk = devm_clk_get(dev, "mgbe_macsec");
|
|
} else {
|
|
macsec_pdata->macsec_clk = devm_clk_get(dev, "macsec");
|
|
}
|
|
if (IS_ERR(macsec_pdata->macsec_clk)) {
|
|
dev_err(dev, "failed to get macsec clk\n");
|
|
ret = PTR_ERR(macsec_pdata->macsec_clk);
|
|
goto exit;
|
|
}
|
|
} else {
|
|
macsec_pdata->eqos_tx_clk = devm_clk_get(dev, "eqos_macsec_tx");
|
|
if (IS_ERR(macsec_pdata->eqos_tx_clk)) {
|
|
dev_err(dev, "failed to get eqos_tx clk\n");
|
|
ret = PTR_ERR(macsec_pdata->eqos_tx_clk);
|
|
goto exit;
|
|
}
|
|
macsec_pdata->eqos_rx_clk = devm_clk_get(dev,
|
|
"eqos_macsec_rx");
|
|
if (IS_ERR(macsec_pdata->eqos_rx_clk)) {
|
|
dev_err(dev, "failed to get eqos_rx_clk clk\n");
|
|
ret = PTR_ERR(macsec_pdata->eqos_rx_clk);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
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->mac_ver != OSI_EQOS_MAC_5_30) {
|
|
if (!IS_ERR_OR_NULL(macsec_pdata->macsec_clk)) {
|
|
devm_clk_put(dev, macsec_pdata->macsec_clk);
|
|
}
|
|
} else {
|
|
if (!IS_ERR_OR_NULL(macsec_pdata->eqos_tx_clk)) {
|
|
devm_clk_put(dev, macsec_pdata->eqos_tx_clk);
|
|
}
|
|
|
|
if (!IS_ERR_OR_NULL(macsec_pdata->eqos_rx_clk)) {
|
|
devm_clk_put(dev, macsec_pdata->eqos_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_strscpy(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 struct macsec_supplicant_data *macsec_get_supplicant(
|
|
struct macsec_priv_data *macsec_pdata,
|
|
unsigned int portid)
|
|
{
|
|
struct macsec_supplicant_data *supplicant = macsec_pdata->supplicant;
|
|
int i;
|
|
|
|
/* check for already exist instance */
|
|
for (i = 0; i < OSI_MAX_NUM_SC; i++) {
|
|
if (supplicant[i].snd_portid == portid &&
|
|
supplicant[i].in_use == OSI_ENABLE) {
|
|
return &supplicant[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int macsec_set_cipher(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct nlattr **attrs = info->attrs;
|
|
struct macsec_priv_data *macsec_pdata;
|
|
struct macsec_supplicant_data *supplicant;
|
|
struct ether_priv_data *pdata = NULL;
|
|
int ret = 0;
|
|
|
|
PRINT_ENTRY();
|
|
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
|
|
!attrs[NV_MACSEC_ATTR_CIPHER_SUITE]) {
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
macsec_pdata = genl_to_macsec_pdata(info);
|
|
if (!macsec_pdata) {
|
|
ret = -EPROTO;
|
|
goto exit;
|
|
}
|
|
pdata = macsec_pdata->ether_pdata;
|
|
|
|
if (!netif_running(pdata->ndev)) {
|
|
ret = -ENETDOWN;
|
|
dev_err(pdata->dev, "%s: MAC interface down!!\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
mutex_lock(&macsec_pdata->lock);
|
|
supplicant = macsec_get_supplicant(macsec_pdata, info->snd_portid);
|
|
if (!supplicant) {
|
|
ret = -EPROTO;
|
|
dev_err(pdata->dev, "%s: failed to get supplicant data\n",
|
|
__func__);
|
|
goto err_unlock;
|
|
}
|
|
supplicant->cipher = nla_get_u32(attrs[NV_MACSEC_ATTR_CIPHER_SUITE]);
|
|
|
|
if (supplicant->cipher != OSI_MACSEC_CIPHER_AES128 &&
|
|
supplicant->cipher != OSI_MACSEC_CIPHER_AES256) {
|
|
ret = -EPROTO;
|
|
dev_err(pdata->dev, "%s: Invalid cipher suit %d\n",
|
|
__func__, supplicant->cipher);
|
|
goto err_unlock;
|
|
}
|
|
|
|
if (macsec_pdata->cipher != supplicant->cipher) {
|
|
ret = osi_macsec_cipher_config(pdata->osi_core,
|
|
supplicant->cipher);
|
|
if (ret < 0) {
|
|
dev_err(pdata->dev, "Failed to set macsec cipher\n");
|
|
}
|
|
macsec_pdata->cipher = supplicant->cipher;
|
|
}
|
|
err_unlock:
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
static int parse_sa_config(struct nlattr **attrs, struct nlattr **tb_sa,
|
|
struct osi_macsec_sc_info *sc_info,
|
|
struct nvpkcs_data *pkcs)
|
|
{
|
|
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_PEER_MACID]) {
|
|
memcpy(sc_info->peer_macid, nla_data(tb_sa[NV_MACSEC_SA_ATTR_PEER_MACID]),
|
|
sizeof(sc_info->peer_macid));
|
|
}
|
|
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_LOWEST_PN]) {
|
|
sc_info->lowest_pn = nla_get_u32(tb_sa[NV_MACSEC_SA_ATTR_LOWEST_PN]);
|
|
}
|
|
if (tb_sa[NV_MACSEC_SA_ATTR_CONF_OFFSET]) {
|
|
sc_info->conf_offset = nla_get_u8(tb_sa[NV_MACSEC_SA_ATTR_CONF_OFFSET]);
|
|
}
|
|
if (tb_sa[NV_MACSEC_SA_ATTR_ENCRYPT]) {
|
|
sc_info->encrypt = nla_get_u8(tb_sa[NV_MACSEC_SA_ATTR_ENCRYPT]);
|
|
}
|
|
#ifdef NVPKCS_MACSEC
|
|
if (pkcs) {
|
|
if (tb_sa[NV_MACSEC_SA_PKCS_KEY_WRAP]) {
|
|
memcpy(pkcs->nv_key,
|
|
nla_data(tb_sa[NV_MACSEC_SA_PKCS_KEY_WRAP]),
|
|
sizeof(pkcs->nv_key));
|
|
}
|
|
if (tb_sa[NV_MACSEC_SA_PKCS_KEK_HANDLE]) {
|
|
pkcs->nv_kek = nla_get_u64(tb_sa[NV_MACSEC_SA_PKCS_KEK_HANDLE]);
|
|
}
|
|
}
|
|
#else
|
|
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));
|
|
}
|
|
#endif /* NVPKCS_MACSEC */
|
|
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 = {0};
|
|
struct nlattr *tb_sa[NUM_NV_MACSEC_SA_ATTR];
|
|
int ret = 0;
|
|
unsigned short kt_idx;
|
|
struct device *dev = NULL;
|
|
#ifndef MACSEC_KEY_PROGRAM
|
|
struct osi_macsec_kt_config kt_config = {0};
|
|
struct osi_macsec_table_config *table_config;
|
|
#endif /* !MACSEC_KEY_PROGRAM */
|
|
struct nvpkcs_data pkcs = {0};
|
|
|
|
PRINT_ENTRY();
|
|
|
|
macsec_pdata = genl_to_macsec_pdata(info);
|
|
if (macsec_pdata) {
|
|
pdata = macsec_pdata->ether_pdata;
|
|
} else {
|
|
ret = -EPROTO;
|
|
goto exit;
|
|
}
|
|
dev = pdata->dev;
|
|
|
|
if (!netif_running(pdata->ndev)) {
|
|
ret = -ENETDOWN;
|
|
dev_err(dev, "%s: MAC interface down!!\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
|
|
parse_sa_config(attrs, tb_sa, &rx_sa, &pkcs)) {
|
|
dev_err(dev, "%s: failed to parse nlattrs", __func__);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
dev_info(dev, "%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);
|
|
dev_info(dev, "\tkey: " KEYSTR, KEY2STR(rx_sa.sak));
|
|
|
|
rx_sa.vlan_in_clear = macsec_pdata->vlan_in_clear;
|
|
mutex_lock(&macsec_pdata->lock);
|
|
ret = osi_macsec_config(pdata->osi_core, &rx_sa, OSI_DISABLE,
|
|
OSI_CTLR_SEL_RX, &kt_idx);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: failed to disable Rx SA", __func__);
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
goto exit;
|
|
}
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
#ifndef MACSEC_KEY_PROGRAM
|
|
table_config = &kt_config.table_config;
|
|
table_config->ctlr_sel = OSI_CTLR_SEL_RX;
|
|
table_config->rw = OSI_LUT_WRITE;
|
|
table_config->index = kt_idx;
|
|
|
|
ret = macsec_tz_kt_config(pdata, NV_MACSEC_CMD_TZ_CONFIG, &kt_config,
|
|
info, &pkcs);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: failed to program SAK through TZ %d",
|
|
__func__, ret);
|
|
goto exit;
|
|
}
|
|
#endif /* !MACSEC_KEY_PROGRAM */
|
|
/* Update the macsec pdata when AN is disabled */
|
|
macsec_pdata->macsec_rx_an_map &= ~((1U) << (rx_sa.curr_an & 0xFU));
|
|
exit:
|
|
PRINT_EXIT();
|
|
return ret;
|
|
}
|
|
|
|
#ifdef MACSEC_KEY_PROGRAM
|
|
/**
|
|
* @brief hkey_generation - Generate HKey for a given SAK
|
|
*
|
|
* @note
|
|
* Algorithm:
|
|
* - Calls Crypto APIs to generate HKey
|
|
*
|
|
* @param[in] sak: Pointer to SA Key.
|
|
* @param[out] hkey: Pointer to HKey generated.
|
|
*
|
|
* @note
|
|
* API Group:
|
|
* - Initialization: Yes
|
|
* - Run time: Yes
|
|
* - De-initialization: No
|
|
*/
|
|
static int hkey_generation(nveu8_t *sak, nveu8_t *hkey)
|
|
{
|
|
struct crypto_cipher *tfm;
|
|
nveu8_t zeros[OSI_KEY_LEN_128] = {0};
|
|
|
|
tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
|
|
if (!tfm)
|
|
return -ENOMEM;
|
|
|
|
if (crypto_cipher_setkey(tfm, sak, OSI_KEY_LEN_128))
|
|
return -EINVAL;
|
|
|
|
crypto_cipher_encrypt_one(tfm, hkey, zeros);
|
|
crypto_free_cipher(tfm);
|
|
return 0;
|
|
}
|
|
#endif /* MACSEC_KEY_PROGRAM */
|
|
|
|
static int macsec_create_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 = {0};
|
|
struct nlattr *tb_sa[NUM_NV_MACSEC_SA_ATTR];
|
|
int ret = 0;
|
|
unsigned short kt_idx;
|
|
struct device *dev = NULL;
|
|
#ifndef MACSEC_KEY_PROGRAM
|
|
int i = 0;
|
|
struct osi_macsec_kt_config kt_config = {0};
|
|
struct osi_macsec_table_config *table_config;
|
|
#endif /* !MACSEC_KEY_PROGRAM */
|
|
struct nvpkcs_data pkcs = {0};
|
|
|
|
PRINT_ENTRY();
|
|
macsec_pdata = genl_to_macsec_pdata(info);
|
|
if (macsec_pdata) {
|
|
pdata = macsec_pdata->ether_pdata;
|
|
} else {
|
|
ret = -EPROTO;
|
|
goto exit;
|
|
}
|
|
dev = pdata->dev;
|
|
|
|
if (!netif_running(pdata->ndev)) {
|
|
ret = -ENETDOWN;
|
|
dev_err(dev, "%s: MAC interface down!!\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
|
|
parse_sa_config(attrs, tb_sa, &rx_sa, &pkcs)) {
|
|
dev_err(dev, "%s: failed to parse nlattrs", __func__);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
rx_sa.pn_window = macsec_pdata->pn_window;
|
|
rx_sa.vlan_in_clear = macsec_pdata->vlan_in_clear;
|
|
dev_info(dev, "%s:\n"
|
|
"\tsci: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n"
|
|
"\tan: %u\n"
|
|
"\tpn: %u\n"
|
|
"\tlowest pn: %u\n"
|
|
"\twindow: %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, rx_sa.lowest_pn, rx_sa.pn_window);
|
|
dev_info(dev, "\tkey: " KEYSTR, KEY2STR(rx_sa.sak));
|
|
|
|
#ifdef MACSEC_KEY_PROGRAM
|
|
ret = hkey_generation(rx_sa.sak, rx_sa.hkey);
|
|
if (ret != 0) {
|
|
dev_err(dev, "%s: failed to Generate HKey", __func__);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
rx_sa.flags = OSI_CREATE_SA;
|
|
#endif /* MACSEC_KEY_PROGRAM */
|
|
|
|
mutex_lock(&macsec_pdata->lock);
|
|
ret = osi_macsec_config(pdata->osi_core, &rx_sa, OSI_ENABLE,
|
|
OSI_CTLR_SEL_RX, &kt_idx);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: failed to enable Rx SA", __func__);
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
goto exit;
|
|
}
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
|
|
#ifndef MACSEC_KEY_PROGRAM
|
|
table_config = &kt_config.table_config;
|
|
table_config->ctlr_sel = OSI_CTLR_SEL_RX;
|
|
table_config->rw = OSI_LUT_WRITE;
|
|
table_config->index = kt_idx;
|
|
kt_config.flags |= OSI_LUT_FLAGS_ENTRY_VALID;
|
|
|
|
for (i = 0; i < OSI_KEY_LEN_256; i++) {
|
|
kt_config.entry.sak[i] = rx_sa.sak[i];
|
|
}
|
|
|
|
ret = macsec_tz_kt_config(pdata, NV_MACSEC_CMD_TZ_CONFIG, &kt_config,
|
|
info, &pkcs);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: failed to program SAK through TZ %d",
|
|
__func__, ret);
|
|
goto exit;
|
|
}
|
|
#endif /* !MACSEC_KEY_PROGRAM */
|
|
|
|
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 = {0};
|
|
struct nlattr *tb_sa[NUM_NV_MACSEC_SA_ATTR];
|
|
int ret = 0;
|
|
unsigned short kt_idx;
|
|
struct device *dev = NULL;
|
|
|
|
PRINT_ENTRY();
|
|
macsec_pdata = genl_to_macsec_pdata(info);
|
|
if (macsec_pdata) {
|
|
pdata = macsec_pdata->ether_pdata;
|
|
} else {
|
|
ret = -EPROTO;
|
|
goto exit;
|
|
}
|
|
dev = pdata->dev;
|
|
|
|
if (!netif_running(pdata->ndev)) {
|
|
ret = -ENETDOWN;
|
|
dev_err(dev, "%s: MAC interface down!!\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
|
|
parse_sa_config(attrs, tb_sa, &rx_sa, NULL)) {
|
|
dev_err(dev, "%s: failed to parse nlattrs", __func__);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
rx_sa.pn_window = macsec_pdata->pn_window;
|
|
rx_sa.vlan_in_clear = macsec_pdata->vlan_in_clear;
|
|
rx_sa.flags = OSI_ENABLE_SA;
|
|
mutex_lock(&macsec_pdata->lock);
|
|
ret = osi_macsec_config(pdata->osi_core, &rx_sa, OSI_ENABLE,
|
|
OSI_CTLR_SEL_RX, &kt_idx);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: failed to enable Rx SA", __func__);
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
goto exit;
|
|
}
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
/* Update the macsec pdata when AN is enabled */
|
|
macsec_pdata->macsec_rx_an_map |= ((1U) << (rx_sa.curr_an & 0xFU));
|
|
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 = {0};
|
|
struct nlattr *tb_sa[NUM_NV_MACSEC_SA_ATTR];
|
|
int ret = 0;
|
|
unsigned short kt_idx;
|
|
struct device *dev = NULL;
|
|
#ifndef MACSEC_KEY_PROGRAM
|
|
struct osi_macsec_kt_config kt_config = {0};
|
|
struct osi_macsec_table_config *table_config;
|
|
#endif /* !MACSEC_KEY_PROGRAM */
|
|
struct nvpkcs_data pkcs = {0};
|
|
|
|
PRINT_ENTRY();
|
|
macsec_pdata = genl_to_macsec_pdata(info);
|
|
if (macsec_pdata) {
|
|
pdata = macsec_pdata->ether_pdata;
|
|
} else {
|
|
ret = -EPROTO;
|
|
goto exit;
|
|
}
|
|
dev = pdata->dev;
|
|
|
|
if (!netif_running(pdata->ndev)) {
|
|
ret = -ENETDOWN;
|
|
dev_err(dev, "%s: MAC interface down!!\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
|
|
parse_sa_config(attrs, tb_sa, &tx_sa, &pkcs)) {
|
|
dev_err(dev, "%s: failed to parse nlattrs", __func__);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
dev_info(dev, "%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);
|
|
dev_info(dev, "\tkey: " KEYSTR, KEY2STR(tx_sa.sak));
|
|
|
|
tx_sa.vlan_in_clear = macsec_pdata->vlan_in_clear;
|
|
mutex_lock(&macsec_pdata->lock);
|
|
ret = osi_macsec_config(pdata->osi_core, &tx_sa, OSI_DISABLE,
|
|
OSI_CTLR_SEL_TX, &kt_idx);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: failed to disable Tx SA", __func__);
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
goto exit;
|
|
}
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
|
|
#ifndef MACSEC_KEY_PROGRAM
|
|
table_config = &kt_config.table_config;
|
|
table_config->ctlr_sel = OSI_CTLR_SEL_TX;
|
|
table_config->rw = OSI_LUT_WRITE;
|
|
table_config->index = kt_idx;
|
|
|
|
ret = macsec_tz_kt_config(pdata, NV_MACSEC_CMD_TZ_CONFIG, &kt_config,
|
|
info, &pkcs);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: failed to program SAK through TZ %d",
|
|
__func__, ret);
|
|
goto exit;
|
|
}
|
|
#endif /* !MACSEC_KEY_PROGRAM */
|
|
|
|
/* Update the macsec pdata when AN is disbled */
|
|
macsec_pdata->macsec_tx_an_map &= ~((1U) << (tx_sa.curr_an & 0xFU));
|
|
exit:
|
|
PRINT_EXIT();
|
|
return ret;
|
|
}
|
|
|
|
static int macsec_create_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 = {0};
|
|
struct nlattr *tb_sa[NUM_NV_MACSEC_SA_ATTR];
|
|
int ret = 0;
|
|
unsigned short kt_idx;
|
|
struct device *dev = NULL;
|
|
#ifndef MACSEC_KEY_PROGRAM
|
|
int i = 0;
|
|
struct osi_macsec_kt_config kt_config = {0};
|
|
struct osi_macsec_table_config *table_config;
|
|
#endif /* !MACSEC_KEY_PROGRAM */
|
|
struct nvpkcs_data pkcs = {0};
|
|
|
|
PRINT_ENTRY();
|
|
macsec_pdata = genl_to_macsec_pdata(info);
|
|
if (macsec_pdata) {
|
|
pdata = macsec_pdata->ether_pdata;
|
|
} else {
|
|
ret = -EPROTO;
|
|
goto exit;
|
|
}
|
|
dev = pdata->dev;
|
|
|
|
if (!netif_running(pdata->ndev)) {
|
|
ret = -ENETDOWN;
|
|
dev_err(dev, "%s: MAC interface down!!\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
|
|
parse_sa_config(attrs, tb_sa, &tx_sa, &pkcs)) {
|
|
dev_err(dev, "%s: failed to parse nlattrs", __func__);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
tx_sa.pn_window = macsec_pdata->pn_window;
|
|
tx_sa.vlan_in_clear = macsec_pdata->vlan_in_clear;
|
|
dev_info(dev, "%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);
|
|
dev_info(dev, "\tkey: " KEYSTR, KEY2STR(tx_sa.sak));
|
|
|
|
#ifdef MACSEC_KEY_PROGRAM
|
|
tx_sa.flags = OSI_CREATE_SA;
|
|
ret = hkey_generation(tx_sa.sak, tx_sa.hkey);
|
|
if (ret != 0) {
|
|
dev_err(dev, "%s: failed to Generate HKey", __func__);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
#endif /* MACSEC_KEY_PROGRAM */
|
|
|
|
mutex_lock(&macsec_pdata->lock);
|
|
ret = osi_macsec_config(pdata->osi_core, &tx_sa, OSI_ENABLE,
|
|
OSI_CTLR_SEL_TX, &kt_idx);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: failed to enable Tx SA", __func__);
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
goto exit;
|
|
}
|
|
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
#ifndef MACSEC_KEY_PROGRAM
|
|
table_config = &kt_config.table_config;
|
|
table_config->ctlr_sel = OSI_CTLR_SEL_TX;
|
|
table_config->rw = OSI_LUT_WRITE;
|
|
table_config->index = kt_idx;
|
|
kt_config.flags |= OSI_LUT_FLAGS_ENTRY_VALID;
|
|
|
|
for (i = 0; i < OSI_KEY_LEN_256; i++) {
|
|
kt_config.entry.sak[i] = tx_sa.sak[i];
|
|
}
|
|
|
|
ret = macsec_tz_kt_config(pdata, NV_MACSEC_CMD_TZ_CONFIG, &kt_config,
|
|
info, &pkcs);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: failed to program SAK through TZ %d",
|
|
__func__, ret);
|
|
goto exit;
|
|
}
|
|
#endif /* !MACSEC_KEY_PROGRAM */
|
|
|
|
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 = {0};
|
|
struct nlattr *tb_sa[NUM_NV_MACSEC_SA_ATTR];
|
|
int ret = 0;
|
|
unsigned short kt_idx;
|
|
struct device *dev = NULL;
|
|
|
|
PRINT_ENTRY();
|
|
macsec_pdata = genl_to_macsec_pdata(info);
|
|
if (macsec_pdata) {
|
|
pdata = macsec_pdata->ether_pdata;
|
|
} else {
|
|
ret = -EPROTO;
|
|
goto exit;
|
|
}
|
|
dev = pdata->dev;
|
|
|
|
if (!netif_running(pdata->ndev)) {
|
|
ret = -ENETDOWN;
|
|
dev_err(dev, "%s: MAC interface down!!\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
|
|
parse_sa_config(attrs, tb_sa, &tx_sa, NULL)) {
|
|
dev_err(dev, "%s: failed to parse nlattrs", __func__);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
tx_sa.pn_window = macsec_pdata->pn_window;
|
|
tx_sa.flags = OSI_ENABLE_SA;
|
|
tx_sa.vlan_in_clear = macsec_pdata->vlan_in_clear;
|
|
mutex_lock(&macsec_pdata->lock);
|
|
ret = osi_macsec_config(pdata->osi_core, &tx_sa, OSI_ENABLE,
|
|
OSI_CTLR_SEL_TX, &kt_idx);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: failed to enable Tx SA", __func__);
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
goto exit;
|
|
}
|
|
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
/* Update the macsec pdata when AN is enabled */
|
|
macsec_pdata->macsec_tx_an_map |= ((1U) << (tx_sa.curr_an & 0xFU));
|
|
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 = NULL;
|
|
struct macsec_supplicant_data *supplicant;
|
|
struct ether_priv_data *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) {
|
|
pr_err("%s: failed to get macsec_pdata", __func__);
|
|
ret = -EPROTO;
|
|
goto exit;
|
|
}
|
|
pdata = macsec_pdata->ether_pdata;
|
|
|
|
mutex_lock(&macsec_pdata->lock);
|
|
supplicant = macsec_get_supplicant(macsec_pdata, info->snd_portid);
|
|
|
|
if (!supplicant) {
|
|
ret = -EPROTO;
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
dev_err(pdata->dev, "%s: failed to get supplicant data",
|
|
__func__);
|
|
goto exit;
|
|
}
|
|
|
|
supplicant->snd_portid = OSI_NONE;
|
|
supplicant->in_use = OSI_NONE;
|
|
macsec_pdata->next_supp_idx--;
|
|
|
|
/* check for reference count to zero before deinit macsec */
|
|
if ((atomic_read(&macsec_pdata->ref_count) - 1) > 0) {
|
|
ret = 0;
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
goto done;
|
|
}
|
|
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
|
|
if (!netif_running(pdata->ndev)) {
|
|
ret = -ENETDOWN;
|
|
dev_err(pdata->dev, "%s: MAC interface down!!", __func__);
|
|
goto done;
|
|
}
|
|
|
|
ret = macsec_close(macsec_pdata);
|
|
if (ret < 0) {
|
|
ret = -EPROTO;
|
|
goto exit;
|
|
}
|
|
done:
|
|
if (atomic_read(&macsec_pdata->ref_count) > 0) {
|
|
atomic_dec(&macsec_pdata->ref_count);
|
|
}
|
|
dev_info(pdata->dev, "%s: ref_count %d", __func__,
|
|
atomic_read(&macsec_pdata->ref_count));
|
|
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 = NULL;
|
|
struct macsec_supplicant_data *supplicant;
|
|
struct ether_priv_data *pdata = NULL;
|
|
struct device *dev = NULL;
|
|
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 = -EPROTO;
|
|
pr_err("%s: failed to get macsec_pdata", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
pdata = macsec_pdata->ether_pdata;
|
|
dev = pdata->dev;
|
|
|
|
if (!netif_running(pdata->ndev)) {
|
|
ret = -ENETDOWN;
|
|
dev_err(dev, "%s: MAC interface down!!\n", __func__);
|
|
goto exit;
|
|
}
|
|
mutex_lock(&macsec_pdata->lock);
|
|
/* only one supplicant is allowed per VF */
|
|
if (macsec_pdata->next_supp_idx >= MAX_SUPPLICANTS_ALLOWED) {
|
|
ret = -EPROTO;
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
dev_err(dev, "%s: Reached max supported supplicants %u", __func__,
|
|
macsec_pdata->next_supp_idx);
|
|
goto exit;
|
|
}
|
|
|
|
supplicant = macsec_get_supplicant(macsec_pdata, info->snd_portid);
|
|
if (!supplicant) {
|
|
supplicant = &macsec_pdata->supplicant[macsec_pdata->next_supp_idx];
|
|
macsec_pdata->next_supp_idx++;
|
|
}
|
|
|
|
supplicant->snd_portid = info->snd_portid;
|
|
supplicant->in_use = OSI_ENABLE;
|
|
|
|
/* check reference count and if macsec already init'd return success */
|
|
if (atomic_read(&macsec_pdata->ref_count) > 0) {
|
|
ret = 0;
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
goto done;
|
|
}
|
|
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
ret = macsec_open(macsec_pdata, info);
|
|
if (ret < 0) {
|
|
ret = -EPROTO;
|
|
goto exit;
|
|
}
|
|
macsec_pdata->macsec_rx_an_map = 0U;
|
|
macsec_pdata->macsec_tx_an_map = 0U;
|
|
done:
|
|
atomic_inc(&macsec_pdata->ref_count);
|
|
dev_info(dev, "%s: ref_count %d", __func__,
|
|
atomic_read(&macsec_pdata->ref_count));
|
|
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;
|
|
struct macsec_priv_data *macsec_pdata = NULL;
|
|
struct ether_priv_data *pdata = NULL;
|
|
struct device *dev = NULL;
|
|
int ret = 0;
|
|
|
|
PRINT_ENTRY();
|
|
|
|
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
|
|
!attrs[NV_MACSEC_ATTR_REPLAY_PROT_EN] ||
|
|
!attrs[NV_MACSEC_ATTR_REPLAY_WINDOW]) {
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
replay_prot = nla_get_u32(attrs[NV_MACSEC_ATTR_REPLAY_PROT_EN]);
|
|
window = nla_get_u32(attrs[NV_MACSEC_ATTR_REPLAY_WINDOW]);
|
|
macsec_pdata = genl_to_macsec_pdata(info);
|
|
|
|
if (!macsec_pdata) {
|
|
ret = -EPROTO;
|
|
pr_err("%s: failed to get macsec_pdata", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
pdata = macsec_pdata->ether_pdata;
|
|
dev = pdata->dev;
|
|
|
|
if (!netif_running(pdata->ndev)) {
|
|
ret = -ENETDOWN;
|
|
dev_err(pdata->dev, "%s: MAC interface down!!\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
/* If Replay protection is disabled from supplicant use maximum
|
|
* PN window as replay protecion is already enabled in macsec_init
|
|
*/
|
|
if (replay_prot == OSI_ENABLE)
|
|
macsec_pdata->pn_window = window;
|
|
else
|
|
macsec_pdata->pn_window = OSI_PN_MAX_DEFAULT;
|
|
|
|
exit:
|
|
PRINT_EXIT();
|
|
return ret;
|
|
}
|
|
|
|
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_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_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_CREATE_TX_SA,
|
|
.doit = macsec_create_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_CREATE_RX_SA,
|
|
.doit = macsec_create_rx_sa,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = NV_MACSEC_CMD_DIS_RX_SA,
|
|
.doit = macsec_dis_rx_sa,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = NV_MACSEC_CMD_GET_TX_NEXT_PN,
|
|
.doit = macsec_get_tx_next_pn,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
};
|
|
|
|
void macsec_remove(struct ether_priv_data *pdata)
|
|
{
|
|
struct macsec_priv_data *macsec_pdata = NULL;
|
|
struct macsec_supplicant_data *supplicant = NULL;
|
|
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
|
int i;
|
|
|
|
PRINT_ENTRY();
|
|
macsec_pdata = pdata->macsec_pdata;
|
|
if (macsec_pdata) {
|
|
mutex_lock(&macsec_pdata->lock);
|
|
/* Delete if any supplicant active heartbeat timer */
|
|
supplicant = macsec_pdata->supplicant;
|
|
for (i = 0; i < OSI_MAX_NUM_SC; i++) {
|
|
if (supplicant[i].in_use == OSI_ENABLE) {
|
|
supplicant->snd_portid = OSI_NONE;
|
|
supplicant->in_use = OSI_NONE;
|
|
}
|
|
}
|
|
mutex_unlock(&macsec_pdata->lock);
|
|
/* if macsec_close() is not called by supplicant gracefully
|
|
* close it now.
|
|
*/
|
|
if ((atomic_read(&macsec_pdata->ref_count) > 0) ||
|
|
(macsec_pdata->enabled == OSI_ENABLE)) {
|
|
macsec_close(macsec_pdata);
|
|
}
|
|
|
|
/* Unregister generic netlink */
|
|
if (macsec_pdata->is_nv_macsec_fam_registered == OSI_ENABLE) {
|
|
genl_unregister_family(&macsec_pdata->nv_macsec_fam);
|
|
macsec_pdata->is_nv_macsec_fam_registered = OSI_DISABLE;
|
|
}
|
|
|
|
if (osi_core->use_virtualization == OSI_DISABLE) {
|
|
macsec_disable_car(macsec_pdata);
|
|
}
|
|
|
|
/* Release platform resources */
|
|
macsec_release_platform_res(macsec_pdata);
|
|
/* free macsec priv */
|
|
devm_kfree(pdata->dev, macsec_pdata);
|
|
}
|
|
PRINT_EXIT();
|
|
}
|
|
|
|
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;
|
|
struct device_node *np = dev->of_node;
|
|
int ret = 0;
|
|
#ifdef MACSEC_KEY_PROGRAM
|
|
unsigned long tz_addr = 0;
|
|
#endif
|
|
|
|
PRINT_ENTRY();
|
|
if (osi_core->use_virtualization == OSI_DISABLE) {
|
|
/* 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;
|
|
}
|
|
#ifdef MACSEC_KEY_PROGRAM
|
|
/* store TZ window base address */
|
|
tz_addr = (res->start - MACSEC_SIZE);
|
|
#endif
|
|
} else {
|
|
/* MACsec not supported per DT config, 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;
|
|
}
|
|
|
|
#ifdef MACSEC_KEY_PROGRAM
|
|
osi_core->tz_base = devm_ioremap(dev, tz_addr, MACSEC_SIZE);
|
|
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;
|
|
}
|
|
#endif
|
|
}
|
|
/* 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;
|
|
|
|
/* Read if macsec is enabled in DT */
|
|
ret = of_property_read_u32(np, "nvidia,macsec-enable",
|
|
&macsec_pdata->is_macsec_enabled_in_dt);
|
|
if (ret != 0 || !macsec_enable ||
|
|
macsec_pdata->is_macsec_enabled_in_dt == 0U) {
|
|
dev_info(dev,
|
|
"macsec parameter is missing or disabled\n");
|
|
ret = 1;
|
|
goto init_err;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "nvidia,macsec_vlan_in_clear",
|
|
&macsec_pdata->vlan_in_clear);
|
|
if (ret != 0) {
|
|
dev_info(dev,
|
|
"DT info about vlan in clear is missing setting default-disabled\n");
|
|
macsec_pdata->vlan_in_clear = OSI_DISABLE;
|
|
}
|
|
|
|
mutex_init(&pdata->macsec_pdata->lock);
|
|
|
|
/* Read MAC instance id and used in TZ api's */
|
|
ret = of_property_read_u32(np, "nvidia,instance_id", &macsec_pdata->id);
|
|
if (ret != 0) {
|
|
dev_info(dev,
|
|
"DT instance_id missing, setting default to MGBE0\n");
|
|
macsec_pdata->id = 0;
|
|
}
|
|
|
|
osi_core->instance_id = macsec_pdata->id;
|
|
/* Get OSI MACsec ops */
|
|
if (osi_init_macsec_ops(osi_core) != 0) {
|
|
dev_err(dev, "osi_init_macsec_ops failed\n");
|
|
ret = -1;
|
|
goto init_err;
|
|
}
|
|
|
|
if (osi_core->use_virtualization == OSI_DISABLE) {
|
|
/* 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 init_err;
|
|
}
|
|
|
|
/* Enable CAR */
|
|
ret = macsec_enable_car(macsec_pdata);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Unable to enable macsec clks & reset\n");
|
|
goto car_err;
|
|
}
|
|
}
|
|
|
|
/* Register macsec generic netlink ops */
|
|
macsec_pdata->nv_macsec_fam.hdrsize = 0;
|
|
macsec_pdata->nv_macsec_fam.version = NV_MACSEC_GENL_VERSION;
|
|
macsec_pdata->nv_macsec_fam.maxattr = NV_MACSEC_ATTR_MAX;
|
|
macsec_pdata->nv_macsec_fam.module = THIS_MODULE;
|
|
macsec_pdata->nv_macsec_fam.ops = nv_macsec_genl_ops;
|
|
macsec_pdata->nv_macsec_fam.n_ops = ARRAY_SIZE(nv_macsec_genl_ops);
|
|
macsec_pdata->nv_macsec_fam.policy = nv_macsec_genl_policy;
|
|
if (macsec_pdata->is_nv_macsec_fam_registered == OSI_DISABLE) {
|
|
if (strlen(netdev_name(pdata->ndev)) >= GENL_NAMSIZ) {
|
|
dev_err(dev, "Intf name %s of len %lu exceed nl_family name size\n",
|
|
netdev_name(pdata->ndev),
|
|
strlen(netdev_name(pdata->ndev)));
|
|
ret = -1;
|
|
goto genl_err;
|
|
} else {
|
|
strncpy(macsec_pdata->nv_macsec_fam.name,
|
|
netdev_name(pdata->ndev), GENL_NAMSIZ - 1);
|
|
}
|
|
ret = genl_register_family(&macsec_pdata->nv_macsec_fam);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to register GENL ops %d\n",
|
|
ret);
|
|
goto genl_err;
|
|
}
|
|
|
|
macsec_pdata->is_nv_macsec_fam_registered = OSI_ENABLE;
|
|
}
|
|
|
|
PRINT_EXIT();
|
|
return ret;
|
|
genl_err:
|
|
if (osi_core->use_virtualization == OSI_DISABLE) {
|
|
macsec_disable_car(macsec_pdata);
|
|
}
|
|
car_err:
|
|
macsec_release_platform_res(macsec_pdata);
|
|
init_err:
|
|
devm_kfree(dev, pdata->macsec_pdata);
|
|
pdata->macsec_pdata = NULL;
|
|
exit:
|
|
PRINT_EXIT();
|
|
return ret;
|
|
}
|
|
|
|
#ifndef MACSEC_KEY_PROGRAM
|
|
/**
|
|
* @brief macsec_tz_kt_config - Program macsec key table entry.
|
|
*
|
|
* @param[in] priv: OSD private data structure.
|
|
* @param[in] cmd: macsec TZ config cmd
|
|
* @param[in] kt_config: Pointer to osi_macsec_kt_config structure
|
|
* @param[in] info: Pointer to netlink msg structure
|
|
*
|
|
* @retval 0 on success
|
|
* @retval negative value on failure.
|
|
*/
|
|
static int macsec_tz_kt_config(struct ether_priv_data *pdata,
|
|
unsigned char cmd,
|
|
struct osi_macsec_kt_config *const kt_config,
|
|
struct genl_info *const info, struct nvpkcs_data *pkcs)
|
|
{
|
|
struct sk_buff *msg;
|
|
struct nlattr *nest;
|
|
void *msg_head;
|
|
int ret = 0;
|
|
struct device *dev = pdata->dev;
|
|
struct macsec_priv_data *macsec_pdata = pdata->macsec_pdata;
|
|
|
|
PRINT_ENTRY();
|
|
if (info == OSI_NULL) {
|
|
dev_info(dev,"Can not config key through TZ, genl_info NULL\n");
|
|
/* return success, as info can be NULL if called from
|
|
* sysfs calls
|
|
*/
|
|
ret = 0;
|
|
goto fail;
|
|
}
|
|
|
|
if (cmd != NV_MACSEC_CMD_TZ_KT_RESET &&
|
|
cmd != NV_MACSEC_CMD_TZ_CONFIG) {
|
|
dev_err(dev, "%s: Wrong TZ cmd %d\n", __func__, cmd);
|
|
ret = -1;
|
|
goto fail;
|
|
}
|
|
|
|
msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
|
if (msg == NULL) {
|
|
dev_err(dev, "Unable to alloc genl reply\n");
|
|
ret = -ENOMEM;
|
|
goto fail;
|
|
}
|
|
|
|
msg_head = genlmsg_put_reply(msg, info, &macsec_pdata->nv_macsec_fam, 0, cmd);
|
|
if (msg_head == NULL) {
|
|
dev_err(dev, "unable to get replyhead\n");
|
|
ret = -EINVAL;
|
|
goto failure;
|
|
}
|
|
if (cmd == NV_MACSEC_CMD_TZ_KT_RESET) {
|
|
nest = nla_nest_start(msg, NV_MACSEC_ATTR_TZ_KT_RESET);
|
|
if (!nest) {
|
|
ret = EINVAL;
|
|
goto failure;
|
|
}
|
|
nla_put_u32(msg, NV_MACSEC_TZ_KT_RESET_INSTANCE_ID,
|
|
pdata->osi_core->instance_id);
|
|
nla_nest_end(msg, nest);
|
|
}
|
|
|
|
if (cmd == NV_MACSEC_CMD_TZ_CONFIG && kt_config != NULL) {
|
|
/* pr_err("%s: ctrl: %hu rw: %hu idx: %hu flags: %#x\n"
|
|
* __func__,
|
|
* kt_config->table_config.ctlr_sel,
|
|
* kt_config->table_config.rw,
|
|
* kt_config->table_config.index,
|
|
* kt_config->flags);
|
|
*/
|
|
|
|
nest = nla_nest_start(msg, NV_MACSEC_ATTR_TZ_CONFIG);
|
|
if (!nest) {
|
|
ret = EINVAL;
|
|
goto failure;
|
|
}
|
|
nla_put_u32(msg, NV_MACSEC_TZ_INSTANCE_ID,
|
|
pdata->osi_core->instance_id);
|
|
nla_put_u8(msg, NV_MACSEC_TZ_ATTR_CTRL,
|
|
kt_config->table_config.ctlr_sel);
|
|
nla_put_u8(msg, NV_MACSEC_TZ_ATTR_RW,
|
|
kt_config->table_config.rw);
|
|
nla_put_u8(msg, NV_MACSEC_TZ_ATTR_INDEX,
|
|
kt_config->table_config.index);
|
|
nla_put_u32(msg, NV_MACSEC_TZ_ATTR_FLAG, kt_config->flags);
|
|
#ifdef NVPKCS_MACSEC
|
|
if (pkcs) {
|
|
nla_put(msg, NV_MACSEC_TZ_PKCS_KEY_WRAP,
|
|
sizeof(pkcs->nv_key),
|
|
pkcs->nv_key);
|
|
nla_put_u64_64bit(msg, NV_MACSEC_TZ_PKCS_KEK_HANDLE,
|
|
pkcs->nv_kek,
|
|
NL_POLICY_TYPE_ATTR_PAD);
|
|
}
|
|
#else
|
|
nla_put(msg, NV_MACSEC_TZ_ATTR_KEY, OSI_KEY_LEN_256,
|
|
kt_config->entry.sak);
|
|
#endif /* NVPKCS_MACSEC */
|
|
nla_nest_end(msg, nest);
|
|
}
|
|
genlmsg_end(msg, msg_head);
|
|
ret = genlmsg_reply(msg, info);
|
|
if (ret != 0) {
|
|
dev_err(dev, "Unable to send reply\n");
|
|
}
|
|
|
|
PRINT_EXIT();
|
|
return ret;
|
|
failure:
|
|
nlmsg_free(msg);
|
|
fail:
|
|
PRINT_EXIT();
|
|
return ret;
|
|
}
|
|
#endif /* MACSEC_KEY_PROGRAM */
|
|
|
|
static int macsec_get_tx_next_pn(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;
|
|
struct device *dev = NULL;
|
|
unsigned char cmd;
|
|
struct sk_buff *msg;
|
|
void *msg_head;
|
|
struct nlattr *nest;
|
|
struct osi_macsec_lut_config lut_config = {0};
|
|
unsigned int key_index = 0;
|
|
struct osi_core_priv_data *osi_core = NULL;
|
|
|
|
PRINT_ENTRY();
|
|
|
|
macsec_pdata = genl_to_macsec_pdata(info);
|
|
if (macsec_pdata) {
|
|
pdata = macsec_pdata->ether_pdata;
|
|
} else {
|
|
ret = -EPROTO;
|
|
goto exit;
|
|
}
|
|
dev = pdata->dev;
|
|
osi_core = pdata->osi_core;
|
|
|
|
if (!netif_running(pdata->ndev)) {
|
|
ret = -ENETDOWN;
|
|
dev_err(dev, "%s: MAC interface down!!\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
if (!attrs[NV_MACSEC_ATTR_IFNAME] ||
|
|
parse_sa_config(attrs, tb_sa, &tx_sa, NULL)) {
|
|
dev_err(dev, "%s: failed to parse nlattrs", __func__);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
ret = osi_macsec_get_sc_lut_key_index(osi_core, tx_sa.sci, &key_index,
|
|
OSI_CTLR_SEL_TX);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to get Key_index\n");
|
|
goto exit;
|
|
}
|
|
|
|
memset(&lut_config, OSI_NONE, sizeof(lut_config));
|
|
lut_config.table_config.ctlr_sel = OSI_CTLR_SEL_TX;
|
|
lut_config.table_config.rw = OSI_LUT_READ;
|
|
lut_config.table_config.index = key_index + tx_sa.curr_an;
|
|
lut_config.lut_sel = OSI_LUT_SEL_SA_STATE;
|
|
if (osi_macsec_config_lut(osi_core, &lut_config) < 0) {
|
|
pr_err("%s: Failed to read SA STATE LUT\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
cmd = NV_MACSEC_CMD_GET_TX_NEXT_PN;
|
|
msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
|
if (!msg) {
|
|
dev_err(dev, "Unable to alloc genl reply\n");
|
|
ret = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
|
|
msg_head = genlmsg_put_reply(msg, info, &macsec_pdata->nv_macsec_fam, 0, cmd);
|
|
if (!msg_head) {
|
|
dev_err(dev, "unable to get replyhead\n");
|
|
ret = -EINVAL;
|
|
goto failure;
|
|
}
|
|
nest = nla_nest_start(msg, NV_MACSEC_ATTR_SA_CONFIG);
|
|
if (!nest) {
|
|
ret = -EINVAL;
|
|
goto failure;
|
|
}
|
|
nla_put_u32(msg, NV_MACSEC_SA_ATTR_PN, lut_config.sa_state_out.next_pn);
|
|
nla_put_u8(msg, NV_MACSEC_SA_ATTR_AN, tx_sa.curr_an);
|
|
nla_put(msg, NV_MACSEC_SA_ATTR_SCI, OSI_SCI_LEN, tx_sa.sci);
|
|
nla_nest_end(msg, nest);
|
|
genlmsg_end(msg, msg_head);
|
|
ret = genlmsg_reply(msg, info);
|
|
if (ret != 0)
|
|
dev_err(dev, "Unable to send reply\n");
|
|
|
|
PRINT_EXIT();
|
|
return ret;
|
|
failure:
|
|
nlmsg_free(msg);
|
|
exit:
|
|
PRINT_EXIT();
|
|
return ret;
|
|
}
|
|
#endif /* MACSEC_SUPPORT */
|