xref: /linux/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c (revision d7735c6bb2310f7ca8235af7f946e6c8716cdb5e)
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