1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2022 Garrett D'Amore
26  */
27 
28 #include <sys/types.h>
29 #include <sys/mac.h>
30 #include <sys/softmac_impl.h>
31 
32 typedef struct softmac_capab_ops {
33 	int	(*sc_hcksum_ack)(void *, t_uscalar_t);
34 	int	(*sc_zcopy_ack)(void *, t_uscalar_t);
35 } softmac_capab_ops_t;
36 
37 static int	dl_capab(ldi_handle_t, mblk_t **);
38 static int	softmac_fill_hcksum_ack(void *, t_uscalar_t);
39 static int	softmac_fill_zcopy_ack(void *, t_uscalar_t);
40 static int	softmac_adv_hcksum_ack(void *, t_uscalar_t);
41 static int	softmac_adv_zcopy_ack(void *, t_uscalar_t);
42 static int	softmac_enable_hcksum_ack(void *, t_uscalar_t);
43 static int	softmac_capab_send(softmac_lower_t *, boolean_t);
44 static int	i_capab_ack(mblk_t *, queue_t *, softmac_capab_ops_t *, void *);
45 static int	i_capab_id_ack(mblk_t *, dl_capability_sub_t *, queue_t *,
46     softmac_capab_ops_t *, void *);
47 static int	i_capab_sub_ack(mblk_t *, dl_capability_sub_t *, queue_t *,
48     softmac_capab_ops_t *, void *);
49 static int	i_capab_hcksum_ack(dl_capab_hcksum_t *, queue_t *,
50     softmac_capab_ops_t *, void *);
51 static int	i_capab_zcopy_ack(dl_capab_zerocopy_t *, queue_t *,
52     softmac_capab_ops_t *, void *);
53 static int	i_capab_hcksum_verify(dl_capab_hcksum_t *, queue_t *);
54 static int	i_capab_zcopy_verify(dl_capab_zerocopy_t *, queue_t *);
55 
56 static softmac_capab_ops_t softmac_fill_capab_ops =
57 {
58 	softmac_fill_hcksum_ack,
59 	softmac_fill_zcopy_ack,
60 };
61 
62 static softmac_capab_ops_t softmac_adv_capab_ops =
63 {
64 	softmac_adv_hcksum_ack,
65 	softmac_adv_zcopy_ack,
66 };
67 
68 static softmac_capab_ops_t softmac_enable_capab_ops =
69 {
70 	softmac_enable_hcksum_ack,
71 	NULL,
72 };
73 
74 int
softmac_fill_capab(ldi_handle_t lh,softmac_t * softmac)75 softmac_fill_capab(ldi_handle_t lh, softmac_t *softmac)
76 {
77 	mblk_t			*mp = NULL;
78 	union DL_primitives	*prim;
79 	int			err = 0;
80 
81 	if ((err = dl_capab(lh, &mp)) != 0)
82 		goto exit;
83 
84 	prim = (union DL_primitives *)mp->b_rptr;
85 	if (prim->dl_primitive == DL_ERROR_ACK) {
86 		err = -1;
87 		goto exit;
88 	}
89 
90 	err = i_capab_ack(mp, NULL, &softmac_fill_capab_ops, softmac);
91 
92 exit:
93 	freemsg(mp);
94 	return (err);
95 }
96 
97 static int
dl_capab(ldi_handle_t lh,mblk_t ** mpp)98 dl_capab(ldi_handle_t lh, mblk_t **mpp)
99 {
100 	dl_capability_req_t	*capb;
101 	union DL_primitives	*dl_prim;
102 	mblk_t			*mp;
103 	int			err;
104 
105 	if ((mp = allocb(sizeof (dl_capability_req_t), BPRI_MED)) == NULL)
106 		return (ENOMEM);
107 	mp->b_datap->db_type = M_PROTO;
108 
109 	capb = (dl_capability_req_t *)mp->b_wptr;
110 	mp->b_wptr += sizeof (dl_capability_req_t);
111 	bzero(mp->b_rptr, sizeof (dl_capability_req_t));
112 	capb->dl_primitive = DL_CAPABILITY_REQ;
113 
114 	(void) ldi_putmsg(lh, mp);
115 	if ((err = ldi_getmsg(lh, &mp, (timestruc_t *)NULL)) != 0)
116 		return (err);
117 
118 	dl_prim = (union DL_primitives *)mp->b_rptr;
119 	switch (dl_prim->dl_primitive) {
120 	case DL_CAPABILITY_ACK:
121 		if (MBLKL(mp) < DL_CAPABILITY_ACK_SIZE) {
122 			printf("dl_capability: DL_CAPABILITY_ACK "
123 			    "protocol err\n");
124 			break;
125 		}
126 		*mpp = mp;
127 		return (0);
128 
129 	case DL_ERROR_ACK:
130 		if (MBLKL(mp) < DL_ERROR_ACK_SIZE) {
131 			printf("dl_capability: DL_ERROR_ACK protocol err\n");
132 			break;
133 		}
134 		if (((dl_error_ack_t *)dl_prim)->dl_error_primitive !=
135 		    DL_CAPABILITY_REQ) {
136 			printf("dl_capability: DL_ERROR_ACK rtnd prim %u\n",
137 			    ((dl_error_ack_t *)dl_prim)->dl_error_primitive);
138 			break;
139 		}
140 
141 		*mpp = mp;
142 		return (0);
143 
144 	default:
145 		printf("dl_capability: bad ACK header %u\n",
146 		    dl_prim->dl_primitive);
147 		break;
148 	}
149 
150 	freemsg(mp);
151 	return (-1);
152 }
153 
154 static int
softmac_fill_hcksum_ack(void * arg,t_uscalar_t flags)155 softmac_fill_hcksum_ack(void *arg, t_uscalar_t flags)
156 {
157 	softmac_t	*softmac = (softmac_t *)arg;
158 
159 	/*
160 	 * There are two types of acks we process here:
161 	 * 1. acks in reply to a (first form) generic capability req
162 	 *    (no ENABLE flag set)
163 	 * 2. acks in reply to a ENABLE capability req.
164 	 *    (ENABLE flag set)
165 	 * Only the first type should be expected here.
166 	 */
167 
168 	if (flags & HCKSUM_ENABLE) {
169 		cmn_err(CE_WARN, "softmac_fill_hcksum_ack: unexpected "
170 		    "HCKSUM_ENABLE flag in hardware checksum capability");
171 	} else if (flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 |
172 	    HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM)) {
173 		softmac->smac_capab_flags |= MAC_CAPAB_HCKSUM;
174 		softmac->smac_hcksum_txflags = flags;
175 	}
176 	return (0);
177 }
178 
179 static int
softmac_fill_zcopy_ack(void * arg,t_uscalar_t flags)180 softmac_fill_zcopy_ack(void *arg, t_uscalar_t flags)
181 {
182 	softmac_t	*softmac = (softmac_t *)arg;
183 
184 	ASSERT(flags == DL_CAPAB_VMSAFE_MEM);
185 	softmac->smac_capab_flags &= (~MAC_CAPAB_NO_ZCOPY);
186 	return (0);
187 }
188 
189 int
softmac_capab_enable(softmac_lower_t * slp)190 softmac_capab_enable(softmac_lower_t *slp)
191 {
192 	softmac_t	*softmac = slp->sl_softmac;
193 	int		err;
194 
195 	if (softmac->smac_no_capability_req)
196 		return (0);
197 
198 	/*
199 	 * Send DL_CAPABILITY_REQ to get capability advertisement.
200 	 */
201 	if ((err = softmac_capab_send(slp, B_FALSE)) != 0)
202 		return (err);
203 
204 	/*
205 	 * Send DL_CAPABILITY_REQ to enable specific capabilities.
206 	 */
207 	if ((err = softmac_capab_send(slp, B_TRUE)) != 0)
208 		return (err);
209 
210 	return (0);
211 }
212 
213 static int
softmac_capab_send(softmac_lower_t * slp,boolean_t enable)214 softmac_capab_send(softmac_lower_t *slp, boolean_t enable)
215 {
216 	softmac_t		*softmac;
217 	dl_capability_req_t	*capb;
218 	dl_capability_sub_t	*subcapb;
219 	mblk_t			*reqmp, *ackmp;
220 	int			err;
221 	size_t			size = 0;
222 
223 	softmac = slp->sl_softmac;
224 
225 	if (enable) {
226 		/* No need to enable DL_CAPAB_ZEROCOPY */
227 		if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM)
228 			size += sizeof (dl_capability_sub_t) +
229 			    sizeof (dl_capab_hcksum_t);
230 
231 		if (size == 0)
232 			return (0);
233 	}
234 
235 	/*
236 	 * Create DL_CAPABILITY_REQ message and send it down
237 	 */
238 	reqmp = allocb(sizeof (dl_capability_req_t) + size, BPRI_MED);
239 	if (reqmp == NULL)
240 		return (ENOMEM);
241 
242 	bzero(reqmp->b_rptr, sizeof (dl_capability_req_t) + size);
243 
244 	DB_TYPE(reqmp) = M_PROTO;
245 	reqmp->b_wptr = reqmp->b_rptr + sizeof (dl_capability_req_t) + size;
246 
247 	capb = (dl_capability_req_t *)reqmp->b_rptr;
248 	capb->dl_primitive = DL_CAPABILITY_REQ;
249 
250 	if (!enable)
251 		goto output;
252 
253 	capb->dl_sub_offset = sizeof (dl_capability_req_t);
254 
255 	if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM) {
256 		dl_capab_hcksum_t *hck_subcapp;
257 
258 		size = sizeof (dl_capability_sub_t) +
259 		    sizeof (dl_capab_hcksum_t);
260 		capb->dl_sub_length += size;
261 
262 		subcapb = (dl_capability_sub_t *)(capb + 1);
263 		subcapb->dl_cap = DL_CAPAB_HCKSUM;
264 		subcapb->dl_length = sizeof (dl_capab_hcksum_t);
265 		hck_subcapp = (dl_capab_hcksum_t *)(subcapb + 1);
266 		hck_subcapp->hcksum_version = HCKSUM_VERSION_1;
267 		hck_subcapp->hcksum_txflags =
268 		    softmac->smac_hcksum_txflags | HCKSUM_ENABLE;
269 	}
270 
271 output:
272 	err = softmac_proto_tx(slp, reqmp, &ackmp);
273 	if (err == 0) {
274 		if (enable) {
275 			err = i_capab_ack(ackmp, NULL,
276 			    &softmac_enable_capab_ops, softmac);
277 		} else {
278 			err = i_capab_ack(ackmp, NULL,
279 			    &softmac_adv_capab_ops, softmac);
280 		}
281 	}
282 	freemsg(ackmp);
283 
284 	return (err);
285 }
286 
287 static int
softmac_adv_hcksum_ack(void * arg,t_uscalar_t flags)288 softmac_adv_hcksum_ack(void *arg, t_uscalar_t flags)
289 {
290 	softmac_t	*softmac = (softmac_t *)arg;
291 
292 	/*
293 	 * There are two types of acks we process here:
294 	 * 1. acks in reply to a (first form) generic capability req
295 	 *    (no ENABLE flag set)
296 	 * 2. acks in reply to a ENABLE capability req.
297 	 *    (ENABLE flag set)
298 	 * Only the first type should be expected here.
299 	 */
300 
301 	if (flags & HCKSUM_ENABLE) {
302 		cmn_err(CE_WARN, "softmac_adv_hcksum_ack: unexpected "
303 		    "HCKSUM_ENABLE flag in hardware checksum capability");
304 		return (-1);
305 	} else if (flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 |
306 	    HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM)) {
307 		/*
308 		 * The acknowledgement should be the same as we got when
309 		 * the softmac is created.
310 		 */
311 		if (!(softmac->smac_capab_flags & MAC_CAPAB_HCKSUM)) {
312 			ASSERT(B_FALSE);
313 			return (-1);
314 		}
315 		if (softmac->smac_hcksum_txflags != flags) {
316 			ASSERT(B_FALSE);
317 			return (-1);
318 		}
319 	}
320 
321 	return (0);
322 }
323 
324 static int
softmac_adv_zcopy_ack(void * arg,t_uscalar_t flags)325 softmac_adv_zcopy_ack(void *arg, t_uscalar_t flags)
326 {
327 	softmac_t	*softmac = (softmac_t *)arg;
328 
329 	/*
330 	 * The acknowledgement should be the same as we got when
331 	 * the softmac is created.
332 	 */
333 	ASSERT(flags == DL_CAPAB_VMSAFE_MEM);
334 	if (softmac->smac_capab_flags & MAC_CAPAB_NO_ZCOPY) {
335 		ASSERT(B_FALSE);
336 		return (-1);
337 	}
338 
339 	return (0);
340 }
341 
342 static int
softmac_enable_hcksum_ack(void * arg,t_uscalar_t flags)343 softmac_enable_hcksum_ack(void *arg, t_uscalar_t flags)
344 {
345 	softmac_t	*softmac = (softmac_t *)arg;
346 
347 	/*
348 	 * There are two types of acks we process here:
349 	 * 1. acks in reply to a (first form) generic capability req
350 	 *    (no ENABLE flag set)
351 	 * 2. acks in reply to a ENABLE capability req.
352 	 *    (ENABLE flag set)
353 	 * Only the second type should be expected here.
354 	 */
355 
356 	if (flags & HCKSUM_ENABLE) {
357 		if ((flags & ~HCKSUM_ENABLE) != softmac->smac_hcksum_txflags) {
358 			cmn_err(CE_WARN, "softmac_enable_hcksum_ack: unexpected"
359 			    " hardware capability flag value 0x%x", flags);
360 			return (-1);
361 		}
362 	} else {
363 		cmn_err(CE_WARN, "softmac_enable_hcksum_ack: "
364 		    "hardware checksum flag HCKSUM_ENABLE is not set");
365 		return (-1);
366 	}
367 
368 	return (0);
369 }
370 
371 static int
i_capab_ack(mblk_t * mp,queue_t * q,softmac_capab_ops_t * op,void * arg)372 i_capab_ack(mblk_t *mp, queue_t *q, softmac_capab_ops_t *op, void *arg)
373 {
374 	union DL_primitives	*prim;
375 	dl_capability_ack_t	*cap;
376 	dl_capability_sub_t	*sub, *end;
377 	int			err = 0;
378 
379 	prim = (union DL_primitives *)mp->b_rptr;
380 	ASSERT(prim->dl_primitive == DL_CAPABILITY_ACK);
381 
382 	cap = (dl_capability_ack_t *)prim;
383 	if (cap->dl_sub_length == 0)
384 		goto exit;
385 
386 	/* Is dl_sub_length correct? */
387 	if ((sizeof (*cap) + cap->dl_sub_length) > MBLKL(mp)) {
388 		err = EINVAL;
389 		goto exit;
390 	}
391 
392 	sub = (dl_capability_sub_t *)((caddr_t)cap + cap->dl_sub_offset);
393 	end = (dl_capability_sub_t *)((caddr_t)cap + cap->dl_sub_length
394 	    - sizeof (*sub));
395 	for (; (sub <= end) && (err == 0); ) {
396 		switch (sub->dl_cap) {
397 		case DL_CAPAB_ID_WRAPPER:
398 			err = i_capab_id_ack(mp, sub, q, op, arg);
399 			break;
400 		default:
401 			err = i_capab_sub_ack(mp, sub, q, op, arg);
402 			break;
403 		}
404 		sub = (dl_capability_sub_t *)((caddr_t)sub + sizeof (*sub)
405 		    + sub->dl_length);
406 	}
407 
408 exit:
409 	return (err);
410 }
411 
412 static int
i_capab_id_ack(mblk_t * mp,dl_capability_sub_t * outers,queue_t * q,softmac_capab_ops_t * op,void * arg)413 i_capab_id_ack(mblk_t *mp, dl_capability_sub_t *outers,
414     queue_t *q, softmac_capab_ops_t *op, void *arg)
415 {
416 	dl_capab_id_t		*capab_id;
417 	dl_capability_sub_t	*inners;
418 	caddr_t			capend;
419 	int			err = EINVAL;
420 
421 	ASSERT(outers->dl_cap == DL_CAPAB_ID_WRAPPER);
422 
423 	capend = (caddr_t)(outers + 1) + outers->dl_length;
424 	if (capend > (caddr_t)mp->b_wptr) {
425 		cmn_err(CE_WARN, "i_capab_id_ack: malformed "
426 		    "sub-capability too long");
427 		return (err);
428 	}
429 
430 	capab_id = (dl_capab_id_t *)(outers + 1);
431 
432 	if (outers->dl_length < sizeof (*capab_id) ||
433 	    (inners = &capab_id->id_subcap,
434 	    inners->dl_length > (outers->dl_length - sizeof (*inners)))) {
435 		cmn_err(CE_WARN, "i_capab_id_ack: malformed "
436 		    "encapsulated capab type %d too long",
437 		    inners->dl_cap);
438 		return (err);
439 	}
440 
441 	if ((q != NULL) && (!dlcapabcheckqid(&capab_id->id_mid, q))) {
442 		cmn_err(CE_WARN, "i_capab_id_ack: pass-thru module(s) "
443 		    "detected, discarding capab type %d", inners->dl_cap);
444 		return (err);
445 	}
446 
447 	/* Process the encapsulated sub-capability */
448 	return (i_capab_sub_ack(mp, inners, q, op, arg));
449 }
450 
451 static int
i_capab_sub_ack(mblk_t * mp,dl_capability_sub_t * sub,queue_t * q,softmac_capab_ops_t * op,void * arg)452 i_capab_sub_ack(mblk_t *mp, dl_capability_sub_t *sub, queue_t *q,
453     softmac_capab_ops_t *op, void *arg)
454 {
455 	caddr_t			capend;
456 	dl_capab_hcksum_t	*hcksum;
457 	dl_capab_zerocopy_t	*zcopy;
458 	int			err = 0;
459 
460 	capend = (caddr_t)(sub + 1) + sub->dl_length;
461 	if (capend > (caddr_t)mp->b_wptr) {
462 		cmn_err(CE_WARN, "i_capab_sub_ack: "
463 		    "malformed sub-capability too long");
464 		return (EINVAL);
465 	}
466 
467 	switch (sub->dl_cap) {
468 	case DL_CAPAB_HCKSUM:
469 		hcksum = (dl_capab_hcksum_t *)(sub + 1);
470 		err = i_capab_hcksum_ack(hcksum, q, op, arg);
471 		break;
472 
473 	case DL_CAPAB_ZEROCOPY:
474 		zcopy = (dl_capab_zerocopy_t *)(sub + 1);
475 		err = i_capab_zcopy_ack(zcopy, q, op, arg);
476 		break;
477 
478 	default:
479 		cmn_err(CE_WARN, "i_capab_sub_ack: unknown capab type %d",
480 		    sub->dl_cap);
481 		err = EINVAL;
482 	}
483 
484 	return (err);
485 }
486 
487 static int
i_capab_hcksum_ack(dl_capab_hcksum_t * hcksum,queue_t * q,softmac_capab_ops_t * op,void * arg)488 i_capab_hcksum_ack(dl_capab_hcksum_t *hcksum, queue_t *q,
489     softmac_capab_ops_t *op, void *arg)
490 {
491 	t_uscalar_t		flags;
492 	int			err = 0;
493 
494 	if ((err = i_capab_hcksum_verify(hcksum, q)) != 0)
495 		return (err);
496 
497 	flags = hcksum->hcksum_txflags;
498 
499 	if (!(flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 |
500 	    HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM | HCKSUM_ENABLE))) {
501 		cmn_err(CE_WARN, "i_capab_hcksum_ack: invalid "
502 		    "hardware checksum capability flags 0x%x", flags);
503 		return (EINVAL);
504 	}
505 
506 	if (op->sc_hcksum_ack)
507 		return (op->sc_hcksum_ack(arg, flags));
508 	else {
509 		cmn_err(CE_WARN, "i_capab_hcksum_ack: unexpected hardware "
510 		    "checksum acknowledgement");
511 		return (EINVAL);
512 	}
513 }
514 
515 static int
i_capab_zcopy_ack(dl_capab_zerocopy_t * zcopy,queue_t * q,softmac_capab_ops_t * op,void * arg)516 i_capab_zcopy_ack(dl_capab_zerocopy_t *zcopy, queue_t *q,
517     softmac_capab_ops_t *op, void *arg)
518 {
519 	t_uscalar_t		flags;
520 	int			err = 0;
521 
522 	if ((err = i_capab_zcopy_verify(zcopy, q)) != 0)
523 		return (err);
524 
525 	flags = zcopy->zerocopy_flags;
526 	if (!(flags & DL_CAPAB_VMSAFE_MEM)) {
527 		cmn_err(CE_WARN, "i_capab_zcopy_ack: invalid zcopy capability "
528 		    "flags 0x%x", flags);
529 		return (EINVAL);
530 	}
531 	if (op->sc_zcopy_ack)
532 		return (op->sc_zcopy_ack(arg, flags));
533 	else {
534 		cmn_err(CE_WARN, "i_capab_zcopy_ack: unexpected zcopy "
535 		    "acknowledgement");
536 		return (EINVAL);
537 	}
538 }
539 
540 static int
i_capab_hcksum_verify(dl_capab_hcksum_t * hcksum,queue_t * q)541 i_capab_hcksum_verify(dl_capab_hcksum_t *hcksum, queue_t *q)
542 {
543 	if (hcksum->hcksum_version != HCKSUM_VERSION_1) {
544 		cmn_err(CE_WARN, "i_capab_hcksum_verify: "
545 		    "unsupported hardware checksum capability (version %d, "
546 		    "expected %d)", hcksum->hcksum_version, HCKSUM_VERSION_1);
547 		return (-1);
548 	}
549 
550 	if ((q != NULL) && !dlcapabcheckqid(&hcksum->hcksum_mid, q)) {
551 		cmn_err(CE_WARN, "i_capab_hcksum_verify: unexpected pass-thru "
552 		    "module detected; hardware checksum capability discarded");
553 		return (-1);
554 	}
555 	return (0);
556 }
557 
558 static int
i_capab_zcopy_verify(dl_capab_zerocopy_t * zcopy,queue_t * q)559 i_capab_zcopy_verify(dl_capab_zerocopy_t *zcopy, queue_t *q)
560 {
561 	if (zcopy->zerocopy_version != ZEROCOPY_VERSION_1) {
562 		cmn_err(CE_WARN, "i_capab_zcopy_verify: unsupported zcopy "
563 		    "capability (version %d, expected %d)",
564 		    zcopy->zerocopy_version, ZEROCOPY_VERSION_1);
565 		return (-1);
566 	}
567 
568 	if ((q != NULL) && !dlcapabcheckqid(&zcopy->zerocopy_mid, q)) {
569 		cmn_err(CE_WARN, "i_capab_zcopy_verify: unexpected pass-thru "
570 		    "module detected; zcopy checksum capability discarded");
571 		return (-1);
572 	}
573 	return (0);
574 }
575