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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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