| // SPDX-License-Identifier: GPL-2.0 |
| /* Marvell MACSEC hardware offload driver |
| * |
| * Copyright (C) 2022 Marvell. |
| */ |
| |
| #include <crypto/skcipher.h> |
| #include <linux/rtnetlink.h> |
| #include <linux/bitfield.h> |
| #include "otx2_common.h" |
| |
| #define MCS_TCAM0_MAC_DA_MASK GENMASK_ULL(47, 0) |
| #define MCS_TCAM0_MAC_SA_MASK GENMASK_ULL(63, 48) |
| #define MCS_TCAM1_MAC_SA_MASK GENMASK_ULL(31, 0) |
| #define MCS_TCAM1_ETYPE_MASK GENMASK_ULL(47, 32) |
| |
| #define MCS_SA_MAP_MEM_SA_USE BIT_ULL(9) |
| |
| #define MCS_RX_SECY_PLCY_RW_MASK GENMASK_ULL(49, 18) |
| #define MCS_RX_SECY_PLCY_RP BIT_ULL(17) |
| #define MCS_RX_SECY_PLCY_AUTH_ENA BIT_ULL(16) |
| #define MCS_RX_SECY_PLCY_CIP GENMASK_ULL(8, 5) |
| #define MCS_RX_SECY_PLCY_VAL GENMASK_ULL(2, 1) |
| #define MCS_RX_SECY_PLCY_ENA BIT_ULL(0) |
| |
| #define MCS_TX_SECY_PLCY_MTU GENMASK_ULL(43, 28) |
| #define MCS_TX_SECY_PLCY_ST_TCI GENMASK_ULL(27, 22) |
| #define MCS_TX_SECY_PLCY_ST_OFFSET GENMASK_ULL(21, 15) |
| #define MCS_TX_SECY_PLCY_INS_MODE BIT_ULL(14) |
| #define MCS_TX_SECY_PLCY_AUTH_ENA BIT_ULL(13) |
| #define MCS_TX_SECY_PLCY_CIP GENMASK_ULL(5, 2) |
| #define MCS_TX_SECY_PLCY_PROTECT BIT_ULL(1) |
| #define MCS_TX_SECY_PLCY_ENA BIT_ULL(0) |
| |
| #define MCS_GCM_AES_128 0 |
| #define MCS_GCM_AES_256 1 |
| #define MCS_GCM_AES_XPN_128 2 |
| #define MCS_GCM_AES_XPN_256 3 |
| |
| #define MCS_TCI_ES 0x40 /* end station */ |
| #define MCS_TCI_SC 0x20 /* SCI present */ |
| #define MCS_TCI_SCB 0x10 /* epon */ |
| #define MCS_TCI_E 0x08 /* encryption */ |
| #define MCS_TCI_C 0x04 /* changed text */ |
| |
| #define CN10K_MAX_HASH_LEN 16 |
| #define CN10K_MAX_SAK_LEN 32 |
| |
| static int cn10k_ecb_aes_encrypt(struct otx2_nic *pfvf, u8 *sak, |
| u16 sak_len, u8 *hash) |
| { |
| u8 data[CN10K_MAX_HASH_LEN] = { 0 }; |
| struct skcipher_request *req = NULL; |
| struct scatterlist sg_src, sg_dst; |
| struct crypto_skcipher *tfm; |
| DECLARE_CRYPTO_WAIT(wait); |
| int err; |
| |
| tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0); |
| if (IS_ERR(tfm)) { |
| dev_err(pfvf->dev, "failed to allocate transform for ecb-aes\n"); |
| return PTR_ERR(tfm); |
| } |
| |
| req = skcipher_request_alloc(tfm, GFP_KERNEL); |
| if (!req) { |
| dev_err(pfvf->dev, "failed to allocate request for skcipher\n"); |
| err = -ENOMEM; |
| goto free_tfm; |
| } |
| |
| err = crypto_skcipher_setkey(tfm, sak, sak_len); |
| if (err) { |
| dev_err(pfvf->dev, "failed to set key for skcipher\n"); |
| goto free_req; |
| } |
| |
| /* build sg list */ |
| sg_init_one(&sg_src, data, CN10K_MAX_HASH_LEN); |
| sg_init_one(&sg_dst, hash, CN10K_MAX_HASH_LEN); |
| |
| skcipher_request_set_callback(req, 0, crypto_req_done, &wait); |
| skcipher_request_set_crypt(req, &sg_src, &sg_dst, |
| CN10K_MAX_HASH_LEN, NULL); |
| |
| err = crypto_skcipher_encrypt(req); |
| err = crypto_wait_req(err, &wait); |
| |
| free_req: |
| skcipher_request_free(req); |
| free_tfm: |
| crypto_free_skcipher(tfm); |
| return err; |
| } |
| |
| static struct cn10k_mcs_txsc *cn10k_mcs_get_txsc(struct cn10k_mcs_cfg *cfg, |
| struct macsec_secy *secy) |
| { |
| struct cn10k_mcs_txsc *txsc; |
| |
| list_for_each_entry(txsc, &cfg->txsc_list, entry) { |
| if (txsc->sw_secy == secy) |
| return txsc; |
| } |
| |
| return NULL; |
| } |
| |
| static struct cn10k_mcs_rxsc *cn10k_mcs_get_rxsc(struct cn10k_mcs_cfg *cfg, |
| struct macsec_secy *secy, |
| struct macsec_rx_sc *rx_sc) |
| { |
| struct cn10k_mcs_rxsc *rxsc; |
| |
| list_for_each_entry(rxsc, &cfg->rxsc_list, entry) { |
| if (rxsc->sw_rxsc == rx_sc && rxsc->sw_secy == secy) |
| return rxsc; |
| } |
| |
| return NULL; |
| } |
| |
| static const char *rsrc_name(enum mcs_rsrc_type rsrc_type) |
| { |
| switch (rsrc_type) { |
| case MCS_RSRC_TYPE_FLOWID: |
| return "FLOW"; |
| case MCS_RSRC_TYPE_SC: |
| return "SC"; |
| case MCS_RSRC_TYPE_SECY: |
| return "SECY"; |
| case MCS_RSRC_TYPE_SA: |
| return "SA"; |
| default: |
| return "Unknown"; |
| }; |
| |
| return "Unknown"; |
| } |
| |
| static int cn10k_mcs_alloc_rsrc(struct otx2_nic *pfvf, enum mcs_direction dir, |
| enum mcs_rsrc_type type, u16 *rsrc_id) |
| { |
| struct mbox *mbox = &pfvf->mbox; |
| struct mcs_alloc_rsrc_req *req; |
| struct mcs_alloc_rsrc_rsp *rsp; |
| int ret = -ENOMEM; |
| |
| mutex_lock(&mbox->lock); |
| |
| req = otx2_mbox_alloc_msg_mcs_alloc_resources(mbox); |
| if (!req) |
| goto fail; |
| |
| req->rsrc_type = type; |
| req->rsrc_cnt = 1; |
| req->dir = dir; |
| |
| ret = otx2_sync_mbox_msg(mbox); |
| if (ret) |
| goto fail; |
| |
| rsp = (struct mcs_alloc_rsrc_rsp *)otx2_mbox_get_rsp(&pfvf->mbox.mbox, |
| 0, &req->hdr); |
| if (IS_ERR(rsp) || req->rsrc_cnt != rsp->rsrc_cnt || |
| req->rsrc_type != rsp->rsrc_type || req->dir != rsp->dir) { |
| ret = -EINVAL; |
| goto fail; |
| } |
| |
| switch (rsp->rsrc_type) { |
| case MCS_RSRC_TYPE_FLOWID: |
| *rsrc_id = rsp->flow_ids[0]; |
| break; |
| case MCS_RSRC_TYPE_SC: |
| *rsrc_id = rsp->sc_ids[0]; |
| break; |
| case MCS_RSRC_TYPE_SECY: |
| *rsrc_id = rsp->secy_ids[0]; |
| break; |
| case MCS_RSRC_TYPE_SA: |
| *rsrc_id = rsp->sa_ids[0]; |
| break; |
| default: |
| ret = -EINVAL; |
| goto fail; |
| } |
| |
| mutex_unlock(&mbox->lock); |
| |
| return 0; |
| fail: |
| dev_err(pfvf->dev, "Failed to allocate %s %s resource\n", |
| dir == MCS_TX ? "TX" : "RX", rsrc_name(type)); |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static void cn10k_mcs_free_rsrc(struct otx2_nic *pfvf, enum mcs_direction dir, |
| enum mcs_rsrc_type type, u16 hw_rsrc_id, |
| bool all) |
| { |
| struct mcs_clear_stats *clear_req; |
| struct mbox *mbox = &pfvf->mbox; |
| struct mcs_free_rsrc_req *req; |
| |
| mutex_lock(&mbox->lock); |
| |
| clear_req = otx2_mbox_alloc_msg_mcs_clear_stats(mbox); |
| if (!clear_req) |
| goto fail; |
| |
| clear_req->id = hw_rsrc_id; |
| clear_req->type = type; |
| clear_req->dir = dir; |
| |
| req = otx2_mbox_alloc_msg_mcs_free_resources(mbox); |
| if (!req) |
| goto fail; |
| |
| req->rsrc_id = hw_rsrc_id; |
| req->rsrc_type = type; |
| req->dir = dir; |
| if (all) |
| req->all = 1; |
| |
| if (otx2_sync_mbox_msg(&pfvf->mbox)) |
| goto fail; |
| |
| mutex_unlock(&mbox->lock); |
| |
| return; |
| fail: |
| dev_err(pfvf->dev, "Failed to free %s %s resource\n", |
| dir == MCS_TX ? "TX" : "RX", rsrc_name(type)); |
| mutex_unlock(&mbox->lock); |
| } |
| |
| static int cn10k_mcs_alloc_txsa(struct otx2_nic *pfvf, u16 *hw_sa_id) |
| { |
| return cn10k_mcs_alloc_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SA, hw_sa_id); |
| } |
| |
| static int cn10k_mcs_alloc_rxsa(struct otx2_nic *pfvf, u16 *hw_sa_id) |
| { |
| return cn10k_mcs_alloc_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SA, hw_sa_id); |
| } |
| |
| static void cn10k_mcs_free_txsa(struct otx2_nic *pfvf, u16 hw_sa_id) |
| { |
| cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SA, hw_sa_id, false); |
| } |
| |
| static void cn10k_mcs_free_rxsa(struct otx2_nic *pfvf, u16 hw_sa_id) |
| { |
| cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SA, hw_sa_id, false); |
| } |
| |
| static int cn10k_mcs_write_rx_secy(struct otx2_nic *pfvf, |
| struct macsec_secy *secy, u8 hw_secy_id) |
| { |
| struct mcs_secy_plcy_write_req *req; |
| struct mbox *mbox = &pfvf->mbox; |
| u64 policy; |
| u8 cipher; |
| int ret; |
| |
| mutex_lock(&mbox->lock); |
| |
| req = otx2_mbox_alloc_msg_mcs_secy_plcy_write(mbox); |
| if (!req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| policy = FIELD_PREP(MCS_RX_SECY_PLCY_RW_MASK, secy->replay_window); |
| if (secy->replay_protect) |
| policy |= MCS_RX_SECY_PLCY_RP; |
| |
| policy |= MCS_RX_SECY_PLCY_AUTH_ENA; |
| |
| switch (secy->key_len) { |
| case 16: |
| cipher = secy->xpn ? MCS_GCM_AES_XPN_128 : MCS_GCM_AES_128; |
| break; |
| case 32: |
| cipher = secy->xpn ? MCS_GCM_AES_XPN_256 : MCS_GCM_AES_256; |
| break; |
| default: |
| cipher = MCS_GCM_AES_128; |
| dev_warn(pfvf->dev, "Unsupported key length\n"); |
| break; |
| } |
| |
| policy |= FIELD_PREP(MCS_RX_SECY_PLCY_CIP, cipher); |
| policy |= FIELD_PREP(MCS_RX_SECY_PLCY_VAL, secy->validate_frames); |
| |
| policy |= MCS_RX_SECY_PLCY_ENA; |
| |
| req->plcy = policy; |
| req->secy_id = hw_secy_id; |
| req->dir = MCS_RX; |
| |
| ret = otx2_sync_mbox_msg(mbox); |
| |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static int cn10k_mcs_write_rx_flowid(struct otx2_nic *pfvf, |
| struct cn10k_mcs_rxsc *rxsc, u8 hw_secy_id) |
| { |
| struct macsec_rx_sc *sw_rx_sc = rxsc->sw_rxsc; |
| struct macsec_secy *secy = rxsc->sw_secy; |
| struct mcs_flowid_entry_write_req *req; |
| struct mbox *mbox = &pfvf->mbox; |
| u64 mac_da; |
| int ret; |
| |
| mutex_lock(&mbox->lock); |
| |
| req = otx2_mbox_alloc_msg_mcs_flowid_entry_write(mbox); |
| if (!req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| mac_da = ether_addr_to_u64(secy->netdev->dev_addr); |
| |
| req->data[0] = FIELD_PREP(MCS_TCAM0_MAC_DA_MASK, mac_da); |
| req->mask[0] = ~0ULL; |
| req->mask[0] = ~MCS_TCAM0_MAC_DA_MASK; |
| |
| req->data[1] = FIELD_PREP(MCS_TCAM1_ETYPE_MASK, ETH_P_MACSEC); |
| req->mask[1] = ~0ULL; |
| req->mask[1] &= ~MCS_TCAM1_ETYPE_MASK; |
| |
| req->mask[2] = ~0ULL; |
| req->mask[3] = ~0ULL; |
| |
| req->flow_id = rxsc->hw_flow_id; |
| req->secy_id = hw_secy_id; |
| req->sc_id = rxsc->hw_sc_id; |
| req->dir = MCS_RX; |
| |
| if (sw_rx_sc->active) |
| req->ena = 1; |
| |
| ret = otx2_sync_mbox_msg(mbox); |
| |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static int cn10k_mcs_write_sc_cam(struct otx2_nic *pfvf, |
| struct cn10k_mcs_rxsc *rxsc, u8 hw_secy_id) |
| { |
| struct macsec_rx_sc *sw_rx_sc = rxsc->sw_rxsc; |
| struct mcs_rx_sc_cam_write_req *sc_req; |
| struct mbox *mbox = &pfvf->mbox; |
| int ret; |
| |
| mutex_lock(&mbox->lock); |
| |
| sc_req = otx2_mbox_alloc_msg_mcs_rx_sc_cam_write(mbox); |
| if (!sc_req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| sc_req->sci = (__force u64)cpu_to_be64((__force u64)sw_rx_sc->sci); |
| sc_req->sc_id = rxsc->hw_sc_id; |
| sc_req->secy_id = hw_secy_id; |
| |
| ret = otx2_sync_mbox_msg(mbox); |
| |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static int cn10k_mcs_write_keys(struct otx2_nic *pfvf, |
| struct macsec_secy *secy, |
| struct mcs_sa_plcy_write_req *req, |
| u8 *sak, u8 *salt, ssci_t ssci) |
| { |
| u8 hash_rev[CN10K_MAX_HASH_LEN]; |
| u8 sak_rev[CN10K_MAX_SAK_LEN]; |
| u8 salt_rev[MACSEC_SALT_LEN]; |
| u8 hash[CN10K_MAX_HASH_LEN]; |
| u32 ssci_63_32; |
| int err, i; |
| |
| err = cn10k_ecb_aes_encrypt(pfvf, sak, secy->key_len, hash); |
| if (err) { |
| dev_err(pfvf->dev, "Generating hash using ECB(AES) failed\n"); |
| return err; |
| } |
| |
| for (i = 0; i < secy->key_len; i++) |
| sak_rev[i] = sak[secy->key_len - 1 - i]; |
| |
| for (i = 0; i < CN10K_MAX_HASH_LEN; i++) |
| hash_rev[i] = hash[CN10K_MAX_HASH_LEN - 1 - i]; |
| |
| for (i = 0; i < MACSEC_SALT_LEN; i++) |
| salt_rev[i] = salt[MACSEC_SALT_LEN - 1 - i]; |
| |
| ssci_63_32 = (__force u32)cpu_to_be32((__force u32)ssci); |
| |
| memcpy(&req->plcy[0][0], sak_rev, secy->key_len); |
| memcpy(&req->plcy[0][4], hash_rev, CN10K_MAX_HASH_LEN); |
| memcpy(&req->plcy[0][6], salt_rev, MACSEC_SALT_LEN); |
| req->plcy[0][7] |= (u64)ssci_63_32 << 32; |
| |
| return 0; |
| } |
| |
| static int cn10k_mcs_write_rx_sa_plcy(struct otx2_nic *pfvf, |
| struct macsec_secy *secy, |
| struct cn10k_mcs_rxsc *rxsc, |
| u8 assoc_num, bool sa_in_use) |
| { |
| struct mcs_sa_plcy_write_req *plcy_req; |
| u8 *sak = rxsc->sa_key[assoc_num]; |
| u8 *salt = rxsc->salt[assoc_num]; |
| struct mcs_rx_sc_sa_map *map_req; |
| struct mbox *mbox = &pfvf->mbox; |
| int ret; |
| |
| mutex_lock(&mbox->lock); |
| |
| plcy_req = otx2_mbox_alloc_msg_mcs_sa_plcy_write(mbox); |
| if (!plcy_req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| map_req = otx2_mbox_alloc_msg_mcs_rx_sc_sa_map_write(mbox); |
| if (!map_req) { |
| otx2_mbox_reset(&mbox->mbox, 0); |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| ret = cn10k_mcs_write_keys(pfvf, secy, plcy_req, sak, |
| salt, rxsc->ssci[assoc_num]); |
| if (ret) |
| goto fail; |
| |
| plcy_req->sa_index[0] = rxsc->hw_sa_id[assoc_num]; |
| plcy_req->sa_cnt = 1; |
| plcy_req->dir = MCS_RX; |
| |
| map_req->sa_index = rxsc->hw_sa_id[assoc_num]; |
| map_req->sa_in_use = sa_in_use; |
| map_req->sc_id = rxsc->hw_sc_id; |
| map_req->an = assoc_num; |
| |
| /* Send two messages together */ |
| ret = otx2_sync_mbox_msg(mbox); |
| |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static int cn10k_mcs_write_rx_sa_pn(struct otx2_nic *pfvf, |
| struct cn10k_mcs_rxsc *rxsc, |
| u8 assoc_num, u64 next_pn) |
| { |
| struct mcs_pn_table_write_req *req; |
| struct mbox *mbox = &pfvf->mbox; |
| int ret; |
| |
| mutex_lock(&mbox->lock); |
| |
| req = otx2_mbox_alloc_msg_mcs_pn_table_write(mbox); |
| if (!req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| req->pn_id = rxsc->hw_sa_id[assoc_num]; |
| req->next_pn = next_pn; |
| req->dir = MCS_RX; |
| |
| ret = otx2_sync_mbox_msg(mbox); |
| |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static int cn10k_mcs_write_tx_secy(struct otx2_nic *pfvf, |
| struct macsec_secy *secy, |
| struct cn10k_mcs_txsc *txsc) |
| { |
| struct mcs_secy_plcy_write_req *req; |
| struct mbox *mbox = &pfvf->mbox; |
| struct macsec_tx_sc *sw_tx_sc; |
| u8 sectag_tci = 0; |
| u8 tag_offset; |
| u64 policy; |
| u8 cipher; |
| int ret; |
| |
| /* Insert SecTag after 12 bytes (DA+SA) or 16 bytes |
| * if VLAN tag needs to be sent in clear text. |
| */ |
| tag_offset = txsc->vlan_dev ? 16 : 12; |
| sw_tx_sc = &secy->tx_sc; |
| |
| mutex_lock(&mbox->lock); |
| |
| req = otx2_mbox_alloc_msg_mcs_secy_plcy_write(mbox); |
| if (!req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| if (sw_tx_sc->send_sci) { |
| sectag_tci |= MCS_TCI_SC; |
| } else { |
| if (sw_tx_sc->end_station) |
| sectag_tci |= MCS_TCI_ES; |
| if (sw_tx_sc->scb) |
| sectag_tci |= MCS_TCI_SCB; |
| } |
| |
| if (sw_tx_sc->encrypt) |
| sectag_tci |= (MCS_TCI_E | MCS_TCI_C); |
| |
| policy = FIELD_PREP(MCS_TX_SECY_PLCY_MTU, secy->netdev->mtu); |
| /* Write SecTag excluding AN bits(1..0) */ |
| policy |= FIELD_PREP(MCS_TX_SECY_PLCY_ST_TCI, sectag_tci >> 2); |
| policy |= FIELD_PREP(MCS_TX_SECY_PLCY_ST_OFFSET, tag_offset); |
| policy |= MCS_TX_SECY_PLCY_INS_MODE; |
| policy |= MCS_TX_SECY_PLCY_AUTH_ENA; |
| |
| switch (secy->key_len) { |
| case 16: |
| cipher = secy->xpn ? MCS_GCM_AES_XPN_128 : MCS_GCM_AES_128; |
| break; |
| case 32: |
| cipher = secy->xpn ? MCS_GCM_AES_XPN_256 : MCS_GCM_AES_256; |
| break; |
| default: |
| cipher = MCS_GCM_AES_128; |
| dev_warn(pfvf->dev, "Unsupported key length\n"); |
| break; |
| } |
| |
| policy |= FIELD_PREP(MCS_TX_SECY_PLCY_CIP, cipher); |
| |
| if (secy->protect_frames) |
| policy |= MCS_TX_SECY_PLCY_PROTECT; |
| |
| /* If the encodingsa does not exist/active and protect is |
| * not set then frames can be sent out as it is. Hence enable |
| * the policy irrespective of secy operational when !protect. |
| */ |
| if (!secy->protect_frames || secy->operational) |
| policy |= MCS_TX_SECY_PLCY_ENA; |
| |
| req->plcy = policy; |
| req->secy_id = txsc->hw_secy_id_tx; |
| req->dir = MCS_TX; |
| |
| ret = otx2_sync_mbox_msg(mbox); |
| |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static int cn10k_mcs_write_tx_flowid(struct otx2_nic *pfvf, |
| struct macsec_secy *secy, |
| struct cn10k_mcs_txsc *txsc) |
| { |
| struct mcs_flowid_entry_write_req *req; |
| struct mbox *mbox = &pfvf->mbox; |
| u64 mac_sa; |
| int ret; |
| |
| mutex_lock(&mbox->lock); |
| |
| req = otx2_mbox_alloc_msg_mcs_flowid_entry_write(mbox); |
| if (!req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| mac_sa = ether_addr_to_u64(secy->netdev->dev_addr); |
| |
| req->data[0] = FIELD_PREP(MCS_TCAM0_MAC_SA_MASK, mac_sa); |
| req->data[1] = FIELD_PREP(MCS_TCAM1_MAC_SA_MASK, mac_sa >> 16); |
| |
| req->mask[0] = ~0ULL; |
| req->mask[0] &= ~MCS_TCAM0_MAC_SA_MASK; |
| |
| req->mask[1] = ~0ULL; |
| req->mask[1] &= ~MCS_TCAM1_MAC_SA_MASK; |
| |
| req->mask[2] = ~0ULL; |
| req->mask[3] = ~0ULL; |
| |
| req->flow_id = txsc->hw_flow_id; |
| req->secy_id = txsc->hw_secy_id_tx; |
| req->sc_id = txsc->hw_sc_id; |
| req->sci = (__force u64)cpu_to_be64((__force u64)secy->sci); |
| req->dir = MCS_TX; |
| /* This can be enabled since stack xmits packets only when interface is up */ |
| req->ena = 1; |
| |
| ret = otx2_sync_mbox_msg(mbox); |
| |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static int cn10k_mcs_link_tx_sa2sc(struct otx2_nic *pfvf, |
| struct macsec_secy *secy, |
| struct cn10k_mcs_txsc *txsc, |
| u8 sa_num, bool sa_active) |
| { |
| struct mcs_tx_sc_sa_map *map_req; |
| struct mbox *mbox = &pfvf->mbox; |
| int ret; |
| |
| /* Link the encoding_sa only to SC out of all SAs */ |
| if (txsc->encoding_sa != sa_num) |
| return 0; |
| |
| mutex_lock(&mbox->lock); |
| |
| map_req = otx2_mbox_alloc_msg_mcs_tx_sc_sa_map_write(mbox); |
| if (!map_req) { |
| otx2_mbox_reset(&mbox->mbox, 0); |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| map_req->sa_index0 = txsc->hw_sa_id[sa_num]; |
| map_req->sa_index0_vld = sa_active; |
| map_req->sectag_sci = (__force u64)cpu_to_be64((__force u64)secy->sci); |
| map_req->sc_id = txsc->hw_sc_id; |
| |
| ret = otx2_sync_mbox_msg(mbox); |
| |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static int cn10k_mcs_write_tx_sa_plcy(struct otx2_nic *pfvf, |
| struct macsec_secy *secy, |
| struct cn10k_mcs_txsc *txsc, |
| u8 assoc_num) |
| { |
| struct mcs_sa_plcy_write_req *plcy_req; |
| u8 *sak = txsc->sa_key[assoc_num]; |
| u8 *salt = txsc->salt[assoc_num]; |
| struct mbox *mbox = &pfvf->mbox; |
| int ret; |
| |
| mutex_lock(&mbox->lock); |
| |
| plcy_req = otx2_mbox_alloc_msg_mcs_sa_plcy_write(mbox); |
| if (!plcy_req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| ret = cn10k_mcs_write_keys(pfvf, secy, plcy_req, sak, |
| salt, txsc->ssci[assoc_num]); |
| if (ret) |
| goto fail; |
| |
| plcy_req->plcy[0][8] = assoc_num; |
| plcy_req->sa_index[0] = txsc->hw_sa_id[assoc_num]; |
| plcy_req->sa_cnt = 1; |
| plcy_req->dir = MCS_TX; |
| |
| ret = otx2_sync_mbox_msg(mbox); |
| |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static int cn10k_write_tx_sa_pn(struct otx2_nic *pfvf, |
| struct cn10k_mcs_txsc *txsc, |
| u8 assoc_num, u64 next_pn) |
| { |
| struct mcs_pn_table_write_req *req; |
| struct mbox *mbox = &pfvf->mbox; |
| int ret; |
| |
| mutex_lock(&mbox->lock); |
| |
| req = otx2_mbox_alloc_msg_mcs_pn_table_write(mbox); |
| if (!req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| req->pn_id = txsc->hw_sa_id[assoc_num]; |
| req->next_pn = next_pn; |
| req->dir = MCS_TX; |
| |
| ret = otx2_sync_mbox_msg(mbox); |
| |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static int cn10k_mcs_ena_dis_flowid(struct otx2_nic *pfvf, u16 hw_flow_id, |
| bool enable, enum mcs_direction dir) |
| { |
| struct mcs_flowid_ena_dis_entry *req; |
| struct mbox *mbox = &pfvf->mbox; |
| int ret; |
| |
| mutex_lock(&mbox->lock); |
| |
| req = otx2_mbox_alloc_msg_mcs_flowid_ena_entry(mbox); |
| if (!req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| req->flow_id = hw_flow_id; |
| req->ena = enable; |
| req->dir = dir; |
| |
| ret = otx2_sync_mbox_msg(mbox); |
| |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static int cn10k_mcs_sa_stats(struct otx2_nic *pfvf, u8 hw_sa_id, |
| struct mcs_sa_stats *rsp_p, |
| enum mcs_direction dir, bool clear) |
| { |
| struct mcs_clear_stats *clear_req; |
| struct mbox *mbox = &pfvf->mbox; |
| struct mcs_stats_req *req; |
| struct mcs_sa_stats *rsp; |
| int ret; |
| |
| mutex_lock(&mbox->lock); |
| |
| req = otx2_mbox_alloc_msg_mcs_get_sa_stats(mbox); |
| if (!req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| req->id = hw_sa_id; |
| req->dir = dir; |
| |
| if (!clear) |
| goto send_msg; |
| |
| clear_req = otx2_mbox_alloc_msg_mcs_clear_stats(mbox); |
| if (!clear_req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| clear_req->id = hw_sa_id; |
| clear_req->dir = dir; |
| clear_req->type = MCS_RSRC_TYPE_SA; |
| |
| send_msg: |
| ret = otx2_sync_mbox_msg(mbox); |
| if (ret) |
| goto fail; |
| |
| rsp = (struct mcs_sa_stats *)otx2_mbox_get_rsp(&pfvf->mbox.mbox, |
| 0, &req->hdr); |
| if (IS_ERR(rsp)) { |
| ret = PTR_ERR(rsp); |
| goto fail; |
| } |
| |
| memcpy(rsp_p, rsp, sizeof(*rsp_p)); |
| |
| mutex_unlock(&mbox->lock); |
| |
| return 0; |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static int cn10k_mcs_sc_stats(struct otx2_nic *pfvf, u8 hw_sc_id, |
| struct mcs_sc_stats *rsp_p, |
| enum mcs_direction dir, bool clear) |
| { |
| struct mcs_clear_stats *clear_req; |
| struct mbox *mbox = &pfvf->mbox; |
| struct mcs_stats_req *req; |
| struct mcs_sc_stats *rsp; |
| int ret; |
| |
| mutex_lock(&mbox->lock); |
| |
| req = otx2_mbox_alloc_msg_mcs_get_sc_stats(mbox); |
| if (!req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| req->id = hw_sc_id; |
| req->dir = dir; |
| |
| if (!clear) |
| goto send_msg; |
| |
| clear_req = otx2_mbox_alloc_msg_mcs_clear_stats(mbox); |
| if (!clear_req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| clear_req->id = hw_sc_id; |
| clear_req->dir = dir; |
| clear_req->type = MCS_RSRC_TYPE_SC; |
| |
| send_msg: |
| ret = otx2_sync_mbox_msg(mbox); |
| if (ret) |
| goto fail; |
| |
| rsp = (struct mcs_sc_stats *)otx2_mbox_get_rsp(&pfvf->mbox.mbox, |
| 0, &req->hdr); |
| if (IS_ERR(rsp)) { |
| ret = PTR_ERR(rsp); |
| goto fail; |
| } |
| |
| memcpy(rsp_p, rsp, sizeof(*rsp_p)); |
| |
| mutex_unlock(&mbox->lock); |
| |
| return 0; |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static int cn10k_mcs_secy_stats(struct otx2_nic *pfvf, u8 hw_secy_id, |
| struct mcs_secy_stats *rsp_p, |
| enum mcs_direction dir, bool clear) |
| { |
| struct mcs_clear_stats *clear_req; |
| struct mbox *mbox = &pfvf->mbox; |
| struct mcs_secy_stats *rsp; |
| struct mcs_stats_req *req; |
| int ret; |
| |
| mutex_lock(&mbox->lock); |
| |
| req = otx2_mbox_alloc_msg_mcs_get_secy_stats(mbox); |
| if (!req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| |
| req->id = hw_secy_id; |
| req->dir = dir; |
| |
| if (!clear) |
| goto send_msg; |
| |
| clear_req = otx2_mbox_alloc_msg_mcs_clear_stats(mbox); |
| if (!clear_req) { |
| ret = -ENOMEM; |
| goto fail; |
| } |
| clear_req->id = hw_secy_id; |
| clear_req->dir = dir; |
| clear_req->type = MCS_RSRC_TYPE_SECY; |
| |
| send_msg: |
| ret = otx2_sync_mbox_msg(mbox); |
| if (ret) |
| goto fail; |
| |
| rsp = (struct mcs_secy_stats *)otx2_mbox_get_rsp(&pfvf->mbox.mbox, |
| 0, &req->hdr); |
| if (IS_ERR(rsp)) { |
| ret = PTR_ERR(rsp); |
| goto fail; |
| } |
| |
| memcpy(rsp_p, rsp, sizeof(*rsp_p)); |
| |
| mutex_unlock(&mbox->lock); |
| |
| return 0; |
| fail: |
| mutex_unlock(&mbox->lock); |
| return ret; |
| } |
| |
| static struct cn10k_mcs_txsc *cn10k_mcs_create_txsc(struct otx2_nic *pfvf) |
| { |
| struct cn10k_mcs_txsc *txsc; |
| int ret; |
| |
| txsc = kzalloc(sizeof(*txsc), GFP_KERNEL); |
| if (!txsc) |
| return ERR_PTR(-ENOMEM); |
| |
| ret = cn10k_mcs_alloc_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_FLOWID, |
| &txsc->hw_flow_id); |
| if (ret) |
| goto fail; |
| |
| /* For a SecY, one TX secy and one RX secy HW resources are needed */ |
| ret = cn10k_mcs_alloc_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, |
| &txsc->hw_secy_id_tx); |
| if (ret) |
| goto free_flowid; |
| |
| ret = cn10k_mcs_alloc_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, |
| &txsc->hw_secy_id_rx); |
| if (ret) |
| goto free_tx_secy; |
| |
| ret = cn10k_mcs_alloc_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SC, |
| &txsc->hw_sc_id); |
| if (ret) |
| goto free_rx_secy; |
| |
| return txsc; |
| free_rx_secy: |
| cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, |
| txsc->hw_secy_id_rx, false); |
| free_tx_secy: |
| cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, |
| txsc->hw_secy_id_tx, false); |
| free_flowid: |
| cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_FLOWID, |
| txsc->hw_flow_id, false); |
| fail: |
| kfree(txsc); |
| return ERR_PTR(ret); |
| } |
| |
| /* Free Tx SC and its SAs(if any) resources to AF |
| */ |
| static void cn10k_mcs_delete_txsc(struct otx2_nic *pfvf, |
| struct cn10k_mcs_txsc *txsc) |
| { |
| u8 sa_bmap = txsc->sa_bmap; |
| u8 sa_num = 0; |
| |
| while (sa_bmap) { |
| if (sa_bmap & 1) { |
| cn10k_mcs_write_tx_sa_plcy(pfvf, txsc->sw_secy, |
| txsc, sa_num); |
| cn10k_mcs_free_txsa(pfvf, txsc->hw_sa_id[sa_num]); |
| } |
| sa_num++; |
| sa_bmap >>= 1; |
| } |
| |
| cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SC, |
| txsc->hw_sc_id, false); |
| cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, |
| txsc->hw_secy_id_rx, false); |
| cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, |
| txsc->hw_secy_id_tx, false); |
| cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_FLOWID, |
| txsc->hw_flow_id, false); |
| } |
| |
| static struct cn10k_mcs_rxsc *cn10k_mcs_create_rxsc(struct otx2_nic *pfvf) |
| { |
| struct cn10k_mcs_rxsc *rxsc; |
| int ret; |
| |
| rxsc = kzalloc(sizeof(*rxsc), GFP_KERNEL); |
| if (!rxsc) |
| return ERR_PTR(-ENOMEM); |
| |
| ret = cn10k_mcs_alloc_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_FLOWID, |
| &rxsc->hw_flow_id); |
| if (ret) |
| goto fail; |
| |
| ret = cn10k_mcs_alloc_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SC, |
| &rxsc->hw_sc_id); |
| if (ret) |
| goto free_flowid; |
| |
| return rxsc; |
| free_flowid: |
| cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_FLOWID, |
| rxsc->hw_flow_id, false); |
| fail: |
| kfree(rxsc); |
| return ERR_PTR(ret); |
| } |
| |
| /* Free Rx SC and its SAs(if any) resources to AF |
| */ |
| static void cn10k_mcs_delete_rxsc(struct otx2_nic *pfvf, |
| struct cn10k_mcs_rxsc *rxsc) |
| { |
| u8 sa_bmap = rxsc->sa_bmap; |
| u8 sa_num = 0; |
| |
| while (sa_bmap) { |
| if (sa_bmap & 1) { |
| cn10k_mcs_write_rx_sa_plcy(pfvf, rxsc->sw_secy, rxsc, |
| sa_num, false); |
| cn10k_mcs_free_rxsa(pfvf, rxsc->hw_sa_id[sa_num]); |
| } |
| sa_num++; |
| sa_bmap >>= 1; |
| } |
| |
| cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SC, |
| rxsc->hw_sc_id, false); |
| cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_FLOWID, |
| rxsc->hw_flow_id, false); |
| } |
| |
| static int cn10k_mcs_secy_tx_cfg(struct otx2_nic *pfvf, struct macsec_secy *secy, |
| struct cn10k_mcs_txsc *txsc, |
| struct macsec_tx_sa *sw_tx_sa, u8 sa_num) |
| { |
| if (sw_tx_sa) { |
| cn10k_mcs_write_tx_sa_plcy(pfvf, secy, txsc, sa_num); |
| cn10k_write_tx_sa_pn(pfvf, txsc, sa_num, sw_tx_sa->next_pn); |
| cn10k_mcs_link_tx_sa2sc(pfvf, secy, txsc, sa_num, |
| sw_tx_sa->active); |
| } |
| |
| cn10k_mcs_write_tx_secy(pfvf, secy, txsc); |
| cn10k_mcs_write_tx_flowid(pfvf, secy, txsc); |
| /* When updating secy, change RX secy also */ |
| cn10k_mcs_write_rx_secy(pfvf, secy, txsc->hw_secy_id_rx); |
| |
| return 0; |
| } |
| |
| static int cn10k_mcs_secy_rx_cfg(struct otx2_nic *pfvf, |
| struct macsec_secy *secy, u8 hw_secy_id) |
| { |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct cn10k_mcs_rxsc *mcs_rx_sc; |
| struct macsec_rx_sc *sw_rx_sc; |
| struct macsec_rx_sa *sw_rx_sa; |
| u8 sa_num; |
| |
| for (sw_rx_sc = rcu_dereference_bh(secy->rx_sc); sw_rx_sc && sw_rx_sc->active; |
| sw_rx_sc = rcu_dereference_bh(sw_rx_sc->next)) { |
| mcs_rx_sc = cn10k_mcs_get_rxsc(cfg, secy, sw_rx_sc); |
| if (unlikely(!mcs_rx_sc)) |
| continue; |
| |
| for (sa_num = 0; sa_num < CN10K_MCS_SA_PER_SC; sa_num++) { |
| sw_rx_sa = rcu_dereference_bh(sw_rx_sc->sa[sa_num]); |
| if (!sw_rx_sa) |
| continue; |
| |
| cn10k_mcs_write_rx_sa_plcy(pfvf, secy, mcs_rx_sc, |
| sa_num, sw_rx_sa->active); |
| cn10k_mcs_write_rx_sa_pn(pfvf, mcs_rx_sc, sa_num, |
| sw_rx_sa->next_pn); |
| } |
| |
| cn10k_mcs_write_rx_flowid(pfvf, mcs_rx_sc, hw_secy_id); |
| cn10k_mcs_write_sc_cam(pfvf, mcs_rx_sc, hw_secy_id); |
| } |
| |
| return 0; |
| } |
| |
| static int cn10k_mcs_disable_rxscs(struct otx2_nic *pfvf, |
| struct macsec_secy *secy, |
| bool delete) |
| { |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct cn10k_mcs_rxsc *mcs_rx_sc; |
| struct macsec_rx_sc *sw_rx_sc; |
| int ret; |
| |
| for (sw_rx_sc = rcu_dereference_bh(secy->rx_sc); sw_rx_sc && sw_rx_sc->active; |
| sw_rx_sc = rcu_dereference_bh(sw_rx_sc->next)) { |
| mcs_rx_sc = cn10k_mcs_get_rxsc(cfg, secy, sw_rx_sc); |
| if (unlikely(!mcs_rx_sc)) |
| continue; |
| |
| ret = cn10k_mcs_ena_dis_flowid(pfvf, mcs_rx_sc->hw_flow_id, |
| false, MCS_RX); |
| if (ret) |
| dev_err(pfvf->dev, "Failed to disable TCAM for SC %d\n", |
| mcs_rx_sc->hw_sc_id); |
| if (delete) { |
| cn10k_mcs_delete_rxsc(pfvf, mcs_rx_sc); |
| list_del(&mcs_rx_sc->entry); |
| kfree(mcs_rx_sc); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void cn10k_mcs_sync_stats(struct otx2_nic *pfvf, struct macsec_secy *secy, |
| struct cn10k_mcs_txsc *txsc) |
| { |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct mcs_secy_stats rx_rsp = { 0 }; |
| struct mcs_sc_stats sc_rsp = { 0 }; |
| struct cn10k_mcs_rxsc *rxsc; |
| |
| /* Because of shared counters for some stats in the hardware, when |
| * updating secy policy take a snapshot of current stats and reset them. |
| * Below are the effected stats because of shared counters. |
| */ |
| |
| /* Check if sync is really needed */ |
| if (secy->validate_frames == txsc->last_validate_frames && |
| secy->replay_protect == txsc->last_replay_protect) |
| return; |
| |
| cn10k_mcs_secy_stats(pfvf, txsc->hw_secy_id_rx, &rx_rsp, MCS_RX, true); |
| |
| txsc->stats.InPktsBadTag += rx_rsp.pkt_badtag_cnt; |
| txsc->stats.InPktsUnknownSCI += rx_rsp.pkt_nosa_cnt; |
| txsc->stats.InPktsNoSCI += rx_rsp.pkt_nosaerror_cnt; |
| if (txsc->last_validate_frames == MACSEC_VALIDATE_STRICT) |
| txsc->stats.InPktsNoTag += rx_rsp.pkt_untaged_cnt; |
| else |
| txsc->stats.InPktsUntagged += rx_rsp.pkt_untaged_cnt; |
| |
| list_for_each_entry(rxsc, &cfg->rxsc_list, entry) { |
| cn10k_mcs_sc_stats(pfvf, rxsc->hw_sc_id, &sc_rsp, MCS_RX, true); |
| |
| rxsc->stats.InOctetsValidated += sc_rsp.octet_validate_cnt; |
| rxsc->stats.InOctetsDecrypted += sc_rsp.octet_decrypt_cnt; |
| |
| rxsc->stats.InPktsInvalid += sc_rsp.pkt_invalid_cnt; |
| rxsc->stats.InPktsNotValid += sc_rsp.pkt_notvalid_cnt; |
| |
| if (txsc->last_replay_protect) |
| rxsc->stats.InPktsLate += sc_rsp.pkt_late_cnt; |
| else |
| rxsc->stats.InPktsDelayed += sc_rsp.pkt_late_cnt; |
| |
| if (txsc->last_validate_frames == MACSEC_VALIDATE_DISABLED) |
| rxsc->stats.InPktsUnchecked += sc_rsp.pkt_unchecked_cnt; |
| else |
| rxsc->stats.InPktsOK += sc_rsp.pkt_unchecked_cnt; |
| } |
| |
| txsc->last_validate_frames = secy->validate_frames; |
| txsc->last_replay_protect = secy->replay_protect; |
| } |
| |
| static int cn10k_mdo_open(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct macsec_secy *secy = ctx->secy; |
| struct macsec_tx_sa *sw_tx_sa; |
| struct cn10k_mcs_txsc *txsc; |
| u8 sa_num; |
| int err; |
| |
| txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); |
| if (!txsc) |
| return -ENOENT; |
| |
| sa_num = txsc->encoding_sa; |
| sw_tx_sa = rcu_dereference_bh(secy->tx_sc.sa[sa_num]); |
| |
| err = cn10k_mcs_secy_tx_cfg(pfvf, secy, txsc, sw_tx_sa, sa_num); |
| if (err) |
| return err; |
| |
| return cn10k_mcs_secy_rx_cfg(pfvf, secy, txsc->hw_secy_id_rx); |
| } |
| |
| static int cn10k_mdo_stop(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct cn10k_mcs_txsc *txsc; |
| int err; |
| |
| txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); |
| if (!txsc) |
| return -ENOENT; |
| |
| err = cn10k_mcs_ena_dis_flowid(pfvf, txsc->hw_flow_id, false, MCS_TX); |
| if (err) |
| return err; |
| |
| return cn10k_mcs_disable_rxscs(pfvf, ctx->secy, false); |
| } |
| |
| static int cn10k_mdo_add_secy(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct macsec_secy *secy = ctx->secy; |
| struct cn10k_mcs_txsc *txsc; |
| |
| if (secy->icv_len != MACSEC_DEFAULT_ICV_LEN) |
| return -EOPNOTSUPP; |
| |
| txsc = cn10k_mcs_create_txsc(pfvf); |
| if (IS_ERR(txsc)) |
| return -ENOSPC; |
| |
| txsc->sw_secy = secy; |
| txsc->encoding_sa = secy->tx_sc.encoding_sa; |
| txsc->last_validate_frames = secy->validate_frames; |
| txsc->last_replay_protect = secy->replay_protect; |
| txsc->vlan_dev = is_vlan_dev(ctx->netdev); |
| |
| list_add(&txsc->entry, &cfg->txsc_list); |
| |
| if (netif_running(secy->netdev)) |
| return cn10k_mcs_secy_tx_cfg(pfvf, secy, txsc, NULL, 0); |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_upd_secy(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct macsec_secy *secy = ctx->secy; |
| struct macsec_tx_sa *sw_tx_sa; |
| struct cn10k_mcs_txsc *txsc; |
| bool active; |
| u8 sa_num; |
| int err; |
| |
| txsc = cn10k_mcs_get_txsc(cfg, secy); |
| if (!txsc) |
| return -ENOENT; |
| |
| /* Encoding SA got changed */ |
| if (txsc->encoding_sa != secy->tx_sc.encoding_sa) { |
| txsc->encoding_sa = secy->tx_sc.encoding_sa; |
| sa_num = txsc->encoding_sa; |
| sw_tx_sa = rcu_dereference_bh(secy->tx_sc.sa[sa_num]); |
| active = sw_tx_sa ? sw_tx_sa->active : false; |
| cn10k_mcs_link_tx_sa2sc(pfvf, secy, txsc, sa_num, active); |
| } |
| |
| if (netif_running(secy->netdev)) { |
| cn10k_mcs_sync_stats(pfvf, secy, txsc); |
| |
| err = cn10k_mcs_secy_tx_cfg(pfvf, secy, txsc, NULL, 0); |
| if (err) |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_del_secy(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct cn10k_mcs_txsc *txsc; |
| |
| txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); |
| if (!txsc) |
| return -ENOENT; |
| |
| cn10k_mcs_ena_dis_flowid(pfvf, txsc->hw_flow_id, false, MCS_TX); |
| cn10k_mcs_disable_rxscs(pfvf, ctx->secy, true); |
| cn10k_mcs_delete_txsc(pfvf, txsc); |
| list_del(&txsc->entry); |
| kfree(txsc); |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_add_txsa(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct macsec_tx_sa *sw_tx_sa = ctx->sa.tx_sa; |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct macsec_secy *secy = ctx->secy; |
| u8 sa_num = ctx->sa.assoc_num; |
| struct cn10k_mcs_txsc *txsc; |
| int err; |
| |
| txsc = cn10k_mcs_get_txsc(cfg, secy); |
| if (!txsc) |
| return -ENOENT; |
| |
| if (sa_num >= CN10K_MCS_SA_PER_SC) |
| return -EOPNOTSUPP; |
| |
| if (cn10k_mcs_alloc_txsa(pfvf, &txsc->hw_sa_id[sa_num])) |
| return -ENOSPC; |
| |
| memcpy(&txsc->sa_key[sa_num], ctx->sa.key, secy->key_len); |
| memcpy(&txsc->salt[sa_num], sw_tx_sa->key.salt.bytes, MACSEC_SALT_LEN); |
| txsc->ssci[sa_num] = sw_tx_sa->ssci; |
| |
| txsc->sa_bmap |= 1 << sa_num; |
| |
| if (netif_running(secy->netdev)) { |
| err = cn10k_mcs_write_tx_sa_plcy(pfvf, secy, txsc, sa_num); |
| if (err) |
| return err; |
| |
| err = cn10k_write_tx_sa_pn(pfvf, txsc, sa_num, |
| sw_tx_sa->next_pn); |
| if (err) |
| return err; |
| |
| err = cn10k_mcs_link_tx_sa2sc(pfvf, secy, txsc, |
| sa_num, sw_tx_sa->active); |
| if (err) |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_upd_txsa(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct macsec_tx_sa *sw_tx_sa = ctx->sa.tx_sa; |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct macsec_secy *secy = ctx->secy; |
| u8 sa_num = ctx->sa.assoc_num; |
| struct cn10k_mcs_txsc *txsc; |
| int err; |
| |
| txsc = cn10k_mcs_get_txsc(cfg, secy); |
| if (!txsc) |
| return -ENOENT; |
| |
| if (sa_num >= CN10K_MCS_SA_PER_SC) |
| return -EOPNOTSUPP; |
| |
| if (netif_running(secy->netdev)) { |
| /* Keys cannot be changed after creation */ |
| if (ctx->sa.update_pn) { |
| err = cn10k_write_tx_sa_pn(pfvf, txsc, sa_num, |
| sw_tx_sa->next_pn); |
| if (err) |
| return err; |
| } |
| |
| err = cn10k_mcs_link_tx_sa2sc(pfvf, secy, txsc, |
| sa_num, sw_tx_sa->active); |
| if (err) |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_del_txsa(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| u8 sa_num = ctx->sa.assoc_num; |
| struct cn10k_mcs_txsc *txsc; |
| |
| txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); |
| if (!txsc) |
| return -ENOENT; |
| |
| if (sa_num >= CN10K_MCS_SA_PER_SC) |
| return -EOPNOTSUPP; |
| |
| cn10k_mcs_free_txsa(pfvf, txsc->hw_sa_id[sa_num]); |
| txsc->sa_bmap &= ~(1 << sa_num); |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_add_rxsc(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct macsec_secy *secy = ctx->secy; |
| struct cn10k_mcs_rxsc *rxsc; |
| struct cn10k_mcs_txsc *txsc; |
| int err; |
| |
| txsc = cn10k_mcs_get_txsc(cfg, secy); |
| if (!txsc) |
| return -ENOENT; |
| |
| rxsc = cn10k_mcs_create_rxsc(pfvf); |
| if (IS_ERR(rxsc)) |
| return -ENOSPC; |
| |
| rxsc->sw_secy = ctx->secy; |
| rxsc->sw_rxsc = ctx->rx_sc; |
| list_add(&rxsc->entry, &cfg->rxsc_list); |
| |
| if (netif_running(secy->netdev)) { |
| err = cn10k_mcs_write_rx_flowid(pfvf, rxsc, txsc->hw_secy_id_rx); |
| if (err) |
| return err; |
| |
| err = cn10k_mcs_write_sc_cam(pfvf, rxsc, txsc->hw_secy_id_rx); |
| if (err) |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_upd_rxsc(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct macsec_secy *secy = ctx->secy; |
| bool enable = ctx->rx_sc->active; |
| struct cn10k_mcs_rxsc *rxsc; |
| |
| rxsc = cn10k_mcs_get_rxsc(cfg, secy, ctx->rx_sc); |
| if (!rxsc) |
| return -ENOENT; |
| |
| if (netif_running(secy->netdev)) |
| return cn10k_mcs_ena_dis_flowid(pfvf, rxsc->hw_flow_id, |
| enable, MCS_RX); |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_del_rxsc(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct cn10k_mcs_rxsc *rxsc; |
| |
| rxsc = cn10k_mcs_get_rxsc(cfg, ctx->secy, ctx->rx_sc); |
| if (!rxsc) |
| return -ENOENT; |
| |
| cn10k_mcs_ena_dis_flowid(pfvf, rxsc->hw_flow_id, false, MCS_RX); |
| cn10k_mcs_delete_rxsc(pfvf, rxsc); |
| list_del(&rxsc->entry); |
| kfree(rxsc); |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_add_rxsa(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct macsec_rx_sc *sw_rx_sc = ctx->sa.rx_sa->sc; |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct macsec_rx_sa *rx_sa = ctx->sa.rx_sa; |
| struct macsec_secy *secy = ctx->secy; |
| bool sa_in_use = rx_sa->active; |
| u8 sa_num = ctx->sa.assoc_num; |
| struct cn10k_mcs_rxsc *rxsc; |
| int err; |
| |
| rxsc = cn10k_mcs_get_rxsc(cfg, secy, sw_rx_sc); |
| if (!rxsc) |
| return -ENOENT; |
| |
| if (sa_num >= CN10K_MCS_SA_PER_SC) |
| return -EOPNOTSUPP; |
| |
| if (cn10k_mcs_alloc_rxsa(pfvf, &rxsc->hw_sa_id[sa_num])) |
| return -ENOSPC; |
| |
| memcpy(&rxsc->sa_key[sa_num], ctx->sa.key, ctx->secy->key_len); |
| memcpy(&rxsc->salt[sa_num], rx_sa->key.salt.bytes, MACSEC_SALT_LEN); |
| rxsc->ssci[sa_num] = rx_sa->ssci; |
| |
| rxsc->sa_bmap |= 1 << sa_num; |
| |
| if (netif_running(secy->netdev)) { |
| err = cn10k_mcs_write_rx_sa_plcy(pfvf, secy, rxsc, |
| sa_num, sa_in_use); |
| if (err) |
| return err; |
| |
| err = cn10k_mcs_write_rx_sa_pn(pfvf, rxsc, sa_num, |
| rx_sa->next_pn); |
| if (err) |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_upd_rxsa(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct macsec_rx_sc *sw_rx_sc = ctx->sa.rx_sa->sc; |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct macsec_rx_sa *rx_sa = ctx->sa.rx_sa; |
| struct macsec_secy *secy = ctx->secy; |
| bool sa_in_use = rx_sa->active; |
| u8 sa_num = ctx->sa.assoc_num; |
| struct cn10k_mcs_rxsc *rxsc; |
| int err; |
| |
| rxsc = cn10k_mcs_get_rxsc(cfg, secy, sw_rx_sc); |
| if (!rxsc) |
| return -ENOENT; |
| |
| if (sa_num >= CN10K_MCS_SA_PER_SC) |
| return -EOPNOTSUPP; |
| |
| if (netif_running(secy->netdev)) { |
| err = cn10k_mcs_write_rx_sa_plcy(pfvf, secy, rxsc, sa_num, sa_in_use); |
| if (err) |
| return err; |
| |
| if (!ctx->sa.update_pn) |
| return 0; |
| |
| err = cn10k_mcs_write_rx_sa_pn(pfvf, rxsc, sa_num, |
| rx_sa->next_pn); |
| if (err) |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_del_rxsa(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct macsec_rx_sc *sw_rx_sc = ctx->sa.rx_sa->sc; |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| u8 sa_num = ctx->sa.assoc_num; |
| struct cn10k_mcs_rxsc *rxsc; |
| |
| rxsc = cn10k_mcs_get_rxsc(cfg, ctx->secy, sw_rx_sc); |
| if (!rxsc) |
| return -ENOENT; |
| |
| if (sa_num >= CN10K_MCS_SA_PER_SC) |
| return -EOPNOTSUPP; |
| |
| cn10k_mcs_write_rx_sa_plcy(pfvf, ctx->secy, rxsc, sa_num, false); |
| cn10k_mcs_free_rxsa(pfvf, rxsc->hw_sa_id[sa_num]); |
| |
| rxsc->sa_bmap &= ~(1 << sa_num); |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_get_dev_stats(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct mcs_secy_stats tx_rsp = { 0 }, rx_rsp = { 0 }; |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct macsec_secy *secy = ctx->secy; |
| struct cn10k_mcs_txsc *txsc; |
| |
| txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); |
| if (!txsc) |
| return -ENOENT; |
| |
| cn10k_mcs_secy_stats(pfvf, txsc->hw_secy_id_tx, &tx_rsp, MCS_TX, false); |
| ctx->stats.dev_stats->OutPktsUntagged = tx_rsp.pkt_untagged_cnt; |
| ctx->stats.dev_stats->OutPktsTooLong = tx_rsp.pkt_toolong_cnt; |
| |
| cn10k_mcs_secy_stats(pfvf, txsc->hw_secy_id_rx, &rx_rsp, MCS_RX, true); |
| txsc->stats.InPktsBadTag += rx_rsp.pkt_badtag_cnt; |
| txsc->stats.InPktsUnknownSCI += rx_rsp.pkt_nosa_cnt; |
| txsc->stats.InPktsNoSCI += rx_rsp.pkt_nosaerror_cnt; |
| if (secy->validate_frames == MACSEC_VALIDATE_STRICT) |
| txsc->stats.InPktsNoTag += rx_rsp.pkt_untaged_cnt; |
| else |
| txsc->stats.InPktsUntagged += rx_rsp.pkt_untaged_cnt; |
| txsc->stats.InPktsOverrun = 0; |
| |
| ctx->stats.dev_stats->InPktsNoTag = txsc->stats.InPktsNoTag; |
| ctx->stats.dev_stats->InPktsUntagged = txsc->stats.InPktsUntagged; |
| ctx->stats.dev_stats->InPktsBadTag = txsc->stats.InPktsBadTag; |
| ctx->stats.dev_stats->InPktsUnknownSCI = txsc->stats.InPktsUnknownSCI; |
| ctx->stats.dev_stats->InPktsNoSCI = txsc->stats.InPktsNoSCI; |
| ctx->stats.dev_stats->InPktsOverrun = txsc->stats.InPktsOverrun; |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_get_tx_sc_stats(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct mcs_sc_stats rsp = { 0 }; |
| struct cn10k_mcs_txsc *txsc; |
| |
| txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); |
| if (!txsc) |
| return -ENOENT; |
| |
| cn10k_mcs_sc_stats(pfvf, txsc->hw_sc_id, &rsp, MCS_TX, false); |
| |
| ctx->stats.tx_sc_stats->OutPktsProtected = rsp.pkt_protected_cnt; |
| ctx->stats.tx_sc_stats->OutPktsEncrypted = rsp.pkt_encrypt_cnt; |
| ctx->stats.tx_sc_stats->OutOctetsProtected = rsp.octet_protected_cnt; |
| ctx->stats.tx_sc_stats->OutOctetsEncrypted = rsp.octet_encrypt_cnt; |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_get_tx_sa_stats(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct mcs_sa_stats rsp = { 0 }; |
| u8 sa_num = ctx->sa.assoc_num; |
| struct cn10k_mcs_txsc *txsc; |
| |
| txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); |
| if (!txsc) |
| return -ENOENT; |
| |
| if (sa_num >= CN10K_MCS_SA_PER_SC) |
| return -EOPNOTSUPP; |
| |
| cn10k_mcs_sa_stats(pfvf, txsc->hw_sa_id[sa_num], &rsp, MCS_TX, false); |
| |
| ctx->stats.tx_sa_stats->OutPktsProtected = rsp.pkt_protected_cnt; |
| ctx->stats.tx_sa_stats->OutPktsEncrypted = rsp.pkt_encrypt_cnt; |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_get_rx_sc_stats(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct macsec_secy *secy = ctx->secy; |
| struct mcs_sc_stats rsp = { 0 }; |
| struct cn10k_mcs_rxsc *rxsc; |
| |
| rxsc = cn10k_mcs_get_rxsc(cfg, secy, ctx->rx_sc); |
| if (!rxsc) |
| return -ENOENT; |
| |
| cn10k_mcs_sc_stats(pfvf, rxsc->hw_sc_id, &rsp, MCS_RX, true); |
| |
| rxsc->stats.InOctetsValidated += rsp.octet_validate_cnt; |
| rxsc->stats.InOctetsDecrypted += rsp.octet_decrypt_cnt; |
| |
| rxsc->stats.InPktsInvalid += rsp.pkt_invalid_cnt; |
| rxsc->stats.InPktsNotValid += rsp.pkt_notvalid_cnt; |
| |
| if (secy->replay_protect) |
| rxsc->stats.InPktsLate += rsp.pkt_late_cnt; |
| else |
| rxsc->stats.InPktsDelayed += rsp.pkt_late_cnt; |
| |
| if (secy->validate_frames == MACSEC_VALIDATE_DISABLED) |
| rxsc->stats.InPktsUnchecked += rsp.pkt_unchecked_cnt; |
| else |
| rxsc->stats.InPktsOK += rsp.pkt_unchecked_cnt; |
| |
| ctx->stats.rx_sc_stats->InOctetsValidated = rxsc->stats.InOctetsValidated; |
| ctx->stats.rx_sc_stats->InOctetsDecrypted = rxsc->stats.InOctetsDecrypted; |
| ctx->stats.rx_sc_stats->InPktsInvalid = rxsc->stats.InPktsInvalid; |
| ctx->stats.rx_sc_stats->InPktsNotValid = rxsc->stats.InPktsNotValid; |
| ctx->stats.rx_sc_stats->InPktsLate = rxsc->stats.InPktsLate; |
| ctx->stats.rx_sc_stats->InPktsDelayed = rxsc->stats.InPktsDelayed; |
| ctx->stats.rx_sc_stats->InPktsUnchecked = rxsc->stats.InPktsUnchecked; |
| ctx->stats.rx_sc_stats->InPktsOK = rxsc->stats.InPktsOK; |
| |
| return 0; |
| } |
| |
| static int cn10k_mdo_get_rx_sa_stats(struct macsec_context *ctx) |
| { |
| struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); |
| struct macsec_rx_sc *sw_rx_sc = ctx->sa.rx_sa->sc; |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct mcs_sa_stats rsp = { 0 }; |
| u8 sa_num = ctx->sa.assoc_num; |
| struct cn10k_mcs_rxsc *rxsc; |
| |
| rxsc = cn10k_mcs_get_rxsc(cfg, ctx->secy, sw_rx_sc); |
| if (!rxsc) |
| return -ENOENT; |
| |
| if (sa_num >= CN10K_MCS_SA_PER_SC) |
| return -EOPNOTSUPP; |
| |
| cn10k_mcs_sa_stats(pfvf, rxsc->hw_sa_id[sa_num], &rsp, MCS_RX, false); |
| |
| ctx->stats.rx_sa_stats->InPktsOK = rsp.pkt_ok_cnt; |
| ctx->stats.rx_sa_stats->InPktsInvalid = rsp.pkt_invalid_cnt; |
| ctx->stats.rx_sa_stats->InPktsNotValid = rsp.pkt_notvalid_cnt; |
| ctx->stats.rx_sa_stats->InPktsNotUsingSA = rsp.pkt_nosaerror_cnt; |
| ctx->stats.rx_sa_stats->InPktsUnusedSA = rsp.pkt_nosa_cnt; |
| |
| return 0; |
| } |
| |
| static const struct macsec_ops cn10k_mcs_ops = { |
| .mdo_dev_open = cn10k_mdo_open, |
| .mdo_dev_stop = cn10k_mdo_stop, |
| .mdo_add_secy = cn10k_mdo_add_secy, |
| .mdo_upd_secy = cn10k_mdo_upd_secy, |
| .mdo_del_secy = cn10k_mdo_del_secy, |
| .mdo_add_rxsc = cn10k_mdo_add_rxsc, |
| .mdo_upd_rxsc = cn10k_mdo_upd_rxsc, |
| .mdo_del_rxsc = cn10k_mdo_del_rxsc, |
| .mdo_add_rxsa = cn10k_mdo_add_rxsa, |
| .mdo_upd_rxsa = cn10k_mdo_upd_rxsa, |
| .mdo_del_rxsa = cn10k_mdo_del_rxsa, |
| .mdo_add_txsa = cn10k_mdo_add_txsa, |
| .mdo_upd_txsa = cn10k_mdo_upd_txsa, |
| .mdo_del_txsa = cn10k_mdo_del_txsa, |
| .mdo_get_dev_stats = cn10k_mdo_get_dev_stats, |
| .mdo_get_tx_sc_stats = cn10k_mdo_get_tx_sc_stats, |
| .mdo_get_tx_sa_stats = cn10k_mdo_get_tx_sa_stats, |
| .mdo_get_rx_sc_stats = cn10k_mdo_get_rx_sc_stats, |
| .mdo_get_rx_sa_stats = cn10k_mdo_get_rx_sa_stats, |
| }; |
| |
| void cn10k_handle_mcs_event(struct otx2_nic *pfvf, struct mcs_intr_info *event) |
| { |
| struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; |
| struct macsec_tx_sa *sw_tx_sa = NULL; |
| struct macsec_secy *secy = NULL; |
| struct cn10k_mcs_txsc *txsc; |
| u8 an; |
| |
| if (!test_bit(CN10K_HW_MACSEC, &pfvf->hw.cap_flag)) |
| return; |
| |
| if (!(event->intr_mask & MCS_CPM_TX_PACKET_XPN_EQ0_INT)) |
| return; |
| |
| /* Find the SecY to which the expired hardware SA is mapped */ |
| list_for_each_entry(txsc, &cfg->txsc_list, entry) { |
| for (an = 0; an < CN10K_MCS_SA_PER_SC; an++) |
| if (txsc->hw_sa_id[an] == event->sa_id) { |
| secy = txsc->sw_secy; |
| sw_tx_sa = rcu_dereference_bh(secy->tx_sc.sa[an]); |
| } |
| } |
| |
| if (secy && sw_tx_sa) |
| macsec_pn_wrapped(secy, sw_tx_sa); |
| } |
| |
| int cn10k_mcs_init(struct otx2_nic *pfvf) |
| { |
| struct mbox *mbox = &pfvf->mbox; |
| struct cn10k_mcs_cfg *cfg; |
| struct mcs_intr_cfg *req; |
| |
| if (!test_bit(CN10K_HW_MACSEC, &pfvf->hw.cap_flag)) |
| return 0; |
| |
| cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); |
| if (!cfg) |
| return -ENOMEM; |
| |
| INIT_LIST_HEAD(&cfg->txsc_list); |
| INIT_LIST_HEAD(&cfg->rxsc_list); |
| pfvf->macsec_cfg = cfg; |
| |
| pfvf->netdev->features |= NETIF_F_HW_MACSEC; |
| pfvf->netdev->macsec_ops = &cn10k_mcs_ops; |
| |
| mutex_lock(&mbox->lock); |
| |
| req = otx2_mbox_alloc_msg_mcs_intr_cfg(mbox); |
| if (!req) |
| goto fail; |
| |
| req->intr_mask = MCS_CPM_TX_PACKET_XPN_EQ0_INT; |
| |
| if (otx2_sync_mbox_msg(mbox)) |
| goto fail; |
| |
| mutex_unlock(&mbox->lock); |
| |
| return 0; |
| fail: |
| dev_err(pfvf->dev, "Cannot notify PN wrapped event\n"); |
| mutex_unlock(&mbox->lock); |
| return 0; |
| } |
| |
| void cn10k_mcs_free(struct otx2_nic *pfvf) |
| { |
| if (!test_bit(CN10K_HW_MACSEC, &pfvf->hw.cap_flag)) |
| return; |
| |
| cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, 0, true); |
| cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, 0, true); |
| kfree(pfvf->macsec_cfg); |
| pfvf->macsec_cfg = NULL; |
| } |