10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51502Sericheng * Common Development and Distribution License (the "License"). 61502Sericheng * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 225895Syz147064 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate /* 270Sstevel@tonic-gate * Data-Link Driver 280Sstevel@tonic-gate */ 290Sstevel@tonic-gate #include <sys/sysmacros.h> 30*8275SEric Cheng #include <sys/strsubr.h> 310Sstevel@tonic-gate #include <sys/strsun.h> 320Sstevel@tonic-gate #include <sys/vlan.h> 330Sstevel@tonic-gate #include <sys/dld_impl.h> 34*8275SEric Cheng #include <sys/mac_client.h> 35*8275SEric Cheng #include <sys/mac_client_impl.h> 36*8275SEric Cheng #include <sys/mac_client_priv.h> 370Sstevel@tonic-gate 38*8275SEric Cheng typedef void proto_reqfunc_t(dld_str_t *, mblk_t *); 390Sstevel@tonic-gate 400Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req, 410Sstevel@tonic-gate proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req, 420Sstevel@tonic-gate proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req, 430Sstevel@tonic-gate proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req, 445895Syz147064 proto_notify_req, proto_passive_req; 450Sstevel@tonic-gate 46*8275SEric Cheng static void proto_capability_advertise(dld_str_t *, mblk_t *); 47*8275SEric Cheng static int dld_capab_poll_disable(dld_str_t *, dld_capab_poll_t *); 481184Skrgopi 490Sstevel@tonic-gate #define DL_ACK_PENDING(state) \ 500Sstevel@tonic-gate ((state) == DL_ATTACH_PENDING || \ 510Sstevel@tonic-gate (state) == DL_DETACH_PENDING || \ 520Sstevel@tonic-gate (state) == DL_BIND_PENDING || \ 530Sstevel@tonic-gate (state) == DL_UNBIND_PENDING) 540Sstevel@tonic-gate 550Sstevel@tonic-gate /* 56269Sericheng * Process a DLPI protocol message. 57269Sericheng * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ, 58269Sericheng * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an 59269Sericheng * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t 60269Sericheng * as 'passive' and forbids it from being subsequently made 'active' 61269Sericheng * by the above primitives. 620Sstevel@tonic-gate */ 630Sstevel@tonic-gate void 64*8275SEric Cheng dld_proto(dld_str_t *dsp, mblk_t *mp) 650Sstevel@tonic-gate { 660Sstevel@tonic-gate t_uscalar_t prim; 670Sstevel@tonic-gate 68*8275SEric Cheng if (MBLKL(mp) < sizeof (t_uscalar_t)) { 69*8275SEric Cheng freemsg(mp); 70*8275SEric Cheng return; 71*8275SEric Cheng } 72*8275SEric Cheng prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive; 730Sstevel@tonic-gate 74269Sericheng switch (prim) { 75269Sericheng case DL_INFO_REQ: 76*8275SEric Cheng proto_info_req(dsp, mp); 77269Sericheng break; 78269Sericheng case DL_BIND_REQ: 79*8275SEric Cheng proto_bind_req(dsp, mp); 80269Sericheng break; 81269Sericheng case DL_UNBIND_REQ: 82*8275SEric Cheng proto_unbind_req(dsp, mp); 83*8275SEric Cheng break; 84*8275SEric Cheng case DL_UNITDATA_REQ: 85*8275SEric Cheng proto_unitdata_req(dsp, mp); 86269Sericheng break; 87269Sericheng case DL_UDQOS_REQ: 88*8275SEric Cheng proto_udqos_req(dsp, mp); 89269Sericheng break; 90269Sericheng case DL_ATTACH_REQ: 91*8275SEric Cheng proto_attach_req(dsp, mp); 92269Sericheng break; 93269Sericheng case DL_DETACH_REQ: 94*8275SEric Cheng proto_detach_req(dsp, mp); 95269Sericheng break; 96269Sericheng case DL_ENABMULTI_REQ: 97*8275SEric Cheng proto_enabmulti_req(dsp, mp); 98269Sericheng break; 99269Sericheng case DL_DISABMULTI_REQ: 100*8275SEric Cheng proto_disabmulti_req(dsp, mp); 101269Sericheng break; 102269Sericheng case DL_PROMISCON_REQ: 103*8275SEric Cheng proto_promiscon_req(dsp, mp); 104269Sericheng break; 105269Sericheng case DL_PROMISCOFF_REQ: 106*8275SEric Cheng proto_promiscoff_req(dsp, mp); 107269Sericheng break; 108269Sericheng case DL_PHYS_ADDR_REQ: 109*8275SEric Cheng proto_physaddr_req(dsp, mp); 110269Sericheng break; 111269Sericheng case DL_SET_PHYS_ADDR_REQ: 112*8275SEric Cheng proto_setphysaddr_req(dsp, mp); 113269Sericheng break; 114269Sericheng case DL_NOTIFY_REQ: 115*8275SEric Cheng proto_notify_req(dsp, mp); 116269Sericheng break; 117269Sericheng case DL_CAPABILITY_REQ: 118*8275SEric Cheng proto_capability_req(dsp, mp); 119269Sericheng break; 120269Sericheng case DL_PASSIVE_REQ: 121*8275SEric Cheng proto_passive_req(dsp, mp); 122269Sericheng break; 123269Sericheng default: 124*8275SEric Cheng proto_req(dsp, mp); 125269Sericheng break; 1260Sstevel@tonic-gate } 1270Sstevel@tonic-gate } 1280Sstevel@tonic-gate 129269Sericheng #define NEG(x) -(x) 1300Sstevel@tonic-gate typedef struct dl_info_ack_wrapper { 1310Sstevel@tonic-gate dl_info_ack_t dl_info; 1322311Sseb uint8_t dl_addr[MAXMACADDRLEN + sizeof (uint16_t)]; 1332311Sseb uint8_t dl_brdcst_addr[MAXMACADDRLEN]; 1340Sstevel@tonic-gate dl_qos_cl_range1_t dl_qos_range1; 1350Sstevel@tonic-gate dl_qos_cl_sel1_t dl_qos_sel1; 1360Sstevel@tonic-gate } dl_info_ack_wrapper_t; 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate /* 139269Sericheng * DL_INFO_REQ 1400Sstevel@tonic-gate */ 141*8275SEric Cheng static void 142*8275SEric Cheng proto_info_req(dld_str_t *dsp, mblk_t *mp) 1430Sstevel@tonic-gate { 1440Sstevel@tonic-gate dl_info_ack_wrapper_t *dlwp; 1450Sstevel@tonic-gate dl_info_ack_t *dlp; 1460Sstevel@tonic-gate dl_qos_cl_sel1_t *selp; 1470Sstevel@tonic-gate dl_qos_cl_range1_t *rangep; 1480Sstevel@tonic-gate uint8_t *addr; 1490Sstevel@tonic-gate uint8_t *brdcst_addr; 1500Sstevel@tonic-gate uint_t addr_length; 1510Sstevel@tonic-gate uint_t sap_length; 152269Sericheng mac_info_t minfo; 153269Sericheng mac_info_t *minfop; 154269Sericheng queue_t *q = dsp->ds_wq; 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate /* 1570Sstevel@tonic-gate * Swap the request message for one large enough to contain the 1580Sstevel@tonic-gate * wrapper structure defined above. 1590Sstevel@tonic-gate */ 160269Sericheng if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t), 1610Sstevel@tonic-gate M_PCPROTO, 0)) == NULL) 162*8275SEric Cheng return; 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t)); 1650Sstevel@tonic-gate dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr; 1660Sstevel@tonic-gate 1670Sstevel@tonic-gate dlp = &(dlwp->dl_info); 1680Sstevel@tonic-gate ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr); 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate dlp->dl_primitive = DL_INFO_ACK; 1710Sstevel@tonic-gate 1720Sstevel@tonic-gate /* 1730Sstevel@tonic-gate * Set up the sub-structure pointers. 1740Sstevel@tonic-gate */ 1750Sstevel@tonic-gate addr = dlwp->dl_addr; 1760Sstevel@tonic-gate brdcst_addr = dlwp->dl_brdcst_addr; 1770Sstevel@tonic-gate rangep = &(dlwp->dl_qos_range1); 1780Sstevel@tonic-gate selp = &(dlwp->dl_qos_sel1); 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate /* 1810Sstevel@tonic-gate * This driver supports only version 2 connectionless DLPI provider 1820Sstevel@tonic-gate * nodes. 1830Sstevel@tonic-gate */ 1840Sstevel@tonic-gate dlp->dl_service_mode = DL_CLDLS; 1850Sstevel@tonic-gate dlp->dl_version = DL_VERSION_2; 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate /* 188269Sericheng * Set the style of the provider 1890Sstevel@tonic-gate */ 190269Sericheng dlp->dl_provider_style = dsp->ds_style; 1910Sstevel@tonic-gate ASSERT(dlp->dl_provider_style == DL_STYLE1 || 1920Sstevel@tonic-gate dlp->dl_provider_style == DL_STYLE2); 1930Sstevel@tonic-gate 1940Sstevel@tonic-gate /* 1950Sstevel@tonic-gate * Set the current DLPI state. 1960Sstevel@tonic-gate */ 1970Sstevel@tonic-gate dlp->dl_current_state = dsp->ds_dlstate; 1980Sstevel@tonic-gate 1990Sstevel@tonic-gate /* 200269Sericheng * Gratuitously set the media type. This is to deal with modules 201269Sericheng * that assume the media type is known prior to DL_ATTACH_REQ 2020Sstevel@tonic-gate * being completed. 2030Sstevel@tonic-gate */ 2040Sstevel@tonic-gate dlp->dl_mac_type = DL_ETHER; 2050Sstevel@tonic-gate 2060Sstevel@tonic-gate /* 207269Sericheng * If the stream is not at least attached we try to retrieve the 208269Sericheng * mac_info using mac_info_get() 2090Sstevel@tonic-gate */ 2100Sstevel@tonic-gate if (dsp->ds_dlstate == DL_UNATTACHED || 2110Sstevel@tonic-gate dsp->ds_dlstate == DL_ATTACH_PENDING || 212269Sericheng dsp->ds_dlstate == DL_DETACH_PENDING) { 213269Sericheng if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) { 214269Sericheng /* 215269Sericheng * Cannot find mac_info. giving up. 216269Sericheng */ 217269Sericheng goto done; 218269Sericheng } 219269Sericheng minfop = &minfo; 220269Sericheng } else { 221269Sericheng minfop = (mac_info_t *)dsp->ds_mip; 2225903Ssowmini /* We can only get the sdu if we're attached. */ 2235903Ssowmini mac_sdu_get(dsp->ds_mh, &dlp->dl_min_sdu, &dlp->dl_max_sdu); 224269Sericheng } 2250Sstevel@tonic-gate 2260Sstevel@tonic-gate /* 2270Sstevel@tonic-gate * Set the media type (properly this time). 2280Sstevel@tonic-gate */ 2293147Sxc151355 if (dsp->ds_native) 2303147Sxc151355 dlp->dl_mac_type = minfop->mi_nativemedia; 2313147Sxc151355 else 2323147Sxc151355 dlp->dl_mac_type = minfop->mi_media; 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate /* 2350Sstevel@tonic-gate * Set the DLSAP length. We only support 16 bit values and they 2360Sstevel@tonic-gate * appear after the MAC address portion of DLSAP addresses. 2370Sstevel@tonic-gate */ 2380Sstevel@tonic-gate sap_length = sizeof (uint16_t); 2390Sstevel@tonic-gate dlp->dl_sap_length = NEG(sap_length); 2400Sstevel@tonic-gate 241269Sericheng addr_length = minfop->mi_addr_length; 2420Sstevel@tonic-gate 2430Sstevel@tonic-gate /* 2440Sstevel@tonic-gate * Copy in the media broadcast address. 2450Sstevel@tonic-gate */ 2462311Sseb if (minfop->mi_brdcst_addr != NULL) { 2472311Sseb dlp->dl_brdcst_addr_offset = 2482311Sseb (uintptr_t)brdcst_addr - (uintptr_t)dlp; 2492311Sseb bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length); 2502311Sseb dlp->dl_brdcst_addr_length = addr_length; 2512311Sseb } 2520Sstevel@tonic-gate 2532760Sdg199075 dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp; 2542760Sdg199075 dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t); 2550Sstevel@tonic-gate 2562760Sdg199075 rangep->dl_qos_type = DL_QOS_CL_RANGE1; 2572760Sdg199075 rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN; 2582760Sdg199075 rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN; 2592760Sdg199075 rangep->dl_protection.dl_min = DL_UNKNOWN; 2602760Sdg199075 rangep->dl_protection.dl_max = DL_UNKNOWN; 2612760Sdg199075 rangep->dl_residual_error = DL_UNKNOWN; 2620Sstevel@tonic-gate 2632760Sdg199075 /* 2642760Sdg199075 * Specify the supported range of priorities. 2652760Sdg199075 */ 2662760Sdg199075 rangep->dl_priority.dl_min = 0; 2672760Sdg199075 rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1; 2680Sstevel@tonic-gate 2692760Sdg199075 dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp; 2702760Sdg199075 dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t); 2710Sstevel@tonic-gate 2722760Sdg199075 selp->dl_qos_type = DL_QOS_CL_SEL1; 2732760Sdg199075 selp->dl_trans_delay = DL_UNKNOWN; 2742760Sdg199075 selp->dl_protection = DL_UNKNOWN; 2752760Sdg199075 selp->dl_residual_error = DL_UNKNOWN; 2762760Sdg199075 2772760Sdg199075 /* 2782760Sdg199075 * Specify the current priority (which can be changed by 2792760Sdg199075 * the DL_UDQOS_REQ primitive). 2802760Sdg199075 */ 2812760Sdg199075 selp->dl_priority = dsp->ds_pri; 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate dlp->dl_addr_length = addr_length + sizeof (uint16_t); 2840Sstevel@tonic-gate if (dsp->ds_dlstate == DL_IDLE) { 2850Sstevel@tonic-gate /* 2860Sstevel@tonic-gate * The stream is bound. Therefore we can formulate a valid 2870Sstevel@tonic-gate * DLSAP address. 2880Sstevel@tonic-gate */ 2890Sstevel@tonic-gate dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp; 2902311Sseb if (addr_length > 0) 291*8275SEric Cheng mac_unicast_primary_get(dsp->ds_mh, addr); 292*8275SEric Cheng 2930Sstevel@tonic-gate *(uint16_t *)(addr + addr_length) = dsp->ds_sap; 2940Sstevel@tonic-gate } 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate done: 2970Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0)); 2980Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_range_offset != 0, 2990Sstevel@tonic-gate dlp->dl_qos_range_length != 0)); 3000Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0)); 3010Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0, 3020Sstevel@tonic-gate dlp->dl_brdcst_addr_length != 0)); 3030Sstevel@tonic-gate 304269Sericheng qreply(q, mp); 305269Sericheng } 306269Sericheng 307269Sericheng /* 308269Sericheng * DL_ATTACH_REQ 309269Sericheng */ 310*8275SEric Cheng static void 311*8275SEric Cheng proto_attach_req(dld_str_t *dsp, mblk_t *mp) 312269Sericheng { 313*8275SEric Cheng dl_attach_req_t *dlp = (dl_attach_req_t *)mp->b_rptr; 314269Sericheng int err = 0; 315269Sericheng t_uscalar_t dl_err; 316269Sericheng queue_t *q = dsp->ds_wq; 317269Sericheng 318269Sericheng if (MBLKL(mp) < sizeof (dl_attach_req_t) || 319269Sericheng dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) { 320269Sericheng dl_err = DL_BADPRIM; 321269Sericheng goto failed; 322269Sericheng } 323269Sericheng 324269Sericheng if (dsp->ds_dlstate != DL_UNATTACHED) { 325269Sericheng dl_err = DL_OUTSTATE; 326269Sericheng goto failed; 327269Sericheng } 328269Sericheng 329269Sericheng dsp->ds_dlstate = DL_ATTACH_PENDING; 330269Sericheng 331269Sericheng err = dld_str_attach(dsp, dlp->dl_ppa); 332269Sericheng if (err != 0) { 333269Sericheng switch (err) { 334269Sericheng case ENOENT: 335269Sericheng dl_err = DL_BADPPA; 336269Sericheng err = 0; 337269Sericheng break; 338269Sericheng default: 339269Sericheng dl_err = DL_SYSERR; 340269Sericheng break; 341269Sericheng } 342269Sericheng dsp->ds_dlstate = DL_UNATTACHED; 343269Sericheng goto failed; 344269Sericheng } 345269Sericheng ASSERT(dsp->ds_dlstate == DL_UNBOUND); 346*8275SEric Cheng dlokack(q, mp, DL_ATTACH_REQ); 347*8275SEric Cheng return; 348269Sericheng 349269Sericheng failed: 350269Sericheng dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err); 3510Sstevel@tonic-gate } 3520Sstevel@tonic-gate 353*8275SEric Cheng /* 354*8275SEric Cheng * DL_DETACH_REQ 355*8275SEric Cheng */ 356*8275SEric Cheng static void 357*8275SEric Cheng proto_detach_req(dld_str_t *dsp, mblk_t *mp) 3580Sstevel@tonic-gate { 359269Sericheng queue_t *q = dsp->ds_wq; 360269Sericheng t_uscalar_t dl_err; 3610Sstevel@tonic-gate 362269Sericheng if (MBLKL(mp) < sizeof (dl_detach_req_t)) { 363269Sericheng dl_err = DL_BADPRIM; 364269Sericheng goto failed; 365269Sericheng } 3660Sstevel@tonic-gate 367269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 368269Sericheng dl_err = DL_OUTSTATE; 369269Sericheng goto failed; 3700Sstevel@tonic-gate } 3710Sstevel@tonic-gate 372269Sericheng if (dsp->ds_style == DL_STYLE1) { 373269Sericheng dl_err = DL_BADPRIM; 374269Sericheng goto failed; 375269Sericheng } 376269Sericheng 377*8275SEric Cheng ASSERT(dsp->ds_datathr_cnt == 0); 378269Sericheng dsp->ds_dlstate = DL_DETACH_PENDING; 379*8275SEric Cheng 3805895Syz147064 dld_str_detach(dsp); 3815895Syz147064 dlokack(dsp->ds_wq, mp, DL_DETACH_REQ); 382*8275SEric Cheng return; 383*8275SEric Cheng 384269Sericheng failed: 385269Sericheng dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0); 3860Sstevel@tonic-gate } 3870Sstevel@tonic-gate 3880Sstevel@tonic-gate /* 389269Sericheng * DL_BIND_REQ 3900Sstevel@tonic-gate */ 391*8275SEric Cheng static void 392*8275SEric Cheng proto_bind_req(dld_str_t *dsp, mblk_t *mp) 3930Sstevel@tonic-gate { 394*8275SEric Cheng dl_bind_req_t *dlp = (dl_bind_req_t *)mp->b_rptr; 395269Sericheng int err = 0; 3963037Syz147064 uint8_t dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)]; 3973037Syz147064 uint_t dlsap_addr_length; 398269Sericheng t_uscalar_t dl_err; 399269Sericheng t_scalar_t sap; 400269Sericheng queue_t *q = dsp->ds_wq; 401*8275SEric Cheng mac_perim_handle_t mph; 402*8275SEric Cheng void *mdip; 403*8275SEric Cheng int32_t intr_cpu; 404269Sericheng 405269Sericheng if (MBLKL(mp) < sizeof (dl_bind_req_t)) { 406269Sericheng dl_err = DL_BADPRIM; 407269Sericheng goto failed; 408269Sericheng } 409269Sericheng 410269Sericheng if (dlp->dl_xidtest_flg != 0) { 411269Sericheng dl_err = DL_NOAUTO; 412269Sericheng goto failed; 413269Sericheng } 414269Sericheng 415269Sericheng if (dlp->dl_service_mode != DL_CLDLS) { 416269Sericheng dl_err = DL_UNSUPPORTED; 417269Sericheng goto failed; 418269Sericheng } 419269Sericheng 420269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 421269Sericheng dl_err = DL_OUTSTATE; 422269Sericheng goto failed; 423269Sericheng } 4240Sstevel@tonic-gate 425*8275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 426*8275SEric Cheng 427269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 428*8275SEric Cheng ((err = dls_active_set(dsp)) != 0)) { 429269Sericheng dl_err = DL_SYSERR; 430*8275SEric Cheng goto failed2; 431269Sericheng } 432269Sericheng 433*8275SEric Cheng dsp->ds_dlstate = DL_BIND_PENDING; 434269Sericheng /* 435269Sericheng * Set the receive callback. 436269Sericheng */ 437*8275SEric Cheng dls_rx_set(dsp, (dsp->ds_mode == DLD_RAW) ? 438269Sericheng dld_str_rx_raw : dld_str_rx_unitdata, dsp); 4390Sstevel@tonic-gate 440269Sericheng /* 441269Sericheng * Bind the channel such that it can receive packets. 442269Sericheng */ 4435895Syz147064 sap = dlp->dl_sap; 444*8275SEric Cheng err = dls_bind(dsp, sap); 445269Sericheng if (err != 0) { 446269Sericheng switch (err) { 447269Sericheng case EINVAL: 448269Sericheng dl_err = DL_BADADDR; 449269Sericheng err = 0; 450269Sericheng break; 451269Sericheng default: 452269Sericheng dl_err = DL_SYSERR; 453269Sericheng break; 454269Sericheng } 4555895Syz147064 456*8275SEric Cheng dsp->ds_dlstate = DL_UNBOUND; 457269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 458*8275SEric Cheng dls_active_clear(dsp); 459*8275SEric Cheng goto failed2; 460*8275SEric Cheng } 461269Sericheng 462*8275SEric Cheng intr_cpu = mac_client_intr_cpu(dsp->ds_mch); 463*8275SEric Cheng mdip = mac_get_devinfo(dsp->ds_mh); 464*8275SEric Cheng mac_perim_exit(mph); 465*8275SEric Cheng 466*8275SEric Cheng /* 467*8275SEric Cheng * We do this after we get out of the perim to avoid deadlocks 468*8275SEric Cheng * etc. since part of mac_client_retarget_intr is to walk the 469*8275SEric Cheng * device tree in order to find and retarget the interrupts. 470*8275SEric Cheng */ 471*8275SEric Cheng mac_client_set_intr_cpu(mdip, dsp->ds_mch, intr_cpu); 4720Sstevel@tonic-gate 4730Sstevel@tonic-gate /* 4740Sstevel@tonic-gate * Copy in MAC address. 4750Sstevel@tonic-gate */ 4763037Syz147064 dlsap_addr_length = dsp->ds_mip->mi_addr_length; 477*8275SEric Cheng mac_unicast_primary_get(dsp->ds_mh, dlsap_addr); 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate /* 4803037Syz147064 * Copy in the SAP. 4810Sstevel@tonic-gate */ 4825895Syz147064 *(uint16_t *)(dlsap_addr + dlsap_addr_length) = sap; 4833037Syz147064 dlsap_addr_length += sizeof (uint16_t); 4840Sstevel@tonic-gate 4850Sstevel@tonic-gate dsp->ds_dlstate = DL_IDLE; 486269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 487269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 488269Sericheng 4893037Syz147064 dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0); 490*8275SEric Cheng return; 491*8275SEric Cheng 492*8275SEric Cheng failed2: 493*8275SEric Cheng mac_perim_exit(mph); 4940Sstevel@tonic-gate failed: 495269Sericheng dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err); 4960Sstevel@tonic-gate } 4970Sstevel@tonic-gate 4980Sstevel@tonic-gate /* 499269Sericheng * DL_UNBIND_REQ 5000Sstevel@tonic-gate */ 501*8275SEric Cheng static void 502*8275SEric Cheng proto_unbind_req(dld_str_t *dsp, mblk_t *mp) 5030Sstevel@tonic-gate { 5045895Syz147064 queue_t *q = dsp->ds_wq; 5055895Syz147064 t_uscalar_t dl_err; 506*8275SEric Cheng mac_perim_handle_t mph; 507269Sericheng 5085895Syz147064 if (MBLKL(mp) < sizeof (dl_unbind_req_t)) { 5095895Syz147064 dl_err = DL_BADPRIM; 5105895Syz147064 goto failed; 5115895Syz147064 } 5125895Syz147064 5135895Syz147064 if (dsp->ds_dlstate != DL_IDLE) { 5145895Syz147064 dl_err = DL_OUTSTATE; 5155895Syz147064 goto failed; 5165895Syz147064 } 517269Sericheng 518*8275SEric Cheng mutex_enter(&dsp->ds_lock); 519*8275SEric Cheng while (dsp->ds_datathr_cnt != 0) 520*8275SEric Cheng cv_wait(&dsp->ds_datathr_cv, &dsp->ds_lock); 521269Sericheng 522*8275SEric Cheng dsp->ds_dlstate = DL_UNBIND_PENDING; 523*8275SEric Cheng mutex_exit(&dsp->ds_lock); 524*8275SEric Cheng 525*8275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 526269Sericheng /* 527269Sericheng * Unbind the channel to stop packets being received. 528269Sericheng */ 529*8275SEric Cheng if (dls_unbind(dsp) != 0) { 530*8275SEric Cheng dl_err = DL_OUTSTATE; 531*8275SEric Cheng mac_perim_exit(mph); 532*8275SEric Cheng goto failed; 533*8275SEric Cheng } 5345895Syz147064 5355895Syz147064 /* 536269Sericheng * Disable polling mode, if it is enabled. 537269Sericheng */ 538*8275SEric Cheng (void) dld_capab_poll_disable(dsp, NULL); 5395895Syz147064 5405895Syz147064 /* 5413115Syl150051 * Clear LSO flags. 5423115Syl150051 */ 5433115Syl150051 dsp->ds_lso = B_FALSE; 5443115Syl150051 dsp->ds_lso_max = 0; 5453115Syl150051 5463115Syl150051 /* 547*8275SEric Cheng * Clear the receive callback. 548*8275SEric Cheng */ 549*8275SEric Cheng dls_rx_set(dsp, NULL, NULL); 550*8275SEric Cheng dsp->ds_direct = B_FALSE; 551*8275SEric Cheng 552*8275SEric Cheng /* 553269Sericheng * Set the mode back to the default (unitdata). 554269Sericheng */ 555269Sericheng dsp->ds_mode = DLD_UNITDATA; 5561353Sericheng dsp->ds_dlstate = DL_UNBOUND; 5571353Sericheng 558*8275SEric Cheng mac_perim_exit(mph); 559*8275SEric Cheng dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ); 560*8275SEric Cheng return; 561269Sericheng failed: 562269Sericheng dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0); 5630Sstevel@tonic-gate } 5640Sstevel@tonic-gate 5650Sstevel@tonic-gate /* 566269Sericheng * DL_PROMISCON_REQ 5670Sstevel@tonic-gate */ 568*8275SEric Cheng static void 569*8275SEric Cheng proto_promiscon_req(dld_str_t *dsp, mblk_t *mp) 5700Sstevel@tonic-gate { 571*8275SEric Cheng dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)mp->b_rptr; 572269Sericheng int err = 0; 573269Sericheng t_uscalar_t dl_err; 574*8275SEric Cheng uint32_t promisc_saved; 575269Sericheng queue_t *q = dsp->ds_wq; 576*8275SEric Cheng mac_perim_handle_t mph; 577269Sericheng 578269Sericheng if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) { 579269Sericheng dl_err = DL_BADPRIM; 580269Sericheng goto failed; 581269Sericheng } 582269Sericheng 583269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 584269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 585269Sericheng dl_err = DL_OUTSTATE; 5860Sstevel@tonic-gate goto failed; 587269Sericheng } 5880Sstevel@tonic-gate 589*8275SEric Cheng promisc_saved = dsp->ds_promisc; 590269Sericheng switch (dlp->dl_level) { 591269Sericheng case DL_PROMISC_SAP: 592*8275SEric Cheng dsp->ds_promisc |= DLS_PROMISC_SAP; 593269Sericheng break; 594*8275SEric Cheng 5955895Syz147064 case DL_PROMISC_MULTI: 596*8275SEric Cheng dsp->ds_promisc |= DLS_PROMISC_MULTI; 5975895Syz147064 break; 598*8275SEric Cheng 599269Sericheng case DL_PROMISC_PHYS: 600*8275SEric Cheng dsp->ds_promisc |= DLS_PROMISC_PHYS; 601269Sericheng break; 602*8275SEric Cheng 603269Sericheng default: 604269Sericheng dl_err = DL_NOTSUPPORTED; 605269Sericheng goto failed; 606269Sericheng } 6070Sstevel@tonic-gate 608*8275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 609*8275SEric Cheng 610269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 611*8275SEric Cheng ((err = dls_active_set(dsp)) != 0)) { 612*8275SEric Cheng dsp->ds_promisc = promisc_saved; 613269Sericheng dl_err = DL_SYSERR; 614*8275SEric Cheng goto failed2; 615269Sericheng } 616269Sericheng 617269Sericheng /* 618269Sericheng * Adjust channel promiscuity. 619269Sericheng */ 620*8275SEric Cheng err = dls_promisc(dsp, promisc_saved); 621*8275SEric Cheng 622269Sericheng if (err != 0) { 623269Sericheng dl_err = DL_SYSERR; 624*8275SEric Cheng dsp->ds_promisc = promisc_saved; 625269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 626*8275SEric Cheng dls_active_clear(dsp); 627*8275SEric Cheng goto failed2; 628269Sericheng } 629269Sericheng 630*8275SEric Cheng mac_perim_exit(mph); 631*8275SEric Cheng 632269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 633269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 634*8275SEric Cheng dlokack(q, mp, DL_PROMISCON_REQ); 635*8275SEric Cheng return; 636269Sericheng 637*8275SEric Cheng failed2: 638*8275SEric Cheng mac_perim_exit(mph); 6390Sstevel@tonic-gate failed: 640269Sericheng dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err); 6410Sstevel@tonic-gate } 6420Sstevel@tonic-gate 6430Sstevel@tonic-gate /* 644269Sericheng * DL_PROMISCOFF_REQ 6450Sstevel@tonic-gate */ 646*8275SEric Cheng static void 647*8275SEric Cheng proto_promiscoff_req(dld_str_t *dsp, mblk_t *mp) 6480Sstevel@tonic-gate { 649*8275SEric Cheng dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)mp->b_rptr; 650269Sericheng int err = 0; 651269Sericheng t_uscalar_t dl_err; 652*8275SEric Cheng uint32_t promisc_saved; 653269Sericheng queue_t *q = dsp->ds_wq; 654*8275SEric Cheng mac_perim_handle_t mph; 655269Sericheng 656269Sericheng if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) { 657269Sericheng dl_err = DL_BADPRIM; 6580Sstevel@tonic-gate goto failed; 659269Sericheng } 6600Sstevel@tonic-gate 661269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 662269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 663269Sericheng dl_err = DL_OUTSTATE; 6640Sstevel@tonic-gate goto failed; 665269Sericheng } 6660Sstevel@tonic-gate 667*8275SEric Cheng promisc_saved = dsp->ds_promisc; 668269Sericheng switch (dlp->dl_level) { 669269Sericheng case DL_PROMISC_SAP: 670*8275SEric Cheng if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) { 671*8275SEric Cheng dl_err = DL_NOTENAB; 672*8275SEric Cheng goto failed; 673*8275SEric Cheng } 674*8275SEric Cheng dsp->ds_promisc &= ~DLS_PROMISC_SAP; 6750Sstevel@tonic-gate break; 676*8275SEric Cheng 677269Sericheng case DL_PROMISC_MULTI: 678*8275SEric Cheng if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) { 679*8275SEric Cheng dl_err = DL_NOTENAB; 680*8275SEric Cheng goto failed; 681*8275SEric Cheng } 682*8275SEric Cheng dsp->ds_promisc &= ~DLS_PROMISC_MULTI; 683269Sericheng break; 684*8275SEric Cheng 685269Sericheng case DL_PROMISC_PHYS: 686*8275SEric Cheng if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) { 687*8275SEric Cheng dl_err = DL_NOTENAB; 688*8275SEric Cheng goto failed; 689*8275SEric Cheng } 690*8275SEric Cheng dsp->ds_promisc &= ~DLS_PROMISC_PHYS; 6910Sstevel@tonic-gate break; 692*8275SEric Cheng 6930Sstevel@tonic-gate default: 694269Sericheng dl_err = DL_NOTSUPPORTED; 695269Sericheng goto failed; 696269Sericheng } 697269Sericheng 698*8275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 699*8275SEric Cheng /* 700*8275SEric Cheng * Adjust channel promiscuity. 701*8275SEric Cheng */ 702*8275SEric Cheng err = dls_promisc(dsp, promisc_saved); 703*8275SEric Cheng mac_perim_exit(mph); 7045895Syz147064 705269Sericheng if (err != 0) { 7060Sstevel@tonic-gate dl_err = DL_SYSERR; 707269Sericheng goto failed; 708269Sericheng } 709269Sericheng dlokack(q, mp, DL_PROMISCOFF_REQ); 710*8275SEric Cheng return; 711269Sericheng failed: 712269Sericheng dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err); 713269Sericheng } 714269Sericheng 715269Sericheng /* 716269Sericheng * DL_ENABMULTI_REQ 717269Sericheng */ 718*8275SEric Cheng static void 719*8275SEric Cheng proto_enabmulti_req(dld_str_t *dsp, mblk_t *mp) 720269Sericheng { 721*8275SEric Cheng dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)mp->b_rptr; 722269Sericheng int err = 0; 723269Sericheng t_uscalar_t dl_err; 724269Sericheng queue_t *q = dsp->ds_wq; 725*8275SEric Cheng mac_perim_handle_t mph; 726269Sericheng 727269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 728269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 729269Sericheng dl_err = DL_OUTSTATE; 730269Sericheng goto failed; 731269Sericheng } 732269Sericheng 733269Sericheng if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) || 734269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 735269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 736269Sericheng dl_err = DL_BADPRIM; 737269Sericheng goto failed; 738269Sericheng } 739269Sericheng 740*8275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 741*8275SEric Cheng 742269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 743*8275SEric Cheng ((err = dls_active_set(dsp)) != 0)) { 744269Sericheng dl_err = DL_SYSERR; 745*8275SEric Cheng goto failed2; 7460Sstevel@tonic-gate } 7470Sstevel@tonic-gate 748*8275SEric Cheng err = dls_multicst_add(dsp, mp->b_rptr + dlp->dl_addr_offset); 749*8275SEric Cheng 750269Sericheng if (err != 0) { 751269Sericheng switch (err) { 752269Sericheng case EINVAL: 753269Sericheng dl_err = DL_BADADDR; 754269Sericheng err = 0; 755269Sericheng break; 756269Sericheng case ENOSPC: 757269Sericheng dl_err = DL_TOOMANY; 758269Sericheng err = 0; 759269Sericheng break; 760269Sericheng default: 761269Sericheng dl_err = DL_SYSERR; 762269Sericheng break; 763269Sericheng } 764*8275SEric Cheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 765*8275SEric Cheng dls_active_clear(dsp); 7665895Syz147064 767*8275SEric Cheng goto failed2; 768269Sericheng } 769269Sericheng 770*8275SEric Cheng mac_perim_exit(mph); 771*8275SEric Cheng 772269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 773269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 774*8275SEric Cheng dlokack(q, mp, DL_ENABMULTI_REQ); 775*8275SEric Cheng return; 776269Sericheng 777*8275SEric Cheng failed2: 778*8275SEric Cheng mac_perim_exit(mph); 779269Sericheng failed: 780269Sericheng dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err); 781269Sericheng } 782269Sericheng 783269Sericheng /* 784269Sericheng * DL_DISABMULTI_REQ 785269Sericheng */ 786*8275SEric Cheng static void 787*8275SEric Cheng proto_disabmulti_req(dld_str_t *dsp, mblk_t *mp) 788269Sericheng { 789*8275SEric Cheng dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)mp->b_rptr; 790269Sericheng int err = 0; 791269Sericheng t_uscalar_t dl_err; 792269Sericheng queue_t *q = dsp->ds_wq; 793*8275SEric Cheng mac_perim_handle_t mph; 794269Sericheng 795269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 796269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 797269Sericheng dl_err = DL_OUTSTATE; 798269Sericheng goto failed; 799269Sericheng } 800269Sericheng 801269Sericheng if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) || 802269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 803269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 804269Sericheng dl_err = DL_BADPRIM; 805269Sericheng goto failed; 806269Sericheng } 807269Sericheng 808*8275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 809*8275SEric Cheng err = dls_multicst_remove(dsp, mp->b_rptr + dlp->dl_addr_offset); 810*8275SEric Cheng mac_perim_exit(mph); 811*8275SEric Cheng 812269Sericheng if (err != 0) { 813*8275SEric Cheng switch (err) { 814269Sericheng case EINVAL: 815269Sericheng dl_err = DL_BADADDR; 816269Sericheng err = 0; 817269Sericheng break; 818*8275SEric Cheng 819269Sericheng case ENOENT: 820269Sericheng dl_err = DL_NOTENAB; 821269Sericheng err = 0; 822269Sericheng break; 823*8275SEric Cheng 824269Sericheng default: 825269Sericheng dl_err = DL_SYSERR; 826269Sericheng break; 827269Sericheng } 828269Sericheng goto failed; 829269Sericheng } 830269Sericheng dlokack(q, mp, DL_DISABMULTI_REQ); 831*8275SEric Cheng return; 832269Sericheng failed: 833269Sericheng dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err); 8340Sstevel@tonic-gate } 8350Sstevel@tonic-gate 8360Sstevel@tonic-gate /* 837269Sericheng * DL_PHYS_ADDR_REQ 8380Sstevel@tonic-gate */ 839*8275SEric Cheng static void 840*8275SEric Cheng proto_physaddr_req(dld_str_t *dsp, mblk_t *mp) 8410Sstevel@tonic-gate { 842*8275SEric Cheng dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)mp->b_rptr; 843269Sericheng queue_t *q = dsp->ds_wq; 844269Sericheng t_uscalar_t dl_err; 845269Sericheng char *addr; 846269Sericheng uint_t addr_length; 847269Sericheng 848269Sericheng if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) { 849269Sericheng dl_err = DL_BADPRIM; 850269Sericheng goto failed; 851269Sericheng } 852269Sericheng 853269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 854269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 855269Sericheng dl_err = DL_OUTSTATE; 856269Sericheng goto failed; 857269Sericheng } 8580Sstevel@tonic-gate 859269Sericheng if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR && 860269Sericheng dlp->dl_addr_type != DL_FACT_PHYS_ADDR) { 861269Sericheng dl_err = DL_UNSUPPORTED; 8620Sstevel@tonic-gate goto failed; 863269Sericheng } 8640Sstevel@tonic-gate 865269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 8666353Sdr146992 if (addr_length > 0) { 867*8275SEric Cheng addr = kmem_alloc(addr_length, KM_SLEEP); 868*8275SEric Cheng if (dlp->dl_addr_type == DL_CURR_PHYS_ADDR) 869*8275SEric Cheng mac_unicast_primary_get(dsp->ds_mh, (uint8_t *)addr); 870*8275SEric Cheng else 871*8275SEric Cheng bcopy(dsp->ds_mip->mi_unicst_addr, addr, addr_length); 8720Sstevel@tonic-gate 8736353Sdr146992 dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length); 8746353Sdr146992 kmem_free(addr, addr_length); 8756353Sdr146992 } else { 8766353Sdr146992 dlphysaddrack(q, mp, NULL, 0); 8776353Sdr146992 } 878*8275SEric Cheng return; 8790Sstevel@tonic-gate failed: 880269Sericheng dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0); 881269Sericheng } 8820Sstevel@tonic-gate 883269Sericheng /* 884269Sericheng * DL_SET_PHYS_ADDR_REQ 885269Sericheng */ 886*8275SEric Cheng static void 887*8275SEric Cheng proto_setphysaddr_req(dld_str_t *dsp, mblk_t *mp) 888269Sericheng { 889*8275SEric Cheng dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)mp->b_rptr; 890269Sericheng int err = 0; 891269Sericheng t_uscalar_t dl_err; 892269Sericheng queue_t *q = dsp->ds_wq; 893*8275SEric Cheng mac_perim_handle_t mph; 8940Sstevel@tonic-gate 895269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 896269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 897269Sericheng dl_err = DL_OUTSTATE; 898269Sericheng goto failed; 899269Sericheng } 900269Sericheng 901269Sericheng if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) || 902269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 903269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 904269Sericheng dl_err = DL_BADPRIM; 905269Sericheng goto failed; 9060Sstevel@tonic-gate } 9070Sstevel@tonic-gate 908*8275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 909*8275SEric Cheng 910269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 911*8275SEric Cheng ((err = dls_active_set(dsp)) != 0)) { 912269Sericheng dl_err = DL_SYSERR; 913*8275SEric Cheng goto failed2; 914269Sericheng } 915269Sericheng 916*8275SEric Cheng err = mac_unicast_primary_set(dsp->ds_mh, 917*8275SEric Cheng mp->b_rptr + dlp->dl_addr_offset); 918269Sericheng if (err != 0) { 919269Sericheng switch (err) { 920269Sericheng case EINVAL: 921269Sericheng dl_err = DL_BADADDR; 922269Sericheng err = 0; 923269Sericheng break; 924269Sericheng 925269Sericheng default: 926269Sericheng dl_err = DL_SYSERR; 927269Sericheng break; 928269Sericheng } 929*8275SEric Cheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 930*8275SEric Cheng dls_active_clear(dsp); 9315895Syz147064 932*8275SEric Cheng goto failed2; 933269Sericheng 934269Sericheng } 9355895Syz147064 936*8275SEric Cheng mac_perim_exit(mph); 937*8275SEric Cheng 938269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 939269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 940*8275SEric Cheng dlokack(q, mp, DL_SET_PHYS_ADDR_REQ); 941*8275SEric Cheng return; 942269Sericheng 943*8275SEric Cheng failed2: 944*8275SEric Cheng mac_perim_exit(mph); 945269Sericheng failed: 946269Sericheng dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err); 947269Sericheng } 948269Sericheng 949269Sericheng /* 950269Sericheng * DL_UDQOS_REQ 951269Sericheng */ 952*8275SEric Cheng static void 953*8275SEric Cheng proto_udqos_req(dld_str_t *dsp, mblk_t *mp) 954269Sericheng { 955*8275SEric Cheng dl_udqos_req_t *dlp = (dl_udqos_req_t *)mp->b_rptr; 956269Sericheng dl_qos_cl_sel1_t *selp; 957269Sericheng int off, len; 958269Sericheng t_uscalar_t dl_err; 959269Sericheng queue_t *q = dsp->ds_wq; 960269Sericheng 961269Sericheng off = dlp->dl_qos_offset; 962269Sericheng len = dlp->dl_qos_length; 963269Sericheng 964269Sericheng if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) { 965269Sericheng dl_err = DL_BADPRIM; 966269Sericheng goto failed; 967269Sericheng } 968269Sericheng 969269Sericheng selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off); 970269Sericheng if (selp->dl_qos_type != DL_QOS_CL_SEL1) { 971269Sericheng dl_err = DL_BADQOSTYPE; 972269Sericheng goto failed; 973269Sericheng } 974269Sericheng 9752760Sdg199075 if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 || 976269Sericheng selp->dl_priority < 0) { 977269Sericheng dl_err = DL_BADQOSPARAM; 978269Sericheng goto failed; 979269Sericheng } 980269Sericheng 9815895Syz147064 dsp->ds_pri = selp->dl_priority; 982269Sericheng dlokack(q, mp, DL_UDQOS_REQ); 983*8275SEric Cheng return; 984269Sericheng failed: 985269Sericheng dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0); 9860Sstevel@tonic-gate } 9870Sstevel@tonic-gate 9881184Skrgopi static boolean_t 9891184Skrgopi check_ip_above(queue_t *q) 9901184Skrgopi { 9911184Skrgopi queue_t *next_q; 9921184Skrgopi boolean_t ret = B_TRUE; 9931184Skrgopi 9941184Skrgopi claimstr(q); 9951184Skrgopi next_q = q->q_next; 9961184Skrgopi if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0) 9971184Skrgopi ret = B_FALSE; 9981184Skrgopi releasestr(q); 9991184Skrgopi return (ret); 10001184Skrgopi } 10011184Skrgopi 10020Sstevel@tonic-gate /* 1003269Sericheng * DL_CAPABILITY_REQ 10040Sstevel@tonic-gate */ 1005*8275SEric Cheng static void 1006*8275SEric Cheng proto_capability_req(dld_str_t *dsp, mblk_t *mp) 10070Sstevel@tonic-gate { 1008*8275SEric Cheng dl_capability_req_t *dlp = (dl_capability_req_t *)mp->b_rptr; 1009269Sericheng dl_capability_sub_t *sp; 1010269Sericheng size_t size, len; 1011269Sericheng offset_t off, end; 1012269Sericheng t_uscalar_t dl_err; 1013269Sericheng queue_t *q = dsp->ds_wq; 1014269Sericheng 1015269Sericheng if (MBLKL(mp) < sizeof (dl_capability_req_t)) { 1016269Sericheng dl_err = DL_BADPRIM; 1017269Sericheng goto failed; 1018269Sericheng } 1019269Sericheng 1020269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1021269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1022269Sericheng dl_err = DL_OUTSTATE; 1023269Sericheng goto failed; 1024269Sericheng } 1025269Sericheng 1026269Sericheng /* 1027269Sericheng * This request is overloaded. If there are no requested capabilities 1028269Sericheng * then we just want to acknowledge with all the capabilities we 1029269Sericheng * support. Otherwise we enable the set of capabilities requested. 1030269Sericheng */ 1031269Sericheng if (dlp->dl_sub_length == 0) { 1032*8275SEric Cheng proto_capability_advertise(dsp, mp); 1033*8275SEric Cheng return; 1034269Sericheng } 1035269Sericheng 1036269Sericheng if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) { 1037269Sericheng dl_err = DL_BADPRIM; 1038269Sericheng goto failed; 1039269Sericheng } 1040269Sericheng 1041269Sericheng dlp->dl_primitive = DL_CAPABILITY_ACK; 1042269Sericheng 1043269Sericheng off = dlp->dl_sub_offset; 1044269Sericheng len = dlp->dl_sub_length; 10450Sstevel@tonic-gate 10460Sstevel@tonic-gate /* 1047269Sericheng * Walk the list of capabilities to be enabled. 10480Sstevel@tonic-gate */ 1049269Sericheng for (end = off + len; off < end; ) { 1050269Sericheng sp = (dl_capability_sub_t *)(mp->b_rptr + off); 1051269Sericheng size = sizeof (dl_capability_sub_t) + sp->dl_length; 1052269Sericheng 1053269Sericheng if (off + size > end || 1054269Sericheng !IS_P2ALIGNED(off, sizeof (uint32_t))) { 1055269Sericheng dl_err = DL_BADPRIM; 1056269Sericheng goto failed; 1057269Sericheng } 1058269Sericheng 1059269Sericheng switch (sp->dl_cap) { 1060269Sericheng /* 1061269Sericheng * TCP/IP checksum offload to hardware. 1062269Sericheng */ 1063269Sericheng case DL_CAPAB_HCKSUM: { 1064269Sericheng dl_capab_hcksum_t *hcksump; 1065269Sericheng dl_capab_hcksum_t hcksum; 1066269Sericheng 1067269Sericheng hcksump = (dl_capab_hcksum_t *)&sp[1]; 1068269Sericheng /* 1069269Sericheng * Copy for alignment. 1070269Sericheng */ 1071269Sericheng bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t)); 1072269Sericheng dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 1073269Sericheng bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t)); 1074269Sericheng break; 1075269Sericheng } 1076269Sericheng 1077*8275SEric Cheng case DL_CAPAB_DLD: { 1078*8275SEric Cheng dl_capab_dld_t *dldp; 1079*8275SEric Cheng dl_capab_dld_t dld; 10803115Syl150051 1081*8275SEric Cheng dldp = (dl_capab_dld_t *)&sp[1]; 1082269Sericheng /* 1083269Sericheng * Copy for alignment. 1084269Sericheng */ 1085*8275SEric Cheng bcopy(dldp, &dld, sizeof (dl_capab_dld_t)); 1086*8275SEric Cheng dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq); 1087*8275SEric Cheng bcopy(&dld, dldp, sizeof (dl_capab_dld_t)); 1088269Sericheng break; 1089269Sericheng } 1090269Sericheng default: 1091269Sericheng break; 1092269Sericheng } 1093269Sericheng off += size; 1094269Sericheng } 1095269Sericheng qreply(q, mp); 1096*8275SEric Cheng return; 1097269Sericheng failed: 1098269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0); 10990Sstevel@tonic-gate } 11000Sstevel@tonic-gate 11010Sstevel@tonic-gate /* 1102269Sericheng * DL_NOTIFY_REQ 11030Sstevel@tonic-gate */ 1104*8275SEric Cheng static void 1105*8275SEric Cheng proto_notify_req(dld_str_t *dsp, mblk_t *mp) 11060Sstevel@tonic-gate { 1107*8275SEric Cheng dl_notify_req_t *dlp = (dl_notify_req_t *)mp->b_rptr; 1108269Sericheng t_uscalar_t dl_err; 1109269Sericheng queue_t *q = dsp->ds_wq; 1110269Sericheng uint_t note = 1111269Sericheng DL_NOTE_PROMISC_ON_PHYS | 1112269Sericheng DL_NOTE_PROMISC_OFF_PHYS | 1113269Sericheng DL_NOTE_PHYS_ADDR | 1114269Sericheng DL_NOTE_LINK_UP | 1115269Sericheng DL_NOTE_LINK_DOWN | 11162311Sseb DL_NOTE_CAPAB_RENEG | 11172311Sseb DL_NOTE_SPEED; 11180Sstevel@tonic-gate 1119269Sericheng if (MBLKL(mp) < sizeof (dl_notify_req_t)) { 1120269Sericheng dl_err = DL_BADPRIM; 1121269Sericheng goto failed; 1122269Sericheng } 11230Sstevel@tonic-gate 1124269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1125269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1126269Sericheng dl_err = DL_OUTSTATE; 1127269Sericheng goto failed; 11280Sstevel@tonic-gate } 11290Sstevel@tonic-gate 11305895Syz147064 note &= ~(mac_no_notification(dsp->ds_mh)); 11315895Syz147064 1132269Sericheng /* 1133269Sericheng * Cache the notifications that are being enabled. 1134269Sericheng */ 1135269Sericheng dsp->ds_notifications = dlp->dl_notifications & note; 1136269Sericheng /* 1137269Sericheng * The ACK carries all notifications regardless of which set is 1138269Sericheng * being enabled. 1139269Sericheng */ 1140269Sericheng dlnotifyack(q, mp, note); 1141269Sericheng 1142269Sericheng /* 1143*8275SEric Cheng * Generate DL_NOTIFY_IND messages for each enabled notification. 1144269Sericheng */ 1145269Sericheng if (dsp->ds_notifications != 0) { 1146269Sericheng dld_str_notify_ind(dsp); 1147269Sericheng } 1148*8275SEric Cheng return; 1149269Sericheng failed: 1150269Sericheng dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0); 11510Sstevel@tonic-gate } 11520Sstevel@tonic-gate 11530Sstevel@tonic-gate /* 1154*8275SEric Cheng * DL_UINTDATA_REQ 11550Sstevel@tonic-gate */ 11565895Syz147064 void 1157*8275SEric Cheng proto_unitdata_req(dld_str_t *dsp, mblk_t *mp) 11580Sstevel@tonic-gate { 1159269Sericheng queue_t *q = dsp->ds_wq; 11605895Syz147064 dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_rptr; 1161269Sericheng off_t off; 1162269Sericheng size_t len, size; 1163269Sericheng const uint8_t *addr; 1164269Sericheng uint16_t sap; 1165269Sericheng uint_t addr_length; 11662311Sseb mblk_t *bp, *payload; 1167269Sericheng uint32_t start, stuff, end, value, flags; 1168269Sericheng t_uscalar_t dl_err; 11695903Ssowmini uint_t max_sdu; 1170269Sericheng 1171269Sericheng if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) { 1172*8275SEric Cheng dlerrorack(q, mp, DL_UNITDATA_REQ, DL_BADPRIM, 0); 1173*8275SEric Cheng return; 1174269Sericheng } 1175269Sericheng 1176*8275SEric Cheng mutex_enter(&dsp->ds_lock); 1177*8275SEric Cheng if (dsp->ds_dlstate != DL_IDLE) { 1178*8275SEric Cheng mutex_exit(&dsp->ds_lock); 1179*8275SEric Cheng dlerrorack(q, mp, DL_UNITDATA_REQ, DL_OUTSTATE, 0); 1180*8275SEric Cheng return; 1181*8275SEric Cheng } 1182*8275SEric Cheng DLD_DATATHR_INC(dsp); 1183*8275SEric Cheng mutex_exit(&dsp->ds_lock); 1184*8275SEric Cheng 1185269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 1186269Sericheng 1187269Sericheng off = dlp->dl_dest_addr_offset; 1188269Sericheng len = dlp->dl_dest_addr_length; 1189269Sericheng 1190269Sericheng if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) { 1191269Sericheng dl_err = DL_BADPRIM; 1192269Sericheng goto failed; 1193269Sericheng } 1194269Sericheng 1195269Sericheng if (len != addr_length + sizeof (uint16_t)) { 1196269Sericheng dl_err = DL_BADADDR; 1197269Sericheng goto failed; 1198269Sericheng } 1199269Sericheng 1200269Sericheng addr = mp->b_rptr + off; 1201269Sericheng sap = *(uint16_t *)(mp->b_rptr + off + addr_length); 1202269Sericheng 1203269Sericheng /* 1204269Sericheng * Check the length of the packet and the block types. 1205269Sericheng */ 1206269Sericheng size = 0; 12072311Sseb payload = mp->b_cont; 12082311Sseb for (bp = payload; bp != NULL; bp = bp->b_cont) { 1209269Sericheng if (DB_TYPE(bp) != M_DATA) 1210269Sericheng goto baddata; 1211269Sericheng 1212269Sericheng size += MBLKL(bp); 1213269Sericheng } 1214269Sericheng 12155903Ssowmini mac_sdu_get(dsp->ds_mh, NULL, &max_sdu); 12165903Ssowmini if (size > max_sdu) 1217269Sericheng goto baddata; 1218269Sericheng 1219269Sericheng /* 1220269Sericheng * Build a packet header. 1221269Sericheng */ 1222*8275SEric Cheng if ((bp = dls_header(dsp, addr, sap, dlp->dl_priority.dl_max, 12232760Sdg199075 &payload)) == NULL) { 1224269Sericheng dl_err = DL_BADADDR; 1225269Sericheng goto failed; 1226269Sericheng } 1227269Sericheng 1228269Sericheng /* 1229269Sericheng * We no longer need the M_PROTO header, so free it. 1230269Sericheng */ 1231269Sericheng freeb(mp); 1232269Sericheng 1233269Sericheng /* 1234269Sericheng * Transfer the checksum offload information if it is present. 1235269Sericheng */ 12362311Sseb hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value, 1237269Sericheng &flags); 12382311Sseb (void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0); 1239269Sericheng 1240269Sericheng /* 1241269Sericheng * Link the payload onto the new header. 1242269Sericheng */ 1243269Sericheng ASSERT(bp->b_cont == NULL); 12442311Sseb bp->b_cont = payload; 1245*8275SEric Cheng 1246*8275SEric Cheng /* 1247*8275SEric Cheng * No lock can be held across modules and putnext()'s, 1248*8275SEric Cheng * which can happen here with the call from DLD_TX(). 1249*8275SEric Cheng */ 1250*8275SEric Cheng if (DLD_TX(dsp, bp, 0, 0) != NULL) { 1251*8275SEric Cheng /* flow-controlled */ 1252*8275SEric Cheng DLD_SETQFULL(dsp); 1253*8275SEric Cheng } 1254*8275SEric Cheng DLD_DATATHR_DCR(dsp); 12555895Syz147064 return; 1256*8275SEric Cheng 1257269Sericheng failed: 1258269Sericheng dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0); 1259*8275SEric Cheng DLD_DATATHR_DCR(dsp); 12605895Syz147064 return; 1261269Sericheng 1262269Sericheng baddata: 1263269Sericheng dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0); 1264*8275SEric Cheng DLD_DATATHR_DCR(dsp); 1265269Sericheng } 1266269Sericheng 1267269Sericheng /* 1268269Sericheng * DL_PASSIVE_REQ 1269269Sericheng */ 1270*8275SEric Cheng static void 1271*8275SEric Cheng proto_passive_req(dld_str_t *dsp, mblk_t *mp) 1272269Sericheng { 1273269Sericheng t_uscalar_t dl_err; 1274269Sericheng 12755895Syz147064 /* 1276269Sericheng * If we've already become active by issuing an active primitive, 1277269Sericheng * then it's too late to try to become passive. 1278269Sericheng */ 1279269Sericheng if (dsp->ds_passivestate == DLD_ACTIVE) { 1280269Sericheng dl_err = DL_OUTSTATE; 1281269Sericheng goto failed; 1282269Sericheng } 1283269Sericheng 1284269Sericheng if (MBLKL(mp) < sizeof (dl_passive_req_t)) { 1285269Sericheng dl_err = DL_BADPRIM; 1286269Sericheng goto failed; 1287269Sericheng } 1288269Sericheng 1289269Sericheng dsp->ds_passivestate = DLD_PASSIVE; 1290269Sericheng dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ); 1291*8275SEric Cheng return; 1292269Sericheng failed: 1293269Sericheng dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0); 1294269Sericheng } 1295269Sericheng 1296*8275SEric Cheng 1297269Sericheng /* 1298269Sericheng * Catch-all handler. 1299269Sericheng */ 1300*8275SEric Cheng static void 1301*8275SEric Cheng proto_req(dld_str_t *dsp, mblk_t *mp) 1302*8275SEric Cheng { 1303*8275SEric Cheng union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr; 1304*8275SEric Cheng 1305*8275SEric Cheng dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0); 1306*8275SEric Cheng } 1307*8275SEric Cheng 1308*8275SEric Cheng static int 1309*8275SEric Cheng dld_capab_perim(dld_str_t *dsp, void *data, uint_t flags) 1310269Sericheng { 1311*8275SEric Cheng switch (flags) { 1312*8275SEric Cheng case DLD_ENABLE: 1313*8275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, (mac_perim_handle_t *)data); 1314*8275SEric Cheng return (0); 1315*8275SEric Cheng 1316*8275SEric Cheng case DLD_DISABLE: 1317*8275SEric Cheng mac_perim_exit((mac_perim_handle_t)data); 1318*8275SEric Cheng return (0); 1319*8275SEric Cheng 1320*8275SEric Cheng case DLD_QUERY: 1321*8275SEric Cheng return (mac_perim_held(dsp->ds_mh)); 1322*8275SEric Cheng } 1323*8275SEric Cheng return (0); 13240Sstevel@tonic-gate } 13250Sstevel@tonic-gate 1326*8275SEric Cheng static int 1327*8275SEric Cheng dld_capab_direct(dld_str_t *dsp, void *data, uint_t flags) 13280Sstevel@tonic-gate { 1329*8275SEric Cheng dld_capab_direct_t *direct = data; 1330*8275SEric Cheng 1331*8275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); 13320Sstevel@tonic-gate 1333*8275SEric Cheng switch (flags) { 1334*8275SEric Cheng case DLD_ENABLE: 1335*8275SEric Cheng dls_rx_set(dsp, (dls_rx_t)direct->di_rx_cf, 1336*8275SEric Cheng direct->di_rx_ch); 1337*8275SEric Cheng /* 1338*8275SEric Cheng * TODO: XXXGopi 1339*8275SEric Cheng * 1340*8275SEric Cheng * Direct pointer to functions in the MAC layer 1341*8275SEric Cheng * should be passed here: 1342*8275SEric Cheng * 1343*8275SEric Cheng * 1) pass mac_tx() and mac_client_handle instead 1344*8275SEric Cheng * of str_mdata_fastpath_put() and dld_str_t. But 1345*8275SEric Cheng * not done presently because of some VLAN 1346*8275SEric Cheng * processing stuff in str_mdata_fastpath_put(). 1347*8275SEric Cheng * 1348*8275SEric Cheng * 2) pass a MAC layer callback instead of 1349*8275SEric Cheng * dld_flow_ctl_callb(). 1350*8275SEric Cheng */ 1351*8275SEric Cheng direct->di_tx_df = (uintptr_t)str_mdata_fastpath_put; 1352*8275SEric Cheng direct->di_tx_dh = dsp; 1353*8275SEric Cheng 1354*8275SEric Cheng direct->di_tx_cb_df = (uintptr_t)mac_client_tx_notify; 1355*8275SEric Cheng direct->di_tx_cb_dh = dsp->ds_mch; 1356*8275SEric Cheng dsp->ds_direct = B_TRUE; 1357*8275SEric Cheng 1358*8275SEric Cheng return (0); 1359269Sericheng 1360*8275SEric Cheng case DLD_DISABLE: 1361*8275SEric Cheng dls_rx_set(dsp, (dsp->ds_mode == DLD_FASTPATH) ? 1362*8275SEric Cheng dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp); 1363*8275SEric Cheng dsp->ds_direct = B_FALSE; 1364*8275SEric Cheng 1365*8275SEric Cheng return (0); 1366*8275SEric Cheng } 1367*8275SEric Cheng return (ENOTSUP); 1368*8275SEric Cheng } 13690Sstevel@tonic-gate 1370*8275SEric Cheng /* 1371*8275SEric Cheng * dld_capab_poll_enable() 1372*8275SEric Cheng * 1373*8275SEric Cheng * This function is misnamed. All polling and fanouts are run out of the 1374*8275SEric Cheng * lower mac (in case of VNIC and the only mac in case of NICs). The 1375*8275SEric Cheng * availability of Rx ring and promiscous mode is all taken care between 1376*8275SEric Cheng * the soft ring set (mac_srs), the Rx ring, and S/W classifier. Any 1377*8275SEric Cheng * fanout necessary is done by the soft rings that are part of the 1378*8275SEric Cheng * mac_srs (by default mac_srs sends the packets up via a TCP and 1379*8275SEric Cheng * non TCP soft ring). 1380*8275SEric Cheng * 1381*8275SEric Cheng * The mac_srs (or its associated soft rings) always store the ill_rx_ring 1382*8275SEric Cheng * (the cookie returned when they registered with IP during plumb) as their 1383*8275SEric Cheng * 2nd argument which is passed up as mac_resource_handle_t. The upcall 1384*8275SEric Cheng * function and 1st argument is what the caller registered when they 1385*8275SEric Cheng * called mac_rx_classify_flow_add() to register the flow. For VNIC, 1386*8275SEric Cheng * the function is vnic_rx and argument is vnic_t. For regular NIC 1387*8275SEric Cheng * case, it mac_rx_default and mac_handle_t. As explained above, the 1388*8275SEric Cheng * mac_srs (or its soft ring) will add the ill_rx_ring (mac_resource_handle_t) 1389*8275SEric Cheng * from its stored 2nd argument. 1390*8275SEric Cheng */ 1391*8275SEric Cheng static int 1392*8275SEric Cheng dld_capab_poll_enable(dld_str_t *dsp, dld_capab_poll_t *poll) 1393*8275SEric Cheng { 1394*8275SEric Cheng if (dsp->ds_polling) 1395*8275SEric Cheng return (EINVAL); 1396*8275SEric Cheng 1397*8275SEric Cheng if ((dld_opt & DLD_OPT_NO_POLL) != 0 || dsp->ds_mode == DLD_RAW) 1398*8275SEric Cheng return (ENOTSUP); 13990Sstevel@tonic-gate 14000Sstevel@tonic-gate /* 1401*8275SEric Cheng * Enable client polling if and only if DLS bypass is possible. 1402*8275SEric Cheng * Special cases like VLANs need DLS processing in the Rx data path. 1403*8275SEric Cheng * In such a case we can neither allow the client (IP) to directly 1404*8275SEric Cheng * poll the softring (since DLS processing hasn't been done) nor can 1405*8275SEric Cheng * we allow DLS bypass. 14060Sstevel@tonic-gate */ 1407*8275SEric Cheng if (!mac_rx_bypass_set(dsp->ds_mch, dsp->ds_rx, dsp->ds_rx_arg)) 1408*8275SEric Cheng return (ENOTSUP); 14090Sstevel@tonic-gate 14100Sstevel@tonic-gate /* 1411*8275SEric Cheng * Register soft ring resources. This will come in handy later if 1412*8275SEric Cheng * the user decides to modify CPU bindings to use more CPUs for the 1413*8275SEric Cheng * device in which case we will switch to fanout using soft rings. 14140Sstevel@tonic-gate */ 1415*8275SEric Cheng mac_resource_set_common(dsp->ds_mch, 1416*8275SEric Cheng (mac_resource_add_t)poll->poll_ring_add_cf, 1417*8275SEric Cheng (mac_resource_remove_t)poll->poll_ring_remove_cf, 1418*8275SEric Cheng (mac_resource_quiesce_t)poll->poll_ring_quiesce_cf, 1419*8275SEric Cheng (mac_resource_restart_t)poll->poll_ring_restart_cf, 1420*8275SEric Cheng (mac_resource_bind_t)poll->poll_ring_bind_cf, 1421*8275SEric Cheng poll->poll_ring_ch); 1422*8275SEric Cheng 1423*8275SEric Cheng mac_client_poll_enable(dsp->ds_mch); 14240Sstevel@tonic-gate 14250Sstevel@tonic-gate dsp->ds_polling = B_TRUE; 1426*8275SEric Cheng return (0); 1427*8275SEric Cheng } 1428*8275SEric Cheng 1429*8275SEric Cheng /* ARGSUSED */ 1430*8275SEric Cheng static int 1431*8275SEric Cheng dld_capab_poll_disable(dld_str_t *dsp, dld_capab_poll_t *poll) 1432*8275SEric Cheng { 1433*8275SEric Cheng if (!dsp->ds_polling) 1434*8275SEric Cheng return (EINVAL); 1435*8275SEric Cheng 1436*8275SEric Cheng mac_client_poll_disable(dsp->ds_mch); 1437*8275SEric Cheng mac_resource_set(dsp->ds_mch, NULL, NULL); 1438*8275SEric Cheng 1439*8275SEric Cheng dsp->ds_polling = B_FALSE; 1440*8275SEric Cheng return (0); 14410Sstevel@tonic-gate } 14420Sstevel@tonic-gate 1443*8275SEric Cheng static int 1444*8275SEric Cheng dld_capab_poll(dld_str_t *dsp, void *data, uint_t flags) 1445*8275SEric Cheng { 1446*8275SEric Cheng dld_capab_poll_t *poll = data; 1447*8275SEric Cheng 1448*8275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); 1449*8275SEric Cheng 1450*8275SEric Cheng switch (flags) { 1451*8275SEric Cheng case DLD_ENABLE: 1452*8275SEric Cheng return (dld_capab_poll_enable(dsp, poll)); 1453*8275SEric Cheng case DLD_DISABLE: 1454*8275SEric Cheng return (dld_capab_poll_disable(dsp, poll)); 1455*8275SEric Cheng } 1456*8275SEric Cheng return (ENOTSUP); 1457*8275SEric Cheng } 1458*8275SEric Cheng 1459*8275SEric Cheng static int 1460*8275SEric Cheng dld_capab_lso(dld_str_t *dsp, void *data, uint_t flags) 14611184Skrgopi { 1462*8275SEric Cheng dld_capab_lso_t *lso = data; 1463*8275SEric Cheng 1464*8275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); 1465*8275SEric Cheng 1466*8275SEric Cheng switch (flags) { 1467*8275SEric Cheng case DLD_ENABLE: { 1468*8275SEric Cheng mac_capab_lso_t mac_lso; 14691184Skrgopi 1470*8275SEric Cheng /* 1471*8275SEric Cheng * Check if LSO is supported on this MAC & enable LSO 1472*8275SEric Cheng * accordingly. 1473*8275SEric Cheng */ 1474*8275SEric Cheng if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) { 1475*8275SEric Cheng lso->lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max; 1476*8275SEric Cheng lso->lso_flags = 0; 1477*8275SEric Cheng /* translate the flag for mac clients */ 1478*8275SEric Cheng if ((mac_lso.lso_flags & LSO_TX_BASIC_TCP_IPV4) != 0) 1479*8275SEric Cheng lso->lso_flags |= DLD_LSO_TX_BASIC_TCP_IPV4; 1480*8275SEric Cheng dsp->ds_lso = B_TRUE; 1481*8275SEric Cheng dsp->ds_lso_max = lso->lso_max; 1482*8275SEric Cheng } else { 1483*8275SEric Cheng dsp->ds_lso = B_FALSE; 1484*8275SEric Cheng dsp->ds_lso_max = 0; 1485*8275SEric Cheng return (ENOTSUP); 1486*8275SEric Cheng } 1487*8275SEric Cheng return (0); 1488*8275SEric Cheng } 1489*8275SEric Cheng case DLD_DISABLE: { 1490*8275SEric Cheng dsp->ds_lso = B_FALSE; 1491*8275SEric Cheng dsp->ds_lso_max = 0; 1492*8275SEric Cheng return (0); 1493*8275SEric Cheng } 1494*8275SEric Cheng } 1495*8275SEric Cheng return (ENOTSUP); 1496*8275SEric Cheng } 1497*8275SEric Cheng 1498*8275SEric Cheng static int 1499*8275SEric Cheng dld_capab(dld_str_t *dsp, uint_t type, void *data, uint_t flags) 1500*8275SEric Cheng { 1501*8275SEric Cheng int err; 15021184Skrgopi 15031184Skrgopi /* 1504*8275SEric Cheng * Don't enable direct callback capabilities unless the caller is 1505*8275SEric Cheng * the IP client. When a module is inserted in a stream (_I_INSERT) 1506*8275SEric Cheng * the stack initiates capability disable, but due to races, the 1507*8275SEric Cheng * module insertion may complete before the capability disable 1508*8275SEric Cheng * completes. So we limit the check to DLD_ENABLE case. 15091184Skrgopi */ 1510*8275SEric Cheng if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) && 1511*8275SEric Cheng (dsp->ds_sap != ETHERTYPE_IP || !check_ip_above(dsp->ds_rq))) { 1512*8275SEric Cheng return (ENOTSUP); 1513*8275SEric Cheng } 15141184Skrgopi 1515*8275SEric Cheng switch (type) { 1516*8275SEric Cheng case DLD_CAPAB_DIRECT: 1517*8275SEric Cheng err = dld_capab_direct(dsp, data, flags); 1518*8275SEric Cheng break; 15191184Skrgopi 1520*8275SEric Cheng case DLD_CAPAB_POLL: 1521*8275SEric Cheng err = dld_capab_poll(dsp, data, flags); 1522*8275SEric Cheng break; 15231184Skrgopi 1524*8275SEric Cheng case DLD_CAPAB_PERIM: 1525*8275SEric Cheng err = dld_capab_perim(dsp, data, flags); 1526*8275SEric Cheng break; 15271184Skrgopi 1528*8275SEric Cheng case DLD_CAPAB_LSO: 1529*8275SEric Cheng err = dld_capab_lso(dsp, data, flags); 1530*8275SEric Cheng break; 1531*8275SEric Cheng 1532*8275SEric Cheng default: 1533*8275SEric Cheng err = ENOTSUP; 1534*8275SEric Cheng break; 15351184Skrgopi } 1536*8275SEric Cheng 1537*8275SEric Cheng return (err); 15381184Skrgopi } 15391184Skrgopi 15400Sstevel@tonic-gate /* 15410Sstevel@tonic-gate * DL_CAPABILITY_ACK/DL_ERROR_ACK 15420Sstevel@tonic-gate */ 1543*8275SEric Cheng static void 1544269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp) 15450Sstevel@tonic-gate { 15460Sstevel@tonic-gate dl_capability_ack_t *dlap; 15470Sstevel@tonic-gate dl_capability_sub_t *dlsp; 15480Sstevel@tonic-gate size_t subsize; 1549*8275SEric Cheng dl_capab_dld_t dld; 15500Sstevel@tonic-gate dl_capab_hcksum_t hcksum; 15510Sstevel@tonic-gate dl_capab_zerocopy_t zcopy; 15520Sstevel@tonic-gate uint8_t *ptr; 1553269Sericheng queue_t *q = dsp->ds_wq; 1554269Sericheng mblk_t *mp1; 1555*8275SEric Cheng boolean_t is_vlan; 15565895Syz147064 boolean_t hcksum_capable = B_FALSE; 15575895Syz147064 boolean_t zcopy_capable = B_FALSE; 1558*8275SEric Cheng boolean_t dld_capable = B_FALSE; 15590Sstevel@tonic-gate 15600Sstevel@tonic-gate /* 15610Sstevel@tonic-gate * Initially assume no capabilities. 15620Sstevel@tonic-gate */ 15630Sstevel@tonic-gate subsize = 0; 1564*8275SEric Cheng is_vlan = (mac_client_vid(dsp->ds_mch) != VLAN_ID_NONE); 15650Sstevel@tonic-gate 15660Sstevel@tonic-gate /* 15675895Syz147064 * Check if checksum offload is supported on this MAC. Don't 15685895Syz147064 * advertise DL_CAPAB_HCKSUM if the underlying MAC is VLAN incapable, 15695895Syz147064 * since it might not be able to do the hardware checksum offload 15705895Syz147064 * with the correct offset. 15710Sstevel@tonic-gate */ 15725895Syz147064 bzero(&hcksum, sizeof (dl_capab_hcksum_t)); 15735895Syz147064 if ((!is_vlan || (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_NATIVEVLAN, 15745895Syz147064 NULL))) && mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM, 15752311Sseb &hcksum.hcksum_txflags)) { 15765895Syz147064 if (hcksum.hcksum_txflags != 0) { 15775895Syz147064 hcksum_capable = B_TRUE; 15785895Syz147064 subsize += sizeof (dl_capability_sub_t) + 15795895Syz147064 sizeof (dl_capab_hcksum_t); 15805895Syz147064 } 15810Sstevel@tonic-gate } 15820Sstevel@tonic-gate 15830Sstevel@tonic-gate /* 15845895Syz147064 * Check if zerocopy is supported on this interface. 15855895Syz147064 * If advertising DL_CAPAB_ZEROCOPY has not been explicitly disabled 15865895Syz147064 * then reserve space for that capability. 15870Sstevel@tonic-gate */ 15885895Syz147064 if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_ZCOPY, NULL) && 15895895Syz147064 !(dld_opt & DLD_OPT_NO_ZEROCOPY)) { 15905895Syz147064 zcopy_capable = B_TRUE; 15910Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 15920Sstevel@tonic-gate sizeof (dl_capab_zerocopy_t); 15930Sstevel@tonic-gate } 15940Sstevel@tonic-gate 15950Sstevel@tonic-gate /* 1596*8275SEric Cheng * Direct capability negotiation interface between IP and DLD 1597*8275SEric Cheng */ 1598*8275SEric Cheng if (dsp->ds_sap == ETHERTYPE_IP && check_ip_above(dsp->ds_rq)) { 1599*8275SEric Cheng dld_capable = B_TRUE; 1600*8275SEric Cheng subsize += sizeof (dl_capability_sub_t) + 1601*8275SEric Cheng sizeof (dl_capab_dld_t); 1602*8275SEric Cheng } 1603*8275SEric Cheng 1604*8275SEric Cheng /* 1605269Sericheng * If there are no capabilities to advertise or if we 1606269Sericheng * can't allocate a response, send a DL_ERROR_ACK. 16070Sstevel@tonic-gate */ 16081184Skrgopi if ((mp1 = reallocb(mp, 1609269Sericheng sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) { 1610269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0); 1611*8275SEric Cheng return; 16120Sstevel@tonic-gate } 16130Sstevel@tonic-gate 1614269Sericheng mp = mp1; 1615269Sericheng DB_TYPE(mp) = M_PROTO; 1616269Sericheng mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize; 1617269Sericheng bzero(mp->b_rptr, MBLKL(mp)); 16180Sstevel@tonic-gate dlap = (dl_capability_ack_t *)mp->b_rptr; 16190Sstevel@tonic-gate dlap->dl_primitive = DL_CAPABILITY_ACK; 16200Sstevel@tonic-gate dlap->dl_sub_offset = sizeof (dl_capability_ack_t); 16210Sstevel@tonic-gate dlap->dl_sub_length = subsize; 16220Sstevel@tonic-gate ptr = (uint8_t *)&dlap[1]; 16230Sstevel@tonic-gate 16240Sstevel@tonic-gate /* 16250Sstevel@tonic-gate * TCP/IP checksum offload. 16260Sstevel@tonic-gate */ 16275895Syz147064 if (hcksum_capable) { 16280Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 16290Sstevel@tonic-gate 16300Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_HCKSUM; 16310Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_hcksum_t); 16320Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 16330Sstevel@tonic-gate 16340Sstevel@tonic-gate hcksum.hcksum_version = HCKSUM_VERSION_1; 16350Sstevel@tonic-gate dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 16360Sstevel@tonic-gate bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t)); 16370Sstevel@tonic-gate ptr += sizeof (dl_capab_hcksum_t); 16380Sstevel@tonic-gate } 16390Sstevel@tonic-gate 16400Sstevel@tonic-gate /* 16410Sstevel@tonic-gate * Zero copy 16420Sstevel@tonic-gate */ 16435895Syz147064 if (zcopy_capable) { 16440Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 16450Sstevel@tonic-gate 16460Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_ZEROCOPY; 16470Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_zerocopy_t); 16480Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 16490Sstevel@tonic-gate 16500Sstevel@tonic-gate bzero(&zcopy, sizeof (dl_capab_zerocopy_t)); 16510Sstevel@tonic-gate zcopy.zerocopy_version = ZEROCOPY_VERSION_1; 16520Sstevel@tonic-gate zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM; 16530Sstevel@tonic-gate 16540Sstevel@tonic-gate dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq); 16550Sstevel@tonic-gate bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t)); 16560Sstevel@tonic-gate ptr += sizeof (dl_capab_zerocopy_t); 16570Sstevel@tonic-gate } 16580Sstevel@tonic-gate 1659*8275SEric Cheng /* 1660*8275SEric Cheng * Direct capability negotiation interface between IP and DLD. 1661*8275SEric Cheng * Refer to dld.h for details. 1662*8275SEric Cheng */ 1663*8275SEric Cheng if (dld_capable) { 1664*8275SEric Cheng dlsp = (dl_capability_sub_t *)ptr; 1665*8275SEric Cheng dlsp->dl_cap = DL_CAPAB_DLD; 1666*8275SEric Cheng dlsp->dl_length = sizeof (dl_capab_dld_t); 1667*8275SEric Cheng ptr += sizeof (dl_capability_sub_t); 1668269Sericheng 1669*8275SEric Cheng bzero(&dld, sizeof (dl_capab_dld_t)); 1670*8275SEric Cheng dld.dld_version = DLD_CURRENT_VERSION; 1671*8275SEric Cheng dld.dld_capab = (uintptr_t)dld_capab; 1672*8275SEric Cheng dld.dld_capab_handle = (uintptr_t)dsp; 1673*8275SEric Cheng 1674*8275SEric Cheng dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq); 1675*8275SEric Cheng bcopy(&dld, ptr, sizeof (dl_capab_dld_t)); 1676*8275SEric Cheng ptr += sizeof (dl_capab_dld_t); 1677*8275SEric Cheng } 1678*8275SEric Cheng 1679*8275SEric Cheng ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize); 1680269Sericheng qreply(q, mp); 16810Sstevel@tonic-gate } 16825113Syz147064 16835113Syz147064 /* 16845113Syz147064 * Disable any enabled capabilities. 16855113Syz147064 */ 16865113Syz147064 void 16875113Syz147064 dld_capabilities_disable(dld_str_t *dsp) 16885113Syz147064 { 16895113Syz147064 if (dsp->ds_polling) 1690*8275SEric Cheng (void) dld_capab_poll_disable(dsp, NULL); 16915113Syz147064 } 1692