From 76c4b6e96c5b5dfb2c5584dcea8c149534e1df47 Mon Sep 17 00:00:00 2001 From: Samuel Morris Date: Wed, 3 Jun 2026 16:19:37 -0700 Subject: [PATCH 1/6] net: fec: add imx8mm devinfo with UDP checksum quirk The imx8mm FEC hardware RX checksum offload engine incorrectly marks certain UDP packets as checksum-valid, causing them to be silently dropped by the kernel UDP stack. Add a dedicated fsl,imx8mm-fec compatible string, which previously fell through to fsl,imx8mq-fec, and a new FEC_QUIRK_ERR_UDP_CSUM quirk. When the quirk is set, downgrade CHECKSUM_UNNECESSARY to CHECKSUM_NONE for UDP packets only after the HW reports no error, forcing the kernel UDP stack to re-validate the checksum in software. TCP checksum offload is unaffected. --- drivers/net/ethernet/freescale/fec.h | 6 +++++ drivers/net/ethernet/freescale/fec_main.c | 27 ++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 423838c8a27ae..df33dc711ebd0 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -514,6 +514,12 @@ struct bufdesc_ex { */ #define FEC_QUIRK_HAS_MDIO_C45 BIT(24) +/* i.MX8MM FEC hardware RX checksum offload incorrectly validates certain UDP + * packets, causing them to be silently dropped by the kernel UDP stack. + * Force software re-validation for UDP by downgrading CHECKSUM_UNNECESSARY. + */ +#define FEC_QUIRK_ERR_UDP_CSUM BIT(25) + struct bufdesc_prop { int qid; /* Address of Rx and Tx buffers */ diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index b445ae835849d..2bee5ccc0cb75 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -154,6 +154,17 @@ static const struct fec_devinfo fec_imx6ul_info = { FEC_QUIRK_HAS_MDIO_C45, }; +static const struct fec_devinfo fec_imx8mm_info = { + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | + FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | + FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | + FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | + FEC_QUIRK_HAS_EEE | FEC_QUIRK_WAKEUP_FROM_INT2 | + FEC_QUIRK_HAS_MDIO_C45 | FEC_QUIRK_ERR_UDP_CSUM, +}; + static const struct fec_devinfo fec_imx8mq_info = { .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | @@ -209,6 +220,9 @@ static struct platform_device_id fec_devtype[] = { }, { .name = "imx6ul-fec", .driver_data = (kernel_ulong_t)&fec_imx6ul_info, + }, { + .name = "imx8mm-fec", + .driver_data = (kernel_ulong_t)&fec_imx8mm_info, }, { .name = "imx8mq-fec", .driver_data = (kernel_ulong_t)&fec_imx8mq_info, @@ -232,6 +246,7 @@ enum imx_fec_type { MVF600_FEC, IMX6SX_FEC, IMX6UL_FEC, + IMX8MM_FEC, IMX8MQ_FEC, IMX8QM_FEC, S32V234_FEC, @@ -245,6 +260,7 @@ static const struct of_device_id fec_dt_ids[] = { { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, { .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], }, { .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], }, + { .compatible = "fsl,imx8mm-fec", .data = &fec_devtype[IMX8MM_FEC], }, { .compatible = "fsl,imx8mq-fec", .data = &fec_devtype[IMX8MQ_FEC], }, { .compatible = "fsl,imx8qm-fec", .data = &fec_devtype[IMX8QM_FEC], }, { .compatible = "fsl,s32v234-fec", .data = &fec_devtype[S32V234_FEC], }, @@ -1786,8 +1802,17 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) if (fep->bufdesc_ex && (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) { - /* don't check it */ skb->ip_summed = CHECKSUM_UNNECESSARY; + /* i.MX8MM FEC HW checksum offload incorrectly + * validates certain UDP packets (FEC_QUIRK_ERR_UDP_CSUM). + * Force software re-validation for UDP only. + */ + if (fep->quirks & FEC_QUIRK_ERR_UDP_CSUM) { + struct iphdr *iph = (struct iphdr *)skb_network_header(skb); + + if (iph && iph->protocol == IPPROTO_UDP) + skb->ip_summed = CHECKSUM_NONE; + } } else { skb_checksum_none_assert(skb); } From 06039cf059d56b7485450359e815f3e8a9c7a14f Mon Sep 17 00:00:00 2001 From: Samuel Morris Date: Wed, 17 Jun 2026 08:36:17 -0700 Subject: [PATCH 2/6] net: fec: clarify FEC_QUIRK_ERR_UDP_CSUM scope is IPv4-only The implementation only handles IPv4/UDP, but the comment described the quirk as applying to UDP generally. Update the comment to match. --- drivers/net/ethernet/freescale/fec.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index df33dc711ebd0..aafca07d0beee 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -514,9 +514,9 @@ struct bufdesc_ex { */ #define FEC_QUIRK_HAS_MDIO_C45 BIT(24) -/* i.MX8MM FEC hardware RX checksum offload incorrectly validates certain UDP - * packets, causing them to be silently dropped by the kernel UDP stack. - * Force software re-validation for UDP by downgrading CHECKSUM_UNNECESSARY. +/* i.MX8MM FEC hardware RX checksum offload incorrectly validates certain IPv4 + * UDP packets, causing them to be silently dropped by the kernel UDP stack. + * Force software re-validation for IPv4/UDP by downgrading CHECKSUM_UNNECESSARY. */ #define FEC_QUIRK_ERR_UDP_CSUM BIT(25) From ad22f5a959a2498631b181939b43f9cce933e036 Mon Sep 17 00:00:00 2001 From: Samuel Morris Date: Wed, 17 Jun 2026 08:37:06 -0700 Subject: [PATCH 3/6] net: fec: fix unsafe skb header access in UDP checksum quirk skb_network_header() never returns NULL, making the null check ineffective. It also does not handle non-linear skbs, risking out-of-bounds reads on malformed or non-IPv4 frames. Replace with skb_header_pointer() into a local buffer, and guard the block with an ETH_P_IP protocol check before treating the header as an IPv4 header. --- drivers/net/ethernet/freescale/fec_main.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 2bee5ccc0cb75..ebc0f88902514 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1807,9 +1807,12 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) * validates certain UDP packets (FEC_QUIRK_ERR_UDP_CSUM). * Force software re-validation for UDP only. */ - if (fep->quirks & FEC_QUIRK_ERR_UDP_CSUM) { - struct iphdr *iph = (struct iphdr *)skb_network_header(skb); + if (fep->quirks & FEC_QUIRK_ERR_UDP_CSUM && + skb->protocol == htons(ETH_P_IP)) { + struct iphdr _iph; + const struct iphdr *iph; + iph = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); if (iph && iph->protocol == IPPROTO_UDP) skb->ip_summed = CHECKSUM_NONE; } From a3cb0169741ce66c7a6647b15556e072250e0b7e Mon Sep 17 00:00:00 2001 From: Samuel Morris Date: Wed, 17 Jun 2026 12:49:55 -0700 Subject: [PATCH 4/6] net: fec: disable RACC PRODIS for imx8mm due to UDP checksum errata The i.MX8MM FEC hardware incorrectly identifies certain valid UDP frames as having bad protocol checksums when PRODIS (protocol discard) is active in the RACC register, causing them to be silently dropped before the driver ever receives them. Based on a patch distributed by Audinate with their i.MX8MM EVK. Scoped to imx8mm via the existing FEC_QUIRK_ERR_UDP_CSUM flag rather than changing FEC_RACC_OPTIONS globally, so all other FEC variants retain their existing behaviour. --- drivers/net/ethernet/freescale/fec_main.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index ebc0f88902514..9f7b9a780eece 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1109,11 +1109,20 @@ fec_restart(struct net_device *ndev) /* align IP header */ val |= FEC_RACC_SHIFT16; - if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) + if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) { /* set RX checksum */ - val |= FEC_RACC_OPTIONS; - else + u32 racc_opts = FEC_RACC_OPTIONS; + + /* i.MX8MM FEC incorrectly discards valid UDP frames when + * PRODIS is active due to a hardware checksum errata. + * Disable protocol discard for this variant only. + */ + if (fep->quirks & FEC_QUIRK_ERR_UDP_CSUM) + racc_opts &= ~FEC_RACC_PRODIS; + val |= racc_opts; + } else { val &= ~FEC_RACC_OPTIONS; + } writel(val, fep->hwp + FEC_RACC); writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL); } From 02f3ec89c520f08a5c50554beb77f74c12a643f6 Mon Sep 17 00:00:00 2001 From: Samuel Morris Date: Wed, 17 Jun 2026 13:51:45 -0700 Subject: [PATCH 5/6] net: fec: clear stale RACC option bits before applying PRODIS mask val is read from the FEC_RACC register before being modified. When the FEC_QUIRK_ERR_UDP_CSUM quirk masks PRODIS out of racc_opts, the subsequent 'val |= racc_opts' could not clear a PRODIS bit that was already set in the register, leaving protocol discard active. Clear all FEC_RACC_OPTIONS bits in val first, then OR in the desired options. --- drivers/net/ethernet/freescale/fec_main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 9f7b9a780eece..e7eb6f598f581 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1119,6 +1119,11 @@ fec_restart(struct net_device *ndev) */ if (fep->quirks & FEC_QUIRK_ERR_UDP_CSUM) racc_opts &= ~FEC_RACC_PRODIS; + /* clear all option bits first so any masked-out bits + * (e.g. PRODIS) already set in the register are cleared + * rather than left active by the OR below + */ + val &= ~FEC_RACC_OPTIONS; val |= racc_opts; } else { val &= ~FEC_RACC_OPTIONS; From 047e407324872973939c433de514ba2173ce070e Mon Sep 17 00:00:00 2001 From: Samuel Morris Date: Wed, 17 Jun 2026 13:51:52 -0700 Subject: [PATCH 6/6] net: fec: document both workarounds driven by FEC_QUIRK_ERR_UDP_CSUM The quirk now drives two distinct workarounds: clearing FEC_RACC_PRODIS in fec_restart() and downgrading CHECKSUM_UNNECESSARY in the RX path. The comment only mentioned the latter; document both. --- drivers/net/ethernet/freescale/fec.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index aafca07d0beee..a0e90b7b788b5 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -515,8 +515,12 @@ struct bufdesc_ex { #define FEC_QUIRK_HAS_MDIO_C45 BIT(24) /* i.MX8MM FEC hardware RX checksum offload incorrectly validates certain IPv4 - * UDP packets, causing them to be silently dropped by the kernel UDP stack. - * Force software re-validation for IPv4/UDP by downgrading CHECKSUM_UNNECESSARY. + * UDP packets, causing them to be silently dropped. This quirk drives two + * workarounds: + * - fec_restart() clears FEC_RACC_PRODIS so the hardware stops discarding + * these frames before the driver receives them. + * - the RX path forces software re-validation for IPv4/UDP by downgrading + * CHECKSUM_UNNECESSARY to CHECKSUM_NONE. */ #define FEC_QUIRK_ERR_UDP_CSUM BIT(25)