diff options
| -rw-r--r-- | drivers/net/ethernet/freescale/enetc/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc.h | 15 | ||||
| -rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc4_pf.c | 4 | ||||
| -rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc_mailbox.h | 151 | ||||
| -rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc_msg.c | 127 | ||||
| -rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc_vf.c | 97 |
6 files changed, 326 insertions, 70 deletions
diff --git a/drivers/net/ethernet/freescale/enetc/Kconfig b/drivers/net/ethernet/freescale/enetc/Kconfig index 117038104b69..db5c17a44613 100644 --- a/drivers/net/ethernet/freescale/enetc/Kconfig +++ b/drivers/net/ethernet/freescale/enetc/Kconfig @@ -10,6 +10,7 @@ config FSL_ENETC_CORE config NXP_ENETC_PF_COMMON tristate + select CRC_ITU_T help This module supports common functionality between drivers of different versions of NXP ENETC PF controllers. @@ -70,6 +71,7 @@ config FSL_ENETC_VF select FSL_ENETC_MDIO select PHYLINK select DIMLIB + select CRC_ITU_T help This driver supports NXP ENETC gigabit ethernet controller PCIe virtual function (VF) devices enabled by the ENETC PF driver. diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h index e691144e8756..b70b625328ea 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.h +++ b/drivers/net/ethernet/freescale/enetc/enetc.h @@ -16,6 +16,7 @@ #include "enetc_hw.h" #include "enetc4_hw.h" +#include "enetc_mailbox.h" #define ENETC_MAC_MAXFRM_SIZE 9600 #define ENETC_MAX_MTU (ENETC_MAC_MAXFRM_SIZE - \ @@ -257,12 +258,6 @@ static inline union enetc_rx_bd *enetc_rxbd_ext(union enetc_rx_bd *rxbd) return ++rxbd; } -struct enetc_msg_swbd { - void *vaddr; - dma_addr_t dma; - int size; -}; - #define ENETC_REV1 0x1 #define ENETC_REV4 0x4 @@ -490,14 +485,6 @@ struct enetc_ndev_priv { u64 sysclk_freq; /* NETC system clock frequency */ }; -/* Messaging */ - -/* VF-PF set primary MAC address message format */ -struct enetc_msg_cmd_set_primary_mac { - struct enetc_msg_cmd_header header; - struct sockaddr mac; -}; - #define ENETC_CBD(R, i) (&(((struct enetc_cbd *)((R).bd_base))[i])) #define ENETC_CBDR_TIMEOUT 1000 /* usecs */ diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c index 56899f2254aa..4e771f852358 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c @@ -17,10 +17,6 @@ #define ENETC_MAC_FILTER_TYPE_ALL (ENETC_MAC_FILTER_TYPE_UC | \ ENETC_MAC_FILTER_TYPE_MC) -struct enetc_mac_addr { - u8 addr[ETH_ALEN]; -}; - static void enetc4_get_port_caps(struct enetc_pf *pf) { struct enetc_hw *hw = &pf->si->hw; diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h new file mode 100644 index 000000000000..86a51bae19f3 --- /dev/null +++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* + * Copyright 2025-2026 NXP + * + * The VSI-to-PSI message generic format: + * + * OFFSET 0 16 24 31 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 0x0 | CRC16 (big-endian) | CLASS ID | CMD ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 0x4 | PROTO VER | LEN | RESV | COOKIE| RESV | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 0x8 | RESV | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 0xc | RESV | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 0x10 | | + * 0x14 | | + * 0x18 | Message Body | + * 0x1c | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 0x20 | | + * ~ | Extended Message Body: LEN x 32B | + * 0x3e0 | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Field Descriptions: + * CRC16 (16-bit): Big endian, CRC16 CCITT-FALSE algorithm, It provides the + * equivalent data integrity check functionality as the FCS for standard + * Ethernet frames. + * + * CLASS ID (8-bit) and CMD ID (8-bit): These are 8-bit fields identifying + * the command class and the class-specific operations supported. For more + * details, please refer to the definitions of the relevant class ID and + * cmd ID in this document. + * + * PROTO VER (8-bit): Supported VSI-PSI command protocol version. Currently + * only support version 0. To be incremented for future protocol extensions. + * + * LEN (8-bit): Extended message body length in increments of 32B. The upper + * limit is given by the physical implementation of the NETC VSI-PSI Messaging + * mechanism that supports message sizes of up to 1024B (including headers), + * that are multiple of 32B. + * + * COOKIE (4-bit): Optional parameter, which, if not 0, indicates that the + * command should be execute asynchronously on PSI side. If COOKIE is not 0 + * and the command cannot be executed instantly on the PSI side (it would + * take longer time to complete), the PSI may enqueue the request in a command + * queue of up to 15 entries per VSI and, later after command execution, the + * PSI returns the COOKIE to VSI as part of an asynchronous notification + * message that indicates the command completion status. If COOKIE is 0 then + * the command is considered as blocking, the PSI will wait for the execution + * of the command to complete before updating the PSIMSGRR[MC] field with the + * corresponding return code. + * + * The PSI-to-VSI message generic format: + * 0 4 8 12 15 + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | COOKIE | CLASS CODE | CLASS ID | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * The PSI to VSI message format is mapped to the following PSI message + * registers/fields, depending on use case: + * 1) PSI_RX_control: PSIMSGRR[MC] - for VSI command return code messages + * (blocking requests), and + * 2) PSI_TX_control: PSIMSGSR[MC] - for PSI to VSI notification messages + * (async mode) + */ + +#ifndef __ENETC_MAILBOX_H +#define __ENETC_MAILBOX_H + +#include <linux/crc-itu-t.h> + +#define ENETC_CRC_INIT 0xffff +#define ENETC_MSG_ALIGN 32 +/* s indicates the size of the message */ +#define ENETC_MSG_EXT_BODY_LEN(s) ((s) / ENETC_MSG_ALIGN - 1) +/* l indicates the extended body len (LEN field) of the message */ +#define ENETC_MSG_SIZE(l) (((l) + 1) * ENETC_MSG_ALIGN) + +/* The cookie filed of VSI-to-PSI message */ +#define ENETC_VF_MSG_COOKIE GENMASK(3, 0) +/* The fileds of PSI-to-VSI message, the message is only 16-bit */ +#define ENETC_PF_MSG_COOKIE GENMASK(3, 0) +#define ENETC_PF_MSG_CLASS_CODE GENMASK(7, 4) +#define ENETC_PF_MSG_CLASS_ID GENMASK(15, 8) + +enum enetc_msg_class_id { + /* Class ID for PSI-to-VSI messages */ + ENETC_MSG_CLASS_ID_CMD_SUCCESS = 1, + ENETC_MSG_CLASS_ID_PERMISSION_DENY, + ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT, + ENETC_MSG_CLASS_ID_PSI_BUSY, + ENETC_MSG_CLASS_ID_CRC_ERROR, + ENETC_MSG_CLASS_ID_PROTO_NOT_SUPPORT, + ENETC_MSG_CLASS_ID_INVALID_MSG_LEN, + ENETC_MSG_CLASS_ID_CMD_TIMEOUT, + ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED, + ENETC_MSG_CLASS_ID_CMD_FAIL, /* Generic error code for failure */ + ENETC_MSG_CLASS_ID_CMD_DEFERRED = 0xf, + + /* Common Class ID for PSI-to-VSI and VSI-to-PSI messages */ + ENETC_MSG_CLASS_ID_MAC_FILTER = 0x20, +}; + +enum enetc_msg_mac_filter_cmd_id { + ENETC_MSG_SET_PRIMARY_MAC, +}; + +/* Class-specific error return codes of MAC filter */ +enum enetc_mac_filter_class_code { + ENETC_MF_CLASS_CODE_INVALID_MAC, +}; + +struct enetc_msg_swbd { + void *vaddr; + dma_addr_t dma; + int size; +}; + +/* The generic VSI-to-PSI message header */ +struct enetc_msg_header { + __be16 crc16; + u8 class_id; + u8 cmd_id; + u8 proto_ver; + u8 len; + u8 resv0; + u8 cookie; + u8 resv2[8]; +}; + +struct enetc_mac_addr { + u8 addr[ETH_ALEN]; /* Network byte order */ +}; + +/* Message format of class_id 0x20 for exact MAC filter. + * cmd_id 0x0: set primary MAC + * cmd_id 0x1: Add entries to MAC address filter table + * cmd_id 0x2: Delete entries from MAC address filter table + * Note that cmd_id 0x1 and 0x2 are not supported yet. + */ +struct enetc_msg_mac_exact_filter { + struct enetc_msg_header hdr; + u8 mac_cnt; /* No need to set for cmd_id 0 */ + u8 resv[3]; + struct enetc_mac_addr mac[]; +}; + +#endif diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c index f696751c00ea..4ab123cbfbec 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c @@ -3,6 +3,11 @@ #include "enetc_pf_common.h" +#define ENETC_PF_MSG_SUCCESS FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \ + ENETC_MSG_CLASS_ID_CMD_SUCCESS) +#define ENETC_PF_MSG_NOTSUPP FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \ + ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT) + static void enetc_msg_disable_mr_int(struct enetc_pf *pf) { struct enetc_hw *hw = &pf->si->hw; @@ -36,23 +41,35 @@ static irqreturn_t enetc_msg_psi_msix(int irq, void *data) } /* Messaging */ -static u16 enetc_msg_pf_set_vf_primary_mac_addr(struct enetc_pf *pf, - int vf_id, void *msg) +static bool enetc_msg_check_crc16(void *msg_addr, u32 msg_size) +{ + u32 data_size = msg_size - 2; + u8 *data_buf = msg_addr + 2; + u16 verify_val; + + verify_val = crc_itu_t(ENETC_CRC_INIT, data_buf, data_size); + verify_val = crc_itu_t(verify_val, msg_addr, 2); + if (verify_val) + return false; + + return true; +} + +static u16 enetc_msg_set_vf_primary_mac_addr(struct enetc_pf *pf, int vf_id, + void *vf_msg) { struct enetc_vf_state *vf_state = &pf->vf_state[vf_id]; - struct enetc_msg_cmd_set_primary_mac *cmd = msg; + struct enetc_msg_mac_exact_filter *msg = vf_msg; struct device *dev = &pf->si->pdev->dev; - u16 cmd_id = cmd->header.id; - char *addr; - - if (cmd_id != ENETC_MSG_CMD_MNG_ADD) - return ENETC_MSG_CMD_STATUS_FAIL; + char *addr = msg->mac[0].addr; - addr = cmd->mac.sa_data; if (!is_valid_ether_addr(addr)) { dev_err_ratelimited(dev, "VF%d attempted to set invalid MAC\n", vf_id); - return ENETC_MSG_CMD_STATUS_FAIL; + return (FIELD_PREP(ENETC_PF_MSG_CLASS_ID, + ENETC_MSG_CLASS_ID_MAC_FILTER) | + FIELD_PREP(ENETC_PF_MSG_CLASS_CODE, + ENETC_MF_CLASS_CODE_INVALID_MAC)); } mutex_lock(&vf_state->lock); @@ -61,53 +78,97 @@ static u16 enetc_msg_pf_set_vf_primary_mac_addr(struct enetc_pf *pf, dev_err_ratelimited(dev, "VF%d attempted to override PF set MAC\n", vf_id); - return ENETC_MSG_CMD_STATUS_FAIL; + return FIELD_PREP(ENETC_PF_MSG_CLASS_ID, + ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED); } enetc_set_si_hw_addr(pf, vf_id + 1, addr); mutex_unlock(&vf_state->lock); - return ENETC_MSG_CMD_STATUS_OK; + return ENETC_PF_MSG_SUCCESS; +} + +static u16 enetc_msg_handle_mac_filter(struct enetc_pf *pf, int vf_id, + void *vf_msg) +{ + struct enetc_msg_header *msg_hdr = vf_msg; + + switch (msg_hdr->cmd_id) { + case ENETC_MSG_SET_PRIMARY_MAC: + return enetc_msg_set_vf_primary_mac_addr(pf, vf_id, vf_msg); + default: + return ENETC_PF_MSG_NOTSUPP; + } } static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id, - u16 *status) + u16 *pf_msg) { struct enetc_msg_swbd *msg_swbd = &pf->rxmsg[vf_id]; + struct enetc_msg_header *msg_hdr = msg_swbd->vaddr; + u32 msg_size = ENETC_MSG_SIZE(msg_hdr->len); struct device *dev = &pf->si->pdev->dev; - struct enetc_msg_cmd_header *cmd_hdr; - u16 cmd_type; u8 *msg; - msg = kzalloc_objs(*msg, msg_swbd->size); + if (msg_size > ENETC_DEFAULT_MSG_SIZE) { + dev_err_ratelimited(dev, + "Invalid message size: %u\n", msg_size); + *pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID, + ENETC_MSG_CLASS_ID_INVALID_MSG_LEN); + return; + } + + /* To prevent malicious VF from tampering with the original data by + * sending new messages after passing the check, the DMA buffer data + * is copied to the msg buffer before validation. + */ + msg = kzalloc_objs(*msg, msg_size); if (!msg) { dev_err_ratelimited(dev, "Failed to allocate message buffer\n"); - *status = ENETC_MSG_CMD_STATUS_FAIL; + *pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID, + ENETC_MSG_CLASS_ID_CMD_FAIL); return; } - /* Currently, only ENETC_MSG_CMD_MNG_MAC command is supported, so - * only sizeof(struct enetc_msg_cmd_set_primary_mac) bytes need to - * be copied. This data already includes the cmd_type field, so it - * can correctly return an error code. - */ - memcpy(msg, msg_swbd->vaddr, - sizeof(struct enetc_msg_cmd_set_primary_mac)); - cmd_hdr = (struct enetc_msg_cmd_header *)msg; - cmd_type = cmd_hdr->type; - - switch (cmd_type) { - case ENETC_MSG_CMD_MNG_MAC: - *status = enetc_msg_pf_set_vf_primary_mac_addr(pf, vf_id, msg); + memcpy(msg, msg_swbd->vaddr, msg_size); + if (!enetc_msg_check_crc16(msg, msg_size)) { + dev_err_ratelimited(dev, "VSI to PSI Message CRC16 error\n"); + *pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID, + ENETC_MSG_CLASS_ID_CRC_ERROR); + + goto free_msg; + } + + /* Default to not supported */ + *pf_msg = ENETC_PF_MSG_NOTSUPP; + msg_hdr = (struct enetc_msg_header *)msg; + + /* Currently, asynchronous actions are not supported */ + if (FIELD_GET(ENETC_VF_MSG_COOKIE, msg_hdr->cookie)) { + dev_err_ratelimited(dev, + "Cookie field is not supported yet\n"); + goto free_msg; + } + + /* Currently only support protocol version 0 */ + if (msg_hdr->proto_ver) { + dev_err_ratelimited(dev, "Unsupported protocol version %u\n", + msg_hdr->proto_ver); + goto free_msg; + } + + switch (msg_hdr->class_id) { + case ENETC_MSG_CLASS_ID_MAC_FILTER: + *pf_msg = enetc_msg_handle_mac_filter(pf, vf_id, msg); break; default: - *status = ENETC_MSG_CMD_STATUS_FAIL; dev_err_ratelimited(dev, - "command not supported (cmd_type: 0x%x)\n", - cmd_type); + "Unsupported message class ID: 0x%x\n", + msg_hdr->class_id); } +free_msg: kfree(msg); } diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c index 9065bdbd02aa..77c0eddba6e2 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c @@ -7,7 +7,29 @@ #define ENETC_DRV_NAME_STR "ENETC VF driver" -/* Messaging */ +/* Note: This function should be called after filling the message body, + * because the CRC16 needs to be calculated after all the data has been + * filled. + */ +static void enetc_msg_fill_common_hdr(struct enetc_msg_swbd *msg_swbd, + u8 class_id, u8 cmd_id, u8 proto_ver, + u8 cookie) +{ + struct enetc_msg_header *hdr = msg_swbd->vaddr; + u8 *data_buf = ((u8 *)msg_swbd->vaddr) + 2; /* skip crc16 field */ + u32 data_size = msg_swbd->size - 2; + u16 crc16; + + hdr->class_id = class_id; + hdr->cmd_id = cmd_id; + hdr->len = ENETC_MSG_EXT_BODY_LEN(msg_swbd->size); + hdr->proto_ver = proto_ver; + hdr->cookie = FIELD_PREP(ENETC_VF_MSG_COOKIE, cookie); + + crc16 = crc_itu_t(ENETC_CRC_INIT, data_buf, data_size); + hdr->crc16 = htons(crc16); +} + static void enetc_msg_vsi_write_msg(struct enetc_hw *hw, struct enetc_msg_swbd *msg) { @@ -30,6 +52,7 @@ static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg) { struct device *dev = &si->pdev->dev; u32 vsimsgsr; + u16 pf_msg; int err; /* The VSI mailbox may be busy if last message was not yet processed @@ -60,36 +83,72 @@ static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg) /* check for message delivery error */ if (vsimsgsr & ENETC_VSIMSGSR_MS) { - dev_err(dev, "VSI command execute error: %d\n", - ENETC_SIMSGSR_GET_MC(vsimsgsr)); + dev_err(dev, "Transfer error when copying the data\n"); return -EIO; } - return 0; + pf_msg = ENETC_SIMSGSR_GET_MC(vsimsgsr); + /* Check the user-defined completion status. */ + if (FIELD_GET(ENETC_PF_MSG_CLASS_ID, pf_msg) != + ENETC_MSG_CLASS_ID_CMD_SUCCESS) { + switch (FIELD_GET(ENETC_PF_MSG_CLASS_ID, pf_msg)) { + case ENETC_MSG_CLASS_ID_PERMISSION_DENY: + /* Intentionally returning early to prevent excessive + * error logs due to permission issues. + */ + return -EACCES; + case ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT: + case ENETC_MSG_CLASS_ID_PROTO_NOT_SUPPORT: + err = -EOPNOTSUPP; + break; + case ENETC_MSG_CLASS_ID_PSI_BUSY: + err = -EBUSY; + break; + case ENETC_MSG_CLASS_ID_CMD_TIMEOUT: + err = -ETIME; + break; + case ENETC_MSG_CLASS_ID_INVALID_MSG_LEN: + case ENETC_MSG_CLASS_ID_MAC_FILTER: + err = -EINVAL; + break; + case ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED: + err = -EPERM; + break; + case ENETC_MSG_CLASS_ID_CMD_FAIL: + case ENETC_MSG_CLASS_ID_CRC_ERROR: + case ENETC_MSG_CLASS_ID_CMD_DEFERRED: + default: + err = -EIO; + } + } + + if (err) + dev_err(dev, "Return error code from PSI: 0x%04x\n", pf_msg); + + return err; } static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv, struct sockaddr *saddr) { - struct enetc_msg_cmd_set_primary_mac *cmd; - struct enetc_msg_swbd msg; - - msg.size = ALIGN(sizeof(struct enetc_msg_cmd_set_primary_mac), 64); - msg.vaddr = dma_alloc_coherent(priv->dev, msg.size, &msg.dma, - GFP_KERNEL); - if (!msg.vaddr) { - dev_err(priv->dev, "Failed to alloc Tx msg (size: %d)\n", - msg.size); + struct enetc_msg_mac_exact_filter *msg; + struct enetc_msg_swbd msg_swbd; + u32 msg_size; + + msg_size = struct_size(msg, mac, 1); + msg_swbd.size = ALIGN(msg_size, ENETC_MSG_ALIGN); + msg_swbd.vaddr = dma_alloc_coherent(priv->dev, msg_swbd.size, + &msg_swbd.dma, GFP_KERNEL); + if (!msg_swbd.vaddr) return -ENOMEM; - } - cmd = (struct enetc_msg_cmd_set_primary_mac *)msg.vaddr; - cmd->header.type = ENETC_MSG_CMD_MNG_MAC; - cmd->header.id = ENETC_MSG_CMD_MNG_ADD; - memcpy(&cmd->mac, saddr, sizeof(struct sockaddr)); + msg = (struct enetc_msg_mac_exact_filter *)msg_swbd.vaddr; + memcpy(&msg->mac[0].addr, saddr->sa_data, ETH_ALEN); + enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_MAC_FILTER, + ENETC_MSG_SET_PRIMARY_MAC, 0, 0); /* send the command and wait */ - return enetc_msg_vsi_send(priv->si, &msg); + return enetc_msg_vsi_send(priv->si, &msg_swbd); } static int enetc_vf_set_mac_addr(struct net_device *ndev, void *addr) |
