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