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 2242b06fccSRaju 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; 45*9c11b6b1SRaju Rangoju int eat; 46862a64c8SRaju Rangoju 47862a64c8SRaju Rangoju skb = skb_unshare(skb, GFP_ATOMIC); 48862a64c8SRaju Rangoju if (!skb) 49862a64c8SRaju Rangoju goto out; 50862a64c8SRaju Rangoju 51*9c11b6b1SRaju Rangoju eat = (skb->tail + skb->data_len) - skb->end; 52*9c11b6b1SRaju Rangoju if (eat > 0 && skb_shared(skb)) { 53*9c11b6b1SRaju Rangoju skb = skb_share_check(skb, GFP_ATOMIC); 54*9c11b6b1SRaju Rangoju if (!skb) 55*9c11b6b1SRaju Rangoju goto out; 56*9c11b6b1SRaju Rangoju } 57*9c11b6b1SRaju Rangoju 58862a64c8SRaju Rangoju if (skb_linearize(skb)) 59862a64c8SRaju Rangoju goto out; 60862a64c8SRaju Rangoju 61862a64c8SRaju Rangoju if (skb_headlen(skb) < (NET_TEST_PKT_SIZE - ETH_HLEN)) 62862a64c8SRaju Rangoju goto out; 63862a64c8SRaju Rangoju 64862a64c8SRaju Rangoju eh = (struct ethhdr *)skb_mac_header(skb); 65862a64c8SRaju Rangoju if (dst) { 66862a64c8SRaju Rangoju if (!ether_addr_equal_unaligned(eh->h_dest, dst)) 67862a64c8SRaju Rangoju goto out; 68862a64c8SRaju Rangoju } 69862a64c8SRaju Rangoju if (src) { 70862a64c8SRaju Rangoju if (!ether_addr_equal_unaligned(eh->h_source, src)) 71862a64c8SRaju Rangoju goto out; 72862a64c8SRaju Rangoju } 73862a64c8SRaju Rangoju 74862a64c8SRaju Rangoju ih = ip_hdr(skb); 75862a64c8SRaju Rangoju 76862a64c8SRaju Rangoju if (tdata->packet->tcp) { 77862a64c8SRaju Rangoju if (ih->protocol != IPPROTO_TCP) 78862a64c8SRaju Rangoju goto out; 79862a64c8SRaju Rangoju 80862a64c8SRaju Rangoju th = (struct tcphdr *)((u8 *)ih + 4 * ih->ihl); 81862a64c8SRaju Rangoju if (th->dest != htons(tdata->packet->dport)) 82862a64c8SRaju Rangoju goto out; 83862a64c8SRaju Rangoju 84862a64c8SRaju Rangoju hdr = (struct netsfhdr *)((u8 *)th + sizeof(*th)); 85862a64c8SRaju Rangoju } else { 86862a64c8SRaju Rangoju if (ih->protocol != IPPROTO_UDP) 87862a64c8SRaju Rangoju goto out; 88862a64c8SRaju Rangoju 89862a64c8SRaju Rangoju uh = (struct udphdr *)((u8 *)ih + 4 * ih->ihl); 90862a64c8SRaju Rangoju if (uh->dest != htons(tdata->packet->dport)) 91862a64c8SRaju Rangoju goto out; 92862a64c8SRaju Rangoju 93862a64c8SRaju Rangoju hdr = (struct netsfhdr *)((u8 *)uh + sizeof(*uh)); 94862a64c8SRaju Rangoju } 95862a64c8SRaju Rangoju 96862a64c8SRaju Rangoju if (hdr->magic != cpu_to_be64(NET_TEST_PKT_MAGIC)) 97862a64c8SRaju Rangoju goto out; 98862a64c8SRaju Rangoju if (tdata->packet->id != hdr->id) 99862a64c8SRaju Rangoju goto out; 100862a64c8SRaju Rangoju 101862a64c8SRaju Rangoju tdata->ok = true; 102862a64c8SRaju Rangoju complete(&tdata->comp); 103862a64c8SRaju Rangoju out: 104862a64c8SRaju Rangoju kfree_skb(skb); 105862a64c8SRaju Rangoju return 0; 106862a64c8SRaju Rangoju } 107862a64c8SRaju Rangoju 108862a64c8SRaju Rangoju static int __xgbe_test_loopback(struct xgbe_prv_data *pdata, 109862a64c8SRaju Rangoju struct net_packet_attrs *attr) 110862a64c8SRaju Rangoju { 111862a64c8SRaju Rangoju struct net_test_priv *tdata; 112862a64c8SRaju Rangoju struct sk_buff *skb = NULL; 113862a64c8SRaju Rangoju int ret = 0; 114862a64c8SRaju Rangoju 115862a64c8SRaju Rangoju tdata = kzalloc(sizeof(*tdata), GFP_KERNEL); 116862a64c8SRaju Rangoju if (!tdata) 117862a64c8SRaju Rangoju return -ENOMEM; 118862a64c8SRaju Rangoju 119862a64c8SRaju Rangoju tdata->ok = false; 120862a64c8SRaju Rangoju init_completion(&tdata->comp); 121862a64c8SRaju Rangoju 122862a64c8SRaju Rangoju tdata->pt.type = htons(ETH_P_IP); 123862a64c8SRaju Rangoju tdata->pt.func = xgbe_test_loopback_validate; 124862a64c8SRaju Rangoju tdata->pt.dev = pdata->netdev; 125862a64c8SRaju Rangoju tdata->pt.af_packet_priv = tdata; 126862a64c8SRaju Rangoju tdata->packet = attr; 127862a64c8SRaju Rangoju 128862a64c8SRaju Rangoju dev_add_pack(&tdata->pt); 129862a64c8SRaju Rangoju 130862a64c8SRaju Rangoju skb = net_test_get_skb(pdata->netdev, xgbe_test_id, attr); 131862a64c8SRaju Rangoju if (!skb) { 132862a64c8SRaju Rangoju ret = -ENOMEM; 133862a64c8SRaju Rangoju goto cleanup; 134862a64c8SRaju Rangoju } 135862a64c8SRaju Rangoju 136862a64c8SRaju Rangoju xgbe_test_id++; 137862a64c8SRaju Rangoju ret = dev_direct_xmit(skb, attr->queue_mapping); 138862a64c8SRaju Rangoju if (ret) 139862a64c8SRaju Rangoju goto cleanup; 140862a64c8SRaju Rangoju 141862a64c8SRaju Rangoju if (!attr->timeout) 142862a64c8SRaju Rangoju attr->timeout = NET_LB_TIMEOUT; 143862a64c8SRaju Rangoju 144862a64c8SRaju Rangoju wait_for_completion_timeout(&tdata->comp, attr->timeout); 145862a64c8SRaju Rangoju ret = tdata->ok ? 0 : -ETIMEDOUT; 146862a64c8SRaju Rangoju 147862a64c8SRaju Rangoju if (ret) 148862a64c8SRaju Rangoju netdev_err(pdata->netdev, "Response timedout: ret %d\n", ret); 149862a64c8SRaju Rangoju cleanup: 150862a64c8SRaju Rangoju dev_remove_pack(&tdata->pt); 151862a64c8SRaju Rangoju kfree(tdata); 152862a64c8SRaju Rangoju return ret; 153862a64c8SRaju Rangoju } 154862a64c8SRaju Rangoju 155862a64c8SRaju Rangoju static int xgbe_test_mac_loopback(struct xgbe_prv_data *pdata) 156862a64c8SRaju Rangoju { 157862a64c8SRaju Rangoju struct net_packet_attrs attr = {}; 158862a64c8SRaju Rangoju 159862a64c8SRaju Rangoju attr.dst = pdata->netdev->dev_addr; 160862a64c8SRaju Rangoju return __xgbe_test_loopback(pdata, &attr); 161862a64c8SRaju Rangoju } 162862a64c8SRaju Rangoju 16342b06fccSRaju Rangoju static int xgbe_test_phy_loopback(struct xgbe_prv_data *pdata) 16442b06fccSRaju Rangoju { 16542b06fccSRaju Rangoju struct net_packet_attrs attr = {}; 16642b06fccSRaju Rangoju int ret; 16742b06fccSRaju Rangoju 16842b06fccSRaju Rangoju if (!pdata->netdev->phydev) { 16942b06fccSRaju Rangoju netdev_err(pdata->netdev, "phydev not found: cannot start PHY loopback test\n"); 17042b06fccSRaju Rangoju return -EOPNOTSUPP; 17142b06fccSRaju Rangoju } 17242b06fccSRaju Rangoju 17342b06fccSRaju Rangoju ret = phy_loopback(pdata->netdev->phydev, true, 0); 17442b06fccSRaju Rangoju if (ret) 17542b06fccSRaju Rangoju return ret; 17642b06fccSRaju Rangoju 17742b06fccSRaju Rangoju attr.dst = pdata->netdev->dev_addr; 17842b06fccSRaju Rangoju ret = __xgbe_test_loopback(pdata, &attr); 17942b06fccSRaju Rangoju 18042b06fccSRaju Rangoju phy_loopback(pdata->netdev->phydev, false, 0); 18142b06fccSRaju Rangoju return ret; 18242b06fccSRaju Rangoju } 18342b06fccSRaju Rangoju 184d7735c6bSRaju Rangoju static int xgbe_test_sph(struct xgbe_prv_data *pdata) 185d7735c6bSRaju Rangoju { 186d7735c6bSRaju Rangoju struct net_packet_attrs attr = {}; 187d7735c6bSRaju Rangoju unsigned long cnt_end, cnt_start; 188d7735c6bSRaju Rangoju int ret; 189d7735c6bSRaju Rangoju 190d7735c6bSRaju Rangoju cnt_start = pdata->ext_stats.rx_split_header_packets; 191d7735c6bSRaju Rangoju 192d7735c6bSRaju Rangoju if (!pdata->sph) { 193d7735c6bSRaju Rangoju netdev_err(pdata->netdev, "Split Header not enabled\n"); 194d7735c6bSRaju Rangoju return -EOPNOTSUPP; 195d7735c6bSRaju Rangoju } 196d7735c6bSRaju Rangoju 197d7735c6bSRaju Rangoju /* UDP test */ 198d7735c6bSRaju Rangoju attr.dst = pdata->netdev->dev_addr; 199d7735c6bSRaju Rangoju attr.tcp = false; 200d7735c6bSRaju Rangoju 201d7735c6bSRaju Rangoju ret = __xgbe_test_loopback(pdata, &attr); 202d7735c6bSRaju Rangoju if (ret) 203d7735c6bSRaju Rangoju return ret; 204d7735c6bSRaju Rangoju 205d7735c6bSRaju Rangoju cnt_end = pdata->ext_stats.rx_split_header_packets; 206d7735c6bSRaju Rangoju if (cnt_end <= cnt_start) 207d7735c6bSRaju Rangoju return -EINVAL; 208d7735c6bSRaju Rangoju 209d7735c6bSRaju Rangoju /* TCP test */ 210d7735c6bSRaju Rangoju cnt_start = cnt_end; 211d7735c6bSRaju Rangoju 212d7735c6bSRaju Rangoju attr.dst = pdata->netdev->dev_addr; 213d7735c6bSRaju Rangoju attr.tcp = true; 214d7735c6bSRaju Rangoju 215d7735c6bSRaju Rangoju ret = __xgbe_test_loopback(pdata, &attr); 216d7735c6bSRaju Rangoju if (ret) 217d7735c6bSRaju Rangoju return ret; 218d7735c6bSRaju Rangoju 219d7735c6bSRaju Rangoju cnt_end = pdata->ext_stats.rx_split_header_packets; 220d7735c6bSRaju Rangoju if (cnt_end <= cnt_start) 221d7735c6bSRaju Rangoju return -EINVAL; 222d7735c6bSRaju Rangoju 223d7735c6bSRaju Rangoju return 0; 224d7735c6bSRaju Rangoju } 225d7735c6bSRaju Rangoju 226*9c11b6b1SRaju Rangoju static int xgbe_test_jumbo(struct xgbe_prv_data *pdata) 227*9c11b6b1SRaju Rangoju { 228*9c11b6b1SRaju Rangoju struct net_packet_attrs attr = {}; 229*9c11b6b1SRaju Rangoju int size = pdata->rx_buf_size; 230*9c11b6b1SRaju Rangoju 231*9c11b6b1SRaju Rangoju attr.dst = pdata->netdev->dev_addr; 232*9c11b6b1SRaju Rangoju attr.max_size = size - ETH_FCS_LEN; 233*9c11b6b1SRaju Rangoju 234*9c11b6b1SRaju Rangoju return __xgbe_test_loopback(pdata, &attr); 235*9c11b6b1SRaju Rangoju } 236*9c11b6b1SRaju Rangoju 237862a64c8SRaju Rangoju static const struct xgbe_test xgbe_selftests[] = { 238862a64c8SRaju Rangoju { 239862a64c8SRaju Rangoju .name = "MAC Loopback ", 240862a64c8SRaju Rangoju .lb = XGBE_LOOPBACK_MAC, 241862a64c8SRaju Rangoju .fn = xgbe_test_mac_loopback, 24242b06fccSRaju Rangoju }, { 24342b06fccSRaju Rangoju .name = "PHY Loopback ", 24442b06fccSRaju Rangoju .lb = XGBE_LOOPBACK_NONE, 24542b06fccSRaju Rangoju .fn = xgbe_test_phy_loopback, 246d7735c6bSRaju Rangoju }, { 247d7735c6bSRaju Rangoju .name = "Split Header ", 248d7735c6bSRaju Rangoju .lb = XGBE_LOOPBACK_PHY, 249d7735c6bSRaju Rangoju .fn = xgbe_test_sph, 250*9c11b6b1SRaju Rangoju }, { 251*9c11b6b1SRaju Rangoju .name = "Jumbo Frame ", 252*9c11b6b1SRaju Rangoju .lb = XGBE_LOOPBACK_PHY, 253*9c11b6b1SRaju Rangoju .fn = xgbe_test_jumbo, 254862a64c8SRaju Rangoju }, 255862a64c8SRaju Rangoju }; 256862a64c8SRaju Rangoju 257862a64c8SRaju Rangoju void xgbe_selftest_run(struct net_device *dev, 258862a64c8SRaju Rangoju struct ethtool_test *etest, u64 *buf) 259862a64c8SRaju Rangoju { 260862a64c8SRaju Rangoju struct xgbe_prv_data *pdata = netdev_priv(dev); 261862a64c8SRaju Rangoju int count = xgbe_selftest_get_count(pdata); 262862a64c8SRaju Rangoju int i, ret; 263862a64c8SRaju Rangoju 264862a64c8SRaju Rangoju memset(buf, 0, sizeof(*buf) * count); 265862a64c8SRaju Rangoju xgbe_test_id = 0; 266862a64c8SRaju Rangoju 267862a64c8SRaju Rangoju if (etest->flags != ETH_TEST_FL_OFFLINE) { 268862a64c8SRaju Rangoju netdev_err(pdata->netdev, "Only offline tests are supported\n"); 269862a64c8SRaju Rangoju etest->flags |= ETH_TEST_FL_FAILED; 270862a64c8SRaju Rangoju return; 271862a64c8SRaju Rangoju } else if (!netif_carrier_ok(dev)) { 272862a64c8SRaju Rangoju netdev_err(pdata->netdev, 273862a64c8SRaju Rangoju "Invalid link, cannot execute tests\n"); 274862a64c8SRaju Rangoju etest->flags |= ETH_TEST_FL_FAILED; 275862a64c8SRaju Rangoju return; 276862a64c8SRaju Rangoju } 277862a64c8SRaju Rangoju 278862a64c8SRaju Rangoju /* Wait for queues drain */ 279862a64c8SRaju Rangoju msleep(200); 280862a64c8SRaju Rangoju 281862a64c8SRaju Rangoju for (i = 0; i < count; i++) { 282862a64c8SRaju Rangoju ret = 0; 283862a64c8SRaju Rangoju 284862a64c8SRaju Rangoju switch (xgbe_selftests[i].lb) { 28542b06fccSRaju Rangoju case XGBE_LOOPBACK_PHY: 28642b06fccSRaju Rangoju ret = -EOPNOTSUPP; 28742b06fccSRaju Rangoju if (dev->phydev) 28842b06fccSRaju Rangoju ret = phy_loopback(dev->phydev, true, 0); 28942b06fccSRaju Rangoju if (!ret) 29042b06fccSRaju Rangoju break; 29142b06fccSRaju Rangoju fallthrough; 292862a64c8SRaju Rangoju case XGBE_LOOPBACK_MAC: 293862a64c8SRaju Rangoju ret = xgbe_enable_mac_loopback(pdata); 294862a64c8SRaju Rangoju break; 295862a64c8SRaju Rangoju case XGBE_LOOPBACK_NONE: 296862a64c8SRaju Rangoju break; 297862a64c8SRaju Rangoju default: 298862a64c8SRaju Rangoju ret = -EOPNOTSUPP; 299862a64c8SRaju Rangoju break; 300862a64c8SRaju Rangoju } 301862a64c8SRaju Rangoju 302862a64c8SRaju Rangoju /* 303862a64c8SRaju Rangoju * First tests will always be MAC / PHY loopback. 304862a64c8SRaju Rangoju * If any of them is not supported we abort earlier. 305862a64c8SRaju Rangoju */ 306862a64c8SRaju Rangoju if (ret) { 307862a64c8SRaju Rangoju netdev_err(pdata->netdev, "Loopback not supported\n"); 308862a64c8SRaju Rangoju etest->flags |= ETH_TEST_FL_FAILED; 309862a64c8SRaju Rangoju break; 310862a64c8SRaju Rangoju } 311862a64c8SRaju Rangoju 312862a64c8SRaju Rangoju ret = xgbe_selftests[i].fn(pdata); 313862a64c8SRaju Rangoju if (ret && (ret != -EOPNOTSUPP)) 314862a64c8SRaju Rangoju etest->flags |= ETH_TEST_FL_FAILED; 315862a64c8SRaju Rangoju buf[i] = ret; 316862a64c8SRaju Rangoju 317862a64c8SRaju Rangoju switch (xgbe_selftests[i].lb) { 31842b06fccSRaju Rangoju case XGBE_LOOPBACK_PHY: 31942b06fccSRaju Rangoju ret = -EOPNOTSUPP; 32042b06fccSRaju Rangoju if (dev->phydev) 32142b06fccSRaju Rangoju ret = phy_loopback(dev->phydev, false, 0); 32242b06fccSRaju Rangoju if (!ret) 32342b06fccSRaju Rangoju break; 32442b06fccSRaju Rangoju fallthrough; 325862a64c8SRaju Rangoju case XGBE_LOOPBACK_MAC: 326862a64c8SRaju Rangoju xgbe_disable_mac_loopback(pdata); 327862a64c8SRaju Rangoju break; 328862a64c8SRaju Rangoju default: 329862a64c8SRaju Rangoju break; 330862a64c8SRaju Rangoju } 331862a64c8SRaju Rangoju } 332862a64c8SRaju Rangoju } 333862a64c8SRaju Rangoju 334862a64c8SRaju Rangoju void xgbe_selftest_get_strings(struct xgbe_prv_data *pdata, u8 *data) 335862a64c8SRaju Rangoju { 336862a64c8SRaju Rangoju u8 *p = data; 337862a64c8SRaju Rangoju int i; 338862a64c8SRaju Rangoju 339862a64c8SRaju Rangoju for (i = 0; i < xgbe_selftest_get_count(pdata); i++) 340862a64c8SRaju Rangoju ethtool_puts(&p, xgbe_selftests[i].name); 341862a64c8SRaju Rangoju } 342862a64c8SRaju Rangoju 343862a64c8SRaju Rangoju int xgbe_selftest_get_count(struct xgbe_prv_data *pdata) 344862a64c8SRaju Rangoju { 345862a64c8SRaju Rangoju return ARRAY_SIZE(xgbe_selftests); 346862a64c8SRaju Rangoju } 347