1 // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause)
2 /*
3 * Copyright (c) 2014-2025, Advanced Micro Devices, Inc.
4 * Copyright (c) 2014, Synopsys, Inc.
5 * All rights reserved
6 *
7 * Author: Raju Rangoju <Raju.Rangoju@amd.com>
8 */
9 #include <linux/crc32.h>
10 #include <linux/ip.h>
11 #include <linux/udp.h>
12 #include <net/tcp.h>
13 #include <net/udp.h>
14 #include <net/checksum.h>
15 #include <net/selftests.h>
16
17 #include "xgbe.h"
18 #include "xgbe-common.h"
19
20 #define XGBE_LOOPBACK_NONE 0
21 #define XGBE_LOOPBACK_MAC 1
22 #define XGBE_LOOPBACK_PHY 2
23
24 struct xgbe_test {
25 char name[ETH_GSTRING_LEN];
26 int lb;
27 int (*fn)(struct xgbe_prv_data *pdata);
28 };
29
30 static u8 xgbe_test_id;
31
xgbe_test_loopback_validate(struct sk_buff * skb,struct net_device * ndev,struct packet_type * pt,struct net_device * orig_ndev)32 static int xgbe_test_loopback_validate(struct sk_buff *skb,
33 struct net_device *ndev,
34 struct packet_type *pt,
35 struct net_device *orig_ndev)
36 {
37 struct net_test_priv *tdata = pt->af_packet_priv;
38 const unsigned char *dst = tdata->packet->dst;
39 const unsigned char *src = tdata->packet->src;
40 struct netsfhdr *hdr;
41 struct ethhdr *eh;
42 struct tcphdr *th;
43 struct udphdr *uh;
44 struct iphdr *ih;
45 int eat;
46
47 skb = skb_unshare(skb, GFP_ATOMIC);
48 if (!skb)
49 goto out;
50
51 eat = (skb->tail + skb->data_len) - skb->end;
52 if (eat > 0 && skb_shared(skb)) {
53 skb = skb_share_check(skb, GFP_ATOMIC);
54 if (!skb)
55 goto out;
56 }
57
58 if (skb_linearize(skb))
59 goto out;
60
61 if (skb_headlen(skb) < (NET_TEST_PKT_SIZE - ETH_HLEN))
62 goto out;
63
64 eh = (struct ethhdr *)skb_mac_header(skb);
65 if (dst) {
66 if (!ether_addr_equal_unaligned(eh->h_dest, dst))
67 goto out;
68 }
69 if (src) {
70 if (!ether_addr_equal_unaligned(eh->h_source, src))
71 goto out;
72 }
73
74 ih = ip_hdr(skb);
75
76 if (tdata->packet->tcp) {
77 if (ih->protocol != IPPROTO_TCP)
78 goto out;
79
80 th = (struct tcphdr *)((u8 *)ih + 4 * ih->ihl);
81 if (th->dest != htons(tdata->packet->dport))
82 goto out;
83
84 hdr = (struct netsfhdr *)((u8 *)th + sizeof(*th));
85 } else {
86 if (ih->protocol != IPPROTO_UDP)
87 goto out;
88
89 uh = (struct udphdr *)((u8 *)ih + 4 * ih->ihl);
90 if (uh->dest != htons(tdata->packet->dport))
91 goto out;
92
93 hdr = (struct netsfhdr *)((u8 *)uh + sizeof(*uh));
94 }
95
96 if (hdr->magic != cpu_to_be64(NET_TEST_PKT_MAGIC))
97 goto out;
98 if (tdata->packet->id != hdr->id)
99 goto out;
100
101 tdata->ok = true;
102 complete(&tdata->comp);
103 out:
104 kfree_skb(skb);
105 return 0;
106 }
107
__xgbe_test_loopback(struct xgbe_prv_data * pdata,struct net_packet_attrs * attr)108 static int __xgbe_test_loopback(struct xgbe_prv_data *pdata,
109 struct net_packet_attrs *attr)
110 {
111 struct net_test_priv *tdata;
112 struct sk_buff *skb = NULL;
113 int ret = 0;
114
115 tdata = kzalloc_obj(*tdata);
116 if (!tdata)
117 return -ENOMEM;
118
119 tdata->ok = false;
120 init_completion(&tdata->comp);
121
122 tdata->pt.type = htons(ETH_P_IP);
123 tdata->pt.func = xgbe_test_loopback_validate;
124 tdata->pt.dev = pdata->netdev;
125 tdata->pt.af_packet_priv = tdata;
126 tdata->packet = attr;
127
128 dev_add_pack(&tdata->pt);
129
130 skb = net_test_get_skb(pdata->netdev, xgbe_test_id, attr);
131 if (!skb) {
132 ret = -ENOMEM;
133 goto cleanup;
134 }
135
136 xgbe_test_id++;
137 ret = dev_direct_xmit(skb, attr->queue_mapping);
138 if (ret)
139 goto cleanup;
140
141 if (!attr->timeout)
142 attr->timeout = NET_LB_TIMEOUT;
143
144 wait_for_completion_timeout(&tdata->comp, attr->timeout);
145 ret = tdata->ok ? 0 : -ETIMEDOUT;
146
147 if (ret)
148 netdev_err(pdata->netdev, "Response timedout: ret %d\n", ret);
149 cleanup:
150 dev_remove_pack(&tdata->pt);
151 kfree(tdata);
152 return ret;
153 }
154
xgbe_test_mac_loopback(struct xgbe_prv_data * pdata)155 static int xgbe_test_mac_loopback(struct xgbe_prv_data *pdata)
156 {
157 struct net_packet_attrs attr = {};
158
159 attr.dst = pdata->netdev->dev_addr;
160 return __xgbe_test_loopback(pdata, &attr);
161 }
162
xgbe_test_phy_loopback(struct xgbe_prv_data * pdata)163 static int xgbe_test_phy_loopback(struct xgbe_prv_data *pdata)
164 {
165 struct net_packet_attrs attr = {};
166 int ret;
167
168 if (!pdata->netdev->phydev) {
169 netdev_err(pdata->netdev, "phydev not found: cannot start PHY loopback test\n");
170 return -EOPNOTSUPP;
171 }
172
173 ret = phy_loopback(pdata->netdev->phydev, true, 0);
174 if (ret)
175 return ret;
176
177 attr.dst = pdata->netdev->dev_addr;
178 ret = __xgbe_test_loopback(pdata, &attr);
179
180 phy_loopback(pdata->netdev->phydev, false, 0);
181 return ret;
182 }
183
xgbe_test_sph(struct xgbe_prv_data * pdata)184 static int xgbe_test_sph(struct xgbe_prv_data *pdata)
185 {
186 struct net_packet_attrs attr = {};
187 unsigned long cnt_end, cnt_start;
188 int ret;
189
190 cnt_start = pdata->ext_stats.rx_split_header_packets;
191
192 if (!pdata->sph) {
193 netdev_err(pdata->netdev, "Split Header not enabled\n");
194 return -EOPNOTSUPP;
195 }
196
197 /* UDP test */
198 attr.dst = pdata->netdev->dev_addr;
199 attr.tcp = false;
200
201 ret = __xgbe_test_loopback(pdata, &attr);
202 if (ret)
203 return ret;
204
205 cnt_end = pdata->ext_stats.rx_split_header_packets;
206 if (cnt_end <= cnt_start)
207 return -EINVAL;
208
209 /* TCP test */
210 cnt_start = cnt_end;
211
212 attr.dst = pdata->netdev->dev_addr;
213 attr.tcp = true;
214
215 ret = __xgbe_test_loopback(pdata, &attr);
216 if (ret)
217 return ret;
218
219 cnt_end = pdata->ext_stats.rx_split_header_packets;
220 if (cnt_end <= cnt_start)
221 return -EINVAL;
222
223 return 0;
224 }
225
xgbe_test_jumbo(struct xgbe_prv_data * pdata)226 static int xgbe_test_jumbo(struct xgbe_prv_data *pdata)
227 {
228 struct net_packet_attrs attr = {};
229 int size = pdata->rx_buf_size;
230
231 attr.dst = pdata->netdev->dev_addr;
232 attr.max_size = size - ETH_FCS_LEN;
233
234 return __xgbe_test_loopback(pdata, &attr);
235 }
236
237 static const struct xgbe_test xgbe_selftests[] = {
238 {
239 .name = "MAC Loopback ",
240 .lb = XGBE_LOOPBACK_MAC,
241 .fn = xgbe_test_mac_loopback,
242 }, {
243 .name = "PHY Loopback ",
244 .lb = XGBE_LOOPBACK_NONE,
245 .fn = xgbe_test_phy_loopback,
246 }, {
247 .name = "Split Header ",
248 .lb = XGBE_LOOPBACK_PHY,
249 .fn = xgbe_test_sph,
250 }, {
251 .name = "Jumbo Frame ",
252 .lb = XGBE_LOOPBACK_PHY,
253 .fn = xgbe_test_jumbo,
254 },
255 };
256
xgbe_selftest_run(struct net_device * dev,struct ethtool_test * etest,u64 * buf)257 void xgbe_selftest_run(struct net_device *dev,
258 struct ethtool_test *etest, u64 *buf)
259 {
260 struct xgbe_prv_data *pdata = netdev_priv(dev);
261 int count = xgbe_selftest_get_count(pdata);
262 int i, ret;
263
264 memset(buf, 0, sizeof(*buf) * count);
265 xgbe_test_id = 0;
266
267 if (etest->flags != ETH_TEST_FL_OFFLINE) {
268 netdev_err(pdata->netdev, "Only offline tests are supported\n");
269 etest->flags |= ETH_TEST_FL_FAILED;
270 return;
271 } else if (!netif_carrier_ok(dev)) {
272 netdev_err(pdata->netdev,
273 "Invalid link, cannot execute tests\n");
274 etest->flags |= ETH_TEST_FL_FAILED;
275 return;
276 }
277
278 /* Wait for queues drain */
279 msleep(200);
280
281 for (i = 0; i < count; i++) {
282 ret = 0;
283
284 switch (xgbe_selftests[i].lb) {
285 case XGBE_LOOPBACK_PHY:
286 ret = -EOPNOTSUPP;
287 if (dev->phydev)
288 ret = phy_loopback(dev->phydev, true, 0);
289 if (!ret)
290 break;
291 fallthrough;
292 case XGBE_LOOPBACK_MAC:
293 ret = xgbe_enable_mac_loopback(pdata);
294 break;
295 case XGBE_LOOPBACK_NONE:
296 break;
297 default:
298 ret = -EOPNOTSUPP;
299 break;
300 }
301
302 /*
303 * First tests will always be MAC / PHY loopback.
304 * If any of them is not supported we abort earlier.
305 */
306 if (ret) {
307 netdev_err(pdata->netdev, "Loopback not supported\n");
308 etest->flags |= ETH_TEST_FL_FAILED;
309 break;
310 }
311
312 ret = xgbe_selftests[i].fn(pdata);
313 if (ret && (ret != -EOPNOTSUPP))
314 etest->flags |= ETH_TEST_FL_FAILED;
315 buf[i] = ret;
316
317 switch (xgbe_selftests[i].lb) {
318 case XGBE_LOOPBACK_PHY:
319 ret = -EOPNOTSUPP;
320 if (dev->phydev)
321 ret = phy_loopback(dev->phydev, false, 0);
322 if (!ret)
323 break;
324 fallthrough;
325 case XGBE_LOOPBACK_MAC:
326 xgbe_disable_mac_loopback(pdata);
327 break;
328 default:
329 break;
330 }
331 }
332 }
333
xgbe_selftest_get_strings(struct xgbe_prv_data * pdata,u8 * data)334 void xgbe_selftest_get_strings(struct xgbe_prv_data *pdata, u8 *data)
335 {
336 u8 *p = data;
337 int i;
338
339 for (i = 0; i < xgbe_selftest_get_count(pdata); i++)
340 ethtool_puts(&p, xgbe_selftests[i].name);
341 }
342
xgbe_selftest_get_count(struct xgbe_prv_data * pdata)343 int xgbe_selftest_get_count(struct xgbe_prv_data *pdata)
344 {
345 return ARRAY_SIZE(xgbe_selftests);
346 }
347