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.
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.
#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 - \
return ++rxbd;
}
-struct enetc_msg_swbd {
- void *vaddr;
- dma_addr_t dma;
- int size;
-};
-
#define ENETC_REV1 0x1
#define ENETC_REV4 0x4
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 */
#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;
--- /dev/null
+/* 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
#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;
}
/* 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);
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);
}
#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)
{
{
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
/* 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)