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; 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 15542b06fccSRaju Rangoju static int xgbe_test_phy_loopback(struct xgbe_prv_data *pdata) 15642b06fccSRaju Rangoju { 15742b06fccSRaju Rangoju struct net_packet_attrs attr = {}; 15842b06fccSRaju Rangoju int ret; 15942b06fccSRaju Rangoju 16042b06fccSRaju Rangoju if (!pdata->netdev->phydev) { 16142b06fccSRaju Rangoju netdev_err(pdata->netdev, "phydev not found: cannot start PHY loopback test\n"); 16242b06fccSRaju Rangoju return -EOPNOTSUPP; 16342b06fccSRaju Rangoju } 16442b06fccSRaju Rangoju 16542b06fccSRaju Rangoju ret = phy_loopback(pdata->netdev->phydev, true, 0); 16642b06fccSRaju Rangoju if (ret) 16742b06fccSRaju Rangoju return ret; 16842b06fccSRaju Rangoju 16942b06fccSRaju Rangoju attr.dst = pdata->netdev->dev_addr; 17042b06fccSRaju Rangoju ret = __xgbe_test_loopback(pdata, &attr); 17142b06fccSRaju Rangoju 17242b06fccSRaju Rangoju phy_loopback(pdata->netdev->phydev, false, 0); 17342b06fccSRaju Rangoju return ret; 17442b06fccSRaju Rangoju } 17542b06fccSRaju Rangoju 176*d7735c6bSRaju Rangoju static int xgbe_test_sph(struct xgbe_prv_data *pdata) 177*d7735c6bSRaju Rangoju { 178*d7735c6bSRaju Rangoju struct net_packet_attrs attr = {}; 179*d7735c6bSRaju Rangoju unsigned long cnt_end, cnt_start; 180*d7735c6bSRaju Rangoju int ret; 181*d7735c6bSRaju Rangoju 182*d7735c6bSRaju Rangoju cnt_start = pdata->ext_stats.rx_split_header_packets; 183*d7735c6bSRaju Rangoju 184*d7735c6bSRaju Rangoju if (!pdata->sph) { 185*d7735c6bSRaju Rangoju netdev_err(pdata->netdev, "Split Header not enabled\n"); 186*d7735c6bSRaju Rangoju return -EOPNOTSUPP; 187*d7735c6bSRaju Rangoju } 188*d7735c6bSRaju Rangoju 189*d7735c6bSRaju Rangoju /* UDP test */ 190*d7735c6bSRaju Rangoju attr.dst = pdata->netdev->dev_addr; 191*d7735c6bSRaju Rangoju attr.tcp = false; 192*d7735c6bSRaju Rangoju 193*d7735c6bSRaju Rangoju ret = __xgbe_test_loopback(pdata, &attr); 194*d7735c6bSRaju Rangoju if (ret) 195*d7735c6bSRaju Rangoju return ret; 196*d7735c6bSRaju Rangoju 197*d7735c6bSRaju Rangoju cnt_end = pdata->ext_stats.rx_split_header_packets; 198*d7735c6bSRaju Rangoju if (cnt_end <= cnt_start) 199*d7735c6bSRaju Rangoju return -EINVAL; 200*d7735c6bSRaju Rangoju 201*d7735c6bSRaju Rangoju /* TCP test */ 202*d7735c6bSRaju Rangoju cnt_start = cnt_end; 203*d7735c6bSRaju Rangoju 204*d7735c6bSRaju Rangoju attr.dst = pdata->netdev->dev_addr; 205*d7735c6bSRaju Rangoju attr.tcp = true; 206*d7735c6bSRaju Rangoju 207*d7735c6bSRaju Rangoju ret = __xgbe_test_loopback(pdata, &attr); 208*d7735c6bSRaju Rangoju if (ret) 209*d7735c6bSRaju Rangoju return ret; 210*d7735c6bSRaju Rangoju 211*d7735c6bSRaju Rangoju cnt_end = pdata->ext_stats.rx_split_header_packets; 212*d7735c6bSRaju Rangoju if (cnt_end <= cnt_start) 213*d7735c6bSRaju Rangoju return -EINVAL; 214*d7735c6bSRaju Rangoju 215*d7735c6bSRaju Rangoju return 0; 216*d7735c6bSRaju Rangoju } 217*d7735c6bSRaju Rangoju 218862a64c8SRaju Rangoju static const struct xgbe_test xgbe_selftests[] = { 219862a64c8SRaju Rangoju { 220862a64c8SRaju Rangoju .name = "MAC Loopback ", 221862a64c8SRaju Rangoju .lb = XGBE_LOOPBACK_MAC, 222862a64c8SRaju Rangoju .fn = xgbe_test_mac_loopback, 22342b06fccSRaju Rangoju }, { 22442b06fccSRaju Rangoju .name = "PHY Loopback ", 22542b06fccSRaju Rangoju .lb = XGBE_LOOPBACK_NONE, 22642b06fccSRaju Rangoju .fn = xgbe_test_phy_loopback, 227*d7735c6bSRaju Rangoju }, { 228*d7735c6bSRaju Rangoju .name = "Split Header ", 229*d7735c6bSRaju Rangoju .lb = XGBE_LOOPBACK_PHY, 230*d7735c6bSRaju Rangoju .fn = xgbe_test_sph, 231862a64c8SRaju Rangoju }, 232862a64c8SRaju Rangoju }; 233862a64c8SRaju Rangoju 234862a64c8SRaju Rangoju void xgbe_selftest_run(struct net_device *dev, 235862a64c8SRaju Rangoju struct ethtool_test *etest, u64 *buf) 236862a64c8SRaju Rangoju { 237862a64c8SRaju Rangoju struct xgbe_prv_data *pdata = netdev_priv(dev); 238862a64c8SRaju Rangoju int count = xgbe_selftest_get_count(pdata); 239862a64c8SRaju Rangoju int i, ret; 240862a64c8SRaju Rangoju 241862a64c8SRaju Rangoju memset(buf, 0, sizeof(*buf) * count); 242862a64c8SRaju Rangoju xgbe_test_id = 0; 243862a64c8SRaju Rangoju 244862a64c8SRaju Rangoju if (etest->flags != ETH_TEST_FL_OFFLINE) { 245862a64c8SRaju Rangoju netdev_err(pdata->netdev, "Only offline tests are supported\n"); 246862a64c8SRaju Rangoju etest->flags |= ETH_TEST_FL_FAILED; 247862a64c8SRaju Rangoju return; 248862a64c8SRaju Rangoju } else if (!netif_carrier_ok(dev)) { 249862a64c8SRaju Rangoju netdev_err(pdata->netdev, 250862a64c8SRaju Rangoju "Invalid link, cannot execute tests\n"); 251862a64c8SRaju Rangoju etest->flags |= ETH_TEST_FL_FAILED; 252862a64c8SRaju Rangoju return; 253862a64c8SRaju Rangoju } 254862a64c8SRaju Rangoju 255862a64c8SRaju Rangoju /* Wait for queues drain */ 256862a64c8SRaju Rangoju msleep(200); 257862a64c8SRaju Rangoju 258862a64c8SRaju Rangoju for (i = 0; i < count; i++) { 259862a64c8SRaju Rangoju ret = 0; 260862a64c8SRaju Rangoju 261862a64c8SRaju Rangoju switch (xgbe_selftests[i].lb) { 26242b06fccSRaju Rangoju case XGBE_LOOPBACK_PHY: 26342b06fccSRaju Rangoju ret = -EOPNOTSUPP; 26442b06fccSRaju Rangoju if (dev->phydev) 26542b06fccSRaju Rangoju ret = phy_loopback(dev->phydev, true, 0); 26642b06fccSRaju Rangoju if (!ret) 26742b06fccSRaju Rangoju break; 26842b06fccSRaju Rangoju fallthrough; 269862a64c8SRaju Rangoju case XGBE_LOOPBACK_MAC: 270862a64c8SRaju Rangoju ret = xgbe_enable_mac_loopback(pdata); 271862a64c8SRaju Rangoju break; 272862a64c8SRaju Rangoju case XGBE_LOOPBACK_NONE: 273862a64c8SRaju Rangoju break; 274862a64c8SRaju Rangoju default: 275862a64c8SRaju Rangoju ret = -EOPNOTSUPP; 276862a64c8SRaju Rangoju break; 277862a64c8SRaju Rangoju } 278862a64c8SRaju Rangoju 279862a64c8SRaju Rangoju /* 280862a64c8SRaju Rangoju * First tests will always be MAC / PHY loopback. 281862a64c8SRaju Rangoju * If any of them is not supported we abort earlier. 282862a64c8SRaju Rangoju */ 283862a64c8SRaju Rangoju if (ret) { 284862a64c8SRaju Rangoju netdev_err(pdata->netdev, "Loopback not supported\n"); 285862a64c8SRaju Rangoju etest->flags |= ETH_TEST_FL_FAILED; 286862a64c8SRaju Rangoju break; 287862a64c8SRaju Rangoju } 288862a64c8SRaju Rangoju 289862a64c8SRaju Rangoju ret = xgbe_selftests[i].fn(pdata); 290862a64c8SRaju Rangoju if (ret && (ret != -EOPNOTSUPP)) 291862a64c8SRaju Rangoju etest->flags |= ETH_TEST_FL_FAILED; 292862a64c8SRaju Rangoju buf[i] = ret; 293862a64c8SRaju Rangoju 294862a64c8SRaju Rangoju switch (xgbe_selftests[i].lb) { 29542b06fccSRaju Rangoju case XGBE_LOOPBACK_PHY: 29642b06fccSRaju Rangoju ret = -EOPNOTSUPP; 29742b06fccSRaju Rangoju if (dev->phydev) 29842b06fccSRaju Rangoju ret = phy_loopback(dev->phydev, false, 0); 29942b06fccSRaju Rangoju if (!ret) 30042b06fccSRaju Rangoju break; 30142b06fccSRaju Rangoju fallthrough; 302862a64c8SRaju Rangoju case XGBE_LOOPBACK_MAC: 303862a64c8SRaju Rangoju xgbe_disable_mac_loopback(pdata); 304862a64c8SRaju Rangoju break; 305862a64c8SRaju Rangoju default: 306862a64c8SRaju Rangoju break; 307862a64c8SRaju Rangoju } 308862a64c8SRaju Rangoju } 309862a64c8SRaju Rangoju } 310862a64c8SRaju Rangoju 311862a64c8SRaju Rangoju void xgbe_selftest_get_strings(struct xgbe_prv_data *pdata, u8 *data) 312862a64c8SRaju Rangoju { 313862a64c8SRaju Rangoju u8 *p = data; 314862a64c8SRaju Rangoju int i; 315862a64c8SRaju Rangoju 316862a64c8SRaju Rangoju for (i = 0; i < xgbe_selftest_get_count(pdata); i++) 317862a64c8SRaju Rangoju ethtool_puts(&p, xgbe_selftests[i].name); 318862a64c8SRaju Rangoju } 319862a64c8SRaju Rangoju 320862a64c8SRaju Rangoju int xgbe_selftest_get_count(struct xgbe_prv_data *pdata) 321862a64c8SRaju Rangoju { 322862a64c8SRaju Rangoju return ARRAY_SIZE(xgbe_selftests); 323862a64c8SRaju Rangoju } 324