1862a64c8SRaju Rangoju // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause) 2862a64c8SRaju Rangoju /* 3862a64c8SRaju Rangoju * Copyright (c) 2014-2025, Advanced Micro Devices, Inc. 4862a64c8SRaju Rangoju * Copyright (c) 2014, Synopsys, Inc. 5862a64c8SRaju Rangoju * All rights reserved 6862a64c8SRaju Rangoju * 7862a64c8SRaju Rangoju * Author: Raju Rangoju <Raju.Rangoju@amd.com> 8862a64c8SRaju Rangoju */ 9862a64c8SRaju Rangoju #include <linux/crc32.h> 10862a64c8SRaju Rangoju #include <linux/ip.h> 11862a64c8SRaju Rangoju #include <linux/udp.h> 12862a64c8SRaju Rangoju #include <net/tcp.h> 13862a64c8SRaju Rangoju #include <net/udp.h> 14862a64c8SRaju Rangoju #include <net/checksum.h> 15862a64c8SRaju Rangoju #include <net/selftests.h> 16862a64c8SRaju Rangoju 17862a64c8SRaju Rangoju #include "xgbe.h" 18862a64c8SRaju Rangoju #include "xgbe-common.h" 19862a64c8SRaju Rangoju 20862a64c8SRaju Rangoju #define XGBE_LOOPBACK_NONE 0 21862a64c8SRaju Rangoju #define XGBE_LOOPBACK_MAC 1 22*42b06fccSRaju Rangoju #define XGBE_LOOPBACK_PHY 2 23862a64c8SRaju Rangoju 24862a64c8SRaju Rangoju struct xgbe_test { 25862a64c8SRaju Rangoju char name[ETH_GSTRING_LEN]; 26862a64c8SRaju Rangoju int lb; 27862a64c8SRaju Rangoju int (*fn)(struct xgbe_prv_data *pdata); 28862a64c8SRaju Rangoju }; 29862a64c8SRaju Rangoju 30862a64c8SRaju Rangoju static u8 xgbe_test_id; 31862a64c8SRaju Rangoju 32862a64c8SRaju Rangoju static int xgbe_test_loopback_validate(struct sk_buff *skb, 33862a64c8SRaju Rangoju struct net_device *ndev, 34862a64c8SRaju Rangoju struct packet_type *pt, 35862a64c8SRaju Rangoju struct net_device *orig_ndev) 36862a64c8SRaju Rangoju { 37862a64c8SRaju Rangoju struct net_test_priv *tdata = pt->af_packet_priv; 38862a64c8SRaju Rangoju const unsigned char *dst = tdata->packet->dst; 39862a64c8SRaju Rangoju const unsigned char *src = tdata->packet->src; 40862a64c8SRaju Rangoju struct netsfhdr *hdr; 41862a64c8SRaju Rangoju struct ethhdr *eh; 42862a64c8SRaju Rangoju struct tcphdr *th; 43862a64c8SRaju Rangoju struct udphdr *uh; 44862a64c8SRaju Rangoju struct iphdr *ih; 45862a64c8SRaju Rangoju 46862a64c8SRaju Rangoju skb = skb_unshare(skb, GFP_ATOMIC); 47862a64c8SRaju Rangoju if (!skb) 48862a64c8SRaju Rangoju goto out; 49862a64c8SRaju Rangoju 50862a64c8SRaju Rangoju if (skb_linearize(skb)) 51862a64c8SRaju Rangoju goto out; 52862a64c8SRaju Rangoju 53862a64c8SRaju Rangoju if (skb_headlen(skb) < (NET_TEST_PKT_SIZE - ETH_HLEN)) 54862a64c8SRaju Rangoju goto out; 55862a64c8SRaju Rangoju 56862a64c8SRaju Rangoju eh = (struct ethhdr *)skb_mac_header(skb); 57862a64c8SRaju Rangoju if (dst) { 58862a64c8SRaju Rangoju if (!ether_addr_equal_unaligned(eh->h_dest, dst)) 59862a64c8SRaju Rangoju goto out; 60862a64c8SRaju Rangoju } 61862a64c8SRaju Rangoju if (src) { 62862a64c8SRaju Rangoju if (!ether_addr_equal_unaligned(eh->h_source, src)) 63862a64c8SRaju Rangoju goto out; 64862a64c8SRaju Rangoju } 65862a64c8SRaju Rangoju 66862a64c8SRaju Rangoju ih = ip_hdr(skb); 67862a64c8SRaju Rangoju 68862a64c8SRaju Rangoju if (tdata->packet->tcp) { 69862a64c8SRaju Rangoju if (ih->protocol != IPPROTO_TCP) 70862a64c8SRaju Rangoju goto out; 71862a64c8SRaju Rangoju 72862a64c8SRaju Rangoju th = (struct tcphdr *)((u8 *)ih + 4 * ih->ihl); 73862a64c8SRaju Rangoju if (th->dest != htons(tdata->packet->dport)) 74862a64c8SRaju Rangoju goto out; 75862a64c8SRaju Rangoju 76862a64c8SRaju Rangoju hdr = (struct netsfhdr *)((u8 *)th + sizeof(*th)); 77862a64c8SRaju Rangoju } else { 78862a64c8SRaju Rangoju if (ih->protocol != IPPROTO_UDP) 79862a64c8SRaju Rangoju goto out; 80862a64c8SRaju Rangoju 81862a64c8SRaju Rangoju uh = (struct udphdr *)((u8 *)ih + 4 * ih->ihl); 82862a64c8SRaju Rangoju if (uh->dest != htons(tdata->packet->dport)) 83862a64c8SRaju Rangoju goto out; 84862a64c8SRaju Rangoju 85862a64c8SRaju Rangoju hdr = (struct netsfhdr *)((u8 *)uh + sizeof(*uh)); 86862a64c8SRaju Rangoju } 87862a64c8SRaju Rangoju 88862a64c8SRaju Rangoju if (hdr->magic != cpu_to_be64(NET_TEST_PKT_MAGIC)) 89862a64c8SRaju Rangoju goto out; 90862a64c8SRaju Rangoju if (tdata->packet->id != hdr->id) 91862a64c8SRaju Rangoju goto out; 92862a64c8SRaju Rangoju 93862a64c8SRaju Rangoju tdata->ok = true; 94862a64c8SRaju Rangoju complete(&tdata->comp); 95862a64c8SRaju Rangoju out: 96862a64c8SRaju Rangoju kfree_skb(skb); 97862a64c8SRaju Rangoju return 0; 98862a64c8SRaju Rangoju } 99862a64c8SRaju Rangoju 100862a64c8SRaju Rangoju static int __xgbe_test_loopback(struct xgbe_prv_data *pdata, 101862a64c8SRaju Rangoju struct net_packet_attrs *attr) 102862a64c8SRaju Rangoju { 103862a64c8SRaju Rangoju struct net_test_priv *tdata; 104862a64c8SRaju Rangoju struct sk_buff *skb = NULL; 105862a64c8SRaju Rangoju int ret = 0; 106862a64c8SRaju Rangoju 107862a64c8SRaju Rangoju tdata = kzalloc(sizeof(*tdata), GFP_KERNEL); 108862a64c8SRaju Rangoju if (!tdata) 109862a64c8SRaju Rangoju return -ENOMEM; 110862a64c8SRaju Rangoju 111862a64c8SRaju Rangoju tdata->ok = false; 112862a64c8SRaju Rangoju init_completion(&tdata->comp); 113862a64c8SRaju Rangoju 114862a64c8SRaju Rangoju tdata->pt.type = htons(ETH_P_IP); 115862a64c8SRaju Rangoju tdata->pt.func = xgbe_test_loopback_validate; 116862a64c8SRaju Rangoju tdata->pt.dev = pdata->netdev; 117862a64c8SRaju Rangoju tdata->pt.af_packet_priv = tdata; 118862a64c8SRaju Rangoju tdata->packet = attr; 119862a64c8SRaju Rangoju 120862a64c8SRaju Rangoju dev_add_pack(&tdata->pt); 121862a64c8SRaju Rangoju 122862a64c8SRaju Rangoju skb = net_test_get_skb(pdata->netdev, xgbe_test_id, attr); 123862a64c8SRaju Rangoju if (!skb) { 124862a64c8SRaju Rangoju ret = -ENOMEM; 125862a64c8SRaju Rangoju goto cleanup; 126862a64c8SRaju Rangoju } 127862a64c8SRaju Rangoju 128862a64c8SRaju Rangoju xgbe_test_id++; 129862a64c8SRaju Rangoju ret = dev_direct_xmit(skb, attr->queue_mapping); 130862a64c8SRaju Rangoju if (ret) 131862a64c8SRaju Rangoju goto cleanup; 132862a64c8SRaju Rangoju 133862a64c8SRaju Rangoju if (!attr->timeout) 134862a64c8SRaju Rangoju attr->timeout = NET_LB_TIMEOUT; 135862a64c8SRaju Rangoju 136862a64c8SRaju Rangoju wait_for_completion_timeout(&tdata->comp, attr->timeout); 137862a64c8SRaju Rangoju ret = tdata->ok ? 0 : -ETIMEDOUT; 138862a64c8SRaju Rangoju 139862a64c8SRaju Rangoju if (ret) 140862a64c8SRaju Rangoju netdev_err(pdata->netdev, "Response timedout: ret %d\n", ret); 141862a64c8SRaju Rangoju cleanup: 142862a64c8SRaju Rangoju dev_remove_pack(&tdata->pt); 143862a64c8SRaju Rangoju kfree(tdata); 144862a64c8SRaju Rangoju return ret; 145862a64c8SRaju Rangoju } 146862a64c8SRaju Rangoju 147862a64c8SRaju Rangoju static int xgbe_test_mac_loopback(struct xgbe_prv_data *pdata) 148862a64c8SRaju Rangoju { 149862a64c8SRaju Rangoju struct net_packet_attrs attr = {}; 150862a64c8SRaju Rangoju 151862a64c8SRaju Rangoju attr.dst = pdata->netdev->dev_addr; 152862a64c8SRaju Rangoju return __xgbe_test_loopback(pdata, &attr); 153862a64c8SRaju Rangoju } 154862a64c8SRaju Rangoju 155*42b06fccSRaju Rangoju static int xgbe_test_phy_loopback(struct xgbe_prv_data *pdata) 156*42b06fccSRaju Rangoju { 157*42b06fccSRaju Rangoju struct net_packet_attrs attr = {}; 158*42b06fccSRaju Rangoju int ret; 159*42b06fccSRaju Rangoju 160*42b06fccSRaju Rangoju if (!pdata->netdev->phydev) { 161*42b06fccSRaju Rangoju netdev_err(pdata->netdev, "phydev not found: cannot start PHY loopback test\n"); 162*42b06fccSRaju Rangoju return -EOPNOTSUPP; 163*42b06fccSRaju Rangoju } 164*42b06fccSRaju Rangoju 165*42b06fccSRaju Rangoju ret = phy_loopback(pdata->netdev->phydev, true, 0); 166*42b06fccSRaju Rangoju if (ret) 167*42b06fccSRaju Rangoju return ret; 168*42b06fccSRaju Rangoju 169*42b06fccSRaju Rangoju attr.dst = pdata->netdev->dev_addr; 170*42b06fccSRaju Rangoju ret = __xgbe_test_loopback(pdata, &attr); 171*42b06fccSRaju Rangoju 172*42b06fccSRaju Rangoju phy_loopback(pdata->netdev->phydev, false, 0); 173*42b06fccSRaju Rangoju return ret; 174*42b06fccSRaju Rangoju } 175*42b06fccSRaju Rangoju 176862a64c8SRaju Rangoju static const struct xgbe_test xgbe_selftests[] = { 177862a64c8SRaju Rangoju { 178862a64c8SRaju Rangoju .name = "MAC Loopback ", 179862a64c8SRaju Rangoju .lb = XGBE_LOOPBACK_MAC, 180862a64c8SRaju Rangoju .fn = xgbe_test_mac_loopback, 181*42b06fccSRaju Rangoju }, { 182*42b06fccSRaju Rangoju .name = "PHY Loopback ", 183*42b06fccSRaju Rangoju .lb = XGBE_LOOPBACK_NONE, 184*42b06fccSRaju Rangoju .fn = xgbe_test_phy_loopback, 185862a64c8SRaju Rangoju }, 186862a64c8SRaju Rangoju }; 187862a64c8SRaju Rangoju 188862a64c8SRaju Rangoju void xgbe_selftest_run(struct net_device *dev, 189862a64c8SRaju Rangoju struct ethtool_test *etest, u64 *buf) 190862a64c8SRaju Rangoju { 191862a64c8SRaju Rangoju struct xgbe_prv_data *pdata = netdev_priv(dev); 192862a64c8SRaju Rangoju int count = xgbe_selftest_get_count(pdata); 193862a64c8SRaju Rangoju int i, ret; 194862a64c8SRaju Rangoju 195862a64c8SRaju Rangoju memset(buf, 0, sizeof(*buf) * count); 196862a64c8SRaju Rangoju xgbe_test_id = 0; 197862a64c8SRaju Rangoju 198862a64c8SRaju Rangoju if (etest->flags != ETH_TEST_FL_OFFLINE) { 199862a64c8SRaju Rangoju netdev_err(pdata->netdev, "Only offline tests are supported\n"); 200862a64c8SRaju Rangoju etest->flags |= ETH_TEST_FL_FAILED; 201862a64c8SRaju Rangoju return; 202862a64c8SRaju Rangoju } else if (!netif_carrier_ok(dev)) { 203862a64c8SRaju Rangoju netdev_err(pdata->netdev, 204862a64c8SRaju Rangoju "Invalid link, cannot execute tests\n"); 205862a64c8SRaju Rangoju etest->flags |= ETH_TEST_FL_FAILED; 206862a64c8SRaju Rangoju return; 207862a64c8SRaju Rangoju } 208862a64c8SRaju Rangoju 209862a64c8SRaju Rangoju /* Wait for queues drain */ 210862a64c8SRaju Rangoju msleep(200); 211862a64c8SRaju Rangoju 212862a64c8SRaju Rangoju for (i = 0; i < count; i++) { 213862a64c8SRaju Rangoju ret = 0; 214862a64c8SRaju Rangoju 215862a64c8SRaju Rangoju switch (xgbe_selftests[i].lb) { 216*42b06fccSRaju Rangoju case XGBE_LOOPBACK_PHY: 217*42b06fccSRaju Rangoju ret = -EOPNOTSUPP; 218*42b06fccSRaju Rangoju if (dev->phydev) 219*42b06fccSRaju Rangoju ret = phy_loopback(dev->phydev, true, 0); 220*42b06fccSRaju Rangoju if (!ret) 221*42b06fccSRaju Rangoju break; 222*42b06fccSRaju Rangoju fallthrough; 223862a64c8SRaju Rangoju case XGBE_LOOPBACK_MAC: 224862a64c8SRaju Rangoju ret = xgbe_enable_mac_loopback(pdata); 225862a64c8SRaju Rangoju break; 226862a64c8SRaju Rangoju case XGBE_LOOPBACK_NONE: 227862a64c8SRaju Rangoju break; 228862a64c8SRaju Rangoju default: 229862a64c8SRaju Rangoju ret = -EOPNOTSUPP; 230862a64c8SRaju Rangoju break; 231862a64c8SRaju Rangoju } 232862a64c8SRaju Rangoju 233862a64c8SRaju Rangoju /* 234862a64c8SRaju Rangoju * First tests will always be MAC / PHY loopback. 235862a64c8SRaju Rangoju * If any of them is not supported we abort earlier. 236862a64c8SRaju Rangoju */ 237862a64c8SRaju Rangoju if (ret) { 238862a64c8SRaju Rangoju netdev_err(pdata->netdev, "Loopback not supported\n"); 239862a64c8SRaju Rangoju etest->flags |= ETH_TEST_FL_FAILED; 240862a64c8SRaju Rangoju break; 241862a64c8SRaju Rangoju } 242862a64c8SRaju Rangoju 243862a64c8SRaju Rangoju ret = xgbe_selftests[i].fn(pdata); 244862a64c8SRaju Rangoju if (ret && (ret != -EOPNOTSUPP)) 245862a64c8SRaju Rangoju etest->flags |= ETH_TEST_FL_FAILED; 246862a64c8SRaju Rangoju buf[i] = ret; 247862a64c8SRaju Rangoju 248862a64c8SRaju Rangoju switch (xgbe_selftests[i].lb) { 249*42b06fccSRaju Rangoju case XGBE_LOOPBACK_PHY: 250*42b06fccSRaju Rangoju ret = -EOPNOTSUPP; 251*42b06fccSRaju Rangoju if (dev->phydev) 252*42b06fccSRaju Rangoju ret = phy_loopback(dev->phydev, false, 0); 253*42b06fccSRaju Rangoju if (!ret) 254*42b06fccSRaju Rangoju break; 255*42b06fccSRaju Rangoju fallthrough; 256862a64c8SRaju Rangoju case XGBE_LOOPBACK_MAC: 257862a64c8SRaju Rangoju xgbe_disable_mac_loopback(pdata); 258862a64c8SRaju Rangoju break; 259862a64c8SRaju Rangoju default: 260862a64c8SRaju Rangoju break; 261862a64c8SRaju Rangoju } 262862a64c8SRaju Rangoju } 263862a64c8SRaju Rangoju } 264862a64c8SRaju Rangoju 265862a64c8SRaju Rangoju void xgbe_selftest_get_strings(struct xgbe_prv_data *pdata, u8 *data) 266862a64c8SRaju Rangoju { 267862a64c8SRaju Rangoju u8 *p = data; 268862a64c8SRaju Rangoju int i; 269862a64c8SRaju Rangoju 270862a64c8SRaju Rangoju for (i = 0; i < xgbe_selftest_get_count(pdata); i++) 271862a64c8SRaju Rangoju ethtool_puts(&p, xgbe_selftests[i].name); 272862a64c8SRaju Rangoju } 273862a64c8SRaju Rangoju 274862a64c8SRaju Rangoju int xgbe_selftest_get_count(struct xgbe_prv_data *pdata) 275862a64c8SRaju Rangoju { 276862a64c8SRaju Rangoju return ARRAY_SIZE(xgbe_selftests); 277862a64c8SRaju Rangoju } 278