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