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 /* 228833SVenu.Iyer@Sun.COM * Copyright 2009 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> 308275SEric 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> 348275SEric Cheng #include <sys/mac_client.h> 358275SEric Cheng #include <sys/mac_client_impl.h> 368275SEric Cheng #include <sys/mac_client_priv.h> 370Sstevel@tonic-gate 388275SEric 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 468275SEric Cheng static void proto_capability_advertise(dld_str_t *, mblk_t *); 478275SEric 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 648275SEric Cheng dld_proto(dld_str_t *dsp, mblk_t *mp) 650Sstevel@tonic-gate { 660Sstevel@tonic-gate t_uscalar_t prim; 670Sstevel@tonic-gate 688275SEric Cheng if (MBLKL(mp) < sizeof (t_uscalar_t)) { 698275SEric Cheng freemsg(mp); 708275SEric Cheng return; 718275SEric Cheng } 728275SEric Cheng prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive; 730Sstevel@tonic-gate 74269Sericheng switch (prim) { 75269Sericheng case DL_INFO_REQ: 768275SEric Cheng proto_info_req(dsp, mp); 77269Sericheng break; 78269Sericheng case DL_BIND_REQ: 798275SEric Cheng proto_bind_req(dsp, mp); 80269Sericheng break; 81269Sericheng case DL_UNBIND_REQ: 828275SEric Cheng proto_unbind_req(dsp, mp); 838275SEric Cheng break; 848275SEric Cheng case DL_UNITDATA_REQ: 858275SEric Cheng proto_unitdata_req(dsp, mp); 86269Sericheng break; 87269Sericheng case DL_UDQOS_REQ: 888275SEric Cheng proto_udqos_req(dsp, mp); 89269Sericheng break; 90269Sericheng case DL_ATTACH_REQ: 918275SEric Cheng proto_attach_req(dsp, mp); 92269Sericheng break; 93269Sericheng case DL_DETACH_REQ: 948275SEric Cheng proto_detach_req(dsp, mp); 95269Sericheng break; 96269Sericheng case DL_ENABMULTI_REQ: 978275SEric Cheng proto_enabmulti_req(dsp, mp); 98269Sericheng break; 99269Sericheng case DL_DISABMULTI_REQ: 1008275SEric Cheng proto_disabmulti_req(dsp, mp); 101269Sericheng break; 102269Sericheng case DL_PROMISCON_REQ: 1038275SEric Cheng proto_promiscon_req(dsp, mp); 104269Sericheng break; 105269Sericheng case DL_PROMISCOFF_REQ: 1068275SEric Cheng proto_promiscoff_req(dsp, mp); 107269Sericheng break; 108269Sericheng case DL_PHYS_ADDR_REQ: 1098275SEric Cheng proto_physaddr_req(dsp, mp); 110269Sericheng break; 111269Sericheng case DL_SET_PHYS_ADDR_REQ: 1128275SEric Cheng proto_setphysaddr_req(dsp, mp); 113269Sericheng break; 114269Sericheng case DL_NOTIFY_REQ: 1158275SEric Cheng proto_notify_req(dsp, mp); 116269Sericheng break; 117269Sericheng case DL_CAPABILITY_REQ: 1188275SEric Cheng proto_capability_req(dsp, mp); 119269Sericheng break; 120269Sericheng case DL_PASSIVE_REQ: 1218275SEric Cheng proto_passive_req(dsp, mp); 122269Sericheng break; 123269Sericheng default: 1248275SEric 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 */ 1418275SEric Cheng static void 1428275SEric 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) 1628275SEric 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 2538874SSebastien.Roy@Sun.COM /* Only VLAN links and links that have a normal tag mode support QOS. */ 2548874SSebastien.Roy@Sun.COM if (mac_client_vid(dsp->ds_mch) != VLAN_ID_NONE || 2558874SSebastien.Roy@Sun.COM dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_NORMAL) { 2568874SSebastien.Roy@Sun.COM dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp; 2578874SSebastien.Roy@Sun.COM dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t); 2580Sstevel@tonic-gate 2598874SSebastien.Roy@Sun.COM rangep->dl_qos_type = DL_QOS_CL_RANGE1; 2608874SSebastien.Roy@Sun.COM rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN; 2618874SSebastien.Roy@Sun.COM rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN; 2628874SSebastien.Roy@Sun.COM rangep->dl_protection.dl_min = DL_UNKNOWN; 2638874SSebastien.Roy@Sun.COM rangep->dl_protection.dl_max = DL_UNKNOWN; 2648874SSebastien.Roy@Sun.COM rangep->dl_residual_error = DL_UNKNOWN; 2650Sstevel@tonic-gate 2668874SSebastien.Roy@Sun.COM /* 2678874SSebastien.Roy@Sun.COM * Specify the supported range of priorities. 2688874SSebastien.Roy@Sun.COM */ 2698874SSebastien.Roy@Sun.COM rangep->dl_priority.dl_min = 0; 2708874SSebastien.Roy@Sun.COM rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1; 2710Sstevel@tonic-gate 2728874SSebastien.Roy@Sun.COM dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp; 2738874SSebastien.Roy@Sun.COM dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t); 2740Sstevel@tonic-gate 2758874SSebastien.Roy@Sun.COM selp->dl_qos_type = DL_QOS_CL_SEL1; 2768874SSebastien.Roy@Sun.COM selp->dl_trans_delay = DL_UNKNOWN; 2778874SSebastien.Roy@Sun.COM selp->dl_protection = DL_UNKNOWN; 2788874SSebastien.Roy@Sun.COM selp->dl_residual_error = DL_UNKNOWN; 2792760Sdg199075 2808874SSebastien.Roy@Sun.COM /* 2818874SSebastien.Roy@Sun.COM * Specify the current priority (which can be changed by 2828874SSebastien.Roy@Sun.COM * the DL_UDQOS_REQ primitive). 2838874SSebastien.Roy@Sun.COM */ 2848874SSebastien.Roy@Sun.COM selp->dl_priority = dsp->ds_pri; 2858874SSebastien.Roy@Sun.COM } 2860Sstevel@tonic-gate 2870Sstevel@tonic-gate dlp->dl_addr_length = addr_length + sizeof (uint16_t); 2880Sstevel@tonic-gate if (dsp->ds_dlstate == DL_IDLE) { 2890Sstevel@tonic-gate /* 2900Sstevel@tonic-gate * The stream is bound. Therefore we can formulate a valid 2910Sstevel@tonic-gate * DLSAP address. 2920Sstevel@tonic-gate */ 2930Sstevel@tonic-gate dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp; 2942311Sseb if (addr_length > 0) 2958275SEric Cheng mac_unicast_primary_get(dsp->ds_mh, addr); 2968275SEric Cheng 2970Sstevel@tonic-gate *(uint16_t *)(addr + addr_length) = dsp->ds_sap; 2980Sstevel@tonic-gate } 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate done: 3010Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0)); 3020Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_range_offset != 0, 3030Sstevel@tonic-gate dlp->dl_qos_range_length != 0)); 3040Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0)); 3050Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0, 3060Sstevel@tonic-gate dlp->dl_brdcst_addr_length != 0)); 3070Sstevel@tonic-gate 308269Sericheng qreply(q, mp); 309269Sericheng } 310269Sericheng 311269Sericheng /* 312269Sericheng * DL_ATTACH_REQ 313269Sericheng */ 3148275SEric Cheng static void 3158275SEric Cheng proto_attach_req(dld_str_t *dsp, mblk_t *mp) 316269Sericheng { 3178275SEric Cheng dl_attach_req_t *dlp = (dl_attach_req_t *)mp->b_rptr; 318269Sericheng int err = 0; 319269Sericheng t_uscalar_t dl_err; 320269Sericheng queue_t *q = dsp->ds_wq; 321269Sericheng 322269Sericheng if (MBLKL(mp) < sizeof (dl_attach_req_t) || 323269Sericheng dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) { 324269Sericheng dl_err = DL_BADPRIM; 325269Sericheng goto failed; 326269Sericheng } 327269Sericheng 328269Sericheng if (dsp->ds_dlstate != DL_UNATTACHED) { 329269Sericheng dl_err = DL_OUTSTATE; 330269Sericheng goto failed; 331269Sericheng } 332269Sericheng 333269Sericheng dsp->ds_dlstate = DL_ATTACH_PENDING; 334269Sericheng 335269Sericheng err = dld_str_attach(dsp, dlp->dl_ppa); 336269Sericheng if (err != 0) { 337269Sericheng switch (err) { 338269Sericheng case ENOENT: 339269Sericheng dl_err = DL_BADPPA; 340269Sericheng err = 0; 341269Sericheng break; 342269Sericheng default: 343269Sericheng dl_err = DL_SYSERR; 344269Sericheng break; 345269Sericheng } 346269Sericheng dsp->ds_dlstate = DL_UNATTACHED; 347269Sericheng goto failed; 348269Sericheng } 349269Sericheng ASSERT(dsp->ds_dlstate == DL_UNBOUND); 3508275SEric Cheng dlokack(q, mp, DL_ATTACH_REQ); 3518275SEric Cheng return; 352269Sericheng 353269Sericheng failed: 354269Sericheng dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err); 3550Sstevel@tonic-gate } 3560Sstevel@tonic-gate 3578275SEric Cheng /* 3588275SEric Cheng * DL_DETACH_REQ 3598275SEric Cheng */ 3608275SEric Cheng static void 3618275SEric Cheng proto_detach_req(dld_str_t *dsp, mblk_t *mp) 3620Sstevel@tonic-gate { 363269Sericheng queue_t *q = dsp->ds_wq; 364269Sericheng t_uscalar_t dl_err; 3650Sstevel@tonic-gate 366269Sericheng if (MBLKL(mp) < sizeof (dl_detach_req_t)) { 367269Sericheng dl_err = DL_BADPRIM; 368269Sericheng goto failed; 369269Sericheng } 3700Sstevel@tonic-gate 371269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 372269Sericheng dl_err = DL_OUTSTATE; 373269Sericheng goto failed; 3740Sstevel@tonic-gate } 3750Sstevel@tonic-gate 376269Sericheng if (dsp->ds_style == DL_STYLE1) { 377269Sericheng dl_err = DL_BADPRIM; 378269Sericheng goto failed; 379269Sericheng } 380269Sericheng 3818275SEric Cheng ASSERT(dsp->ds_datathr_cnt == 0); 382269Sericheng dsp->ds_dlstate = DL_DETACH_PENDING; 3838275SEric Cheng 3845895Syz147064 dld_str_detach(dsp); 3855895Syz147064 dlokack(dsp->ds_wq, mp, DL_DETACH_REQ); 3868275SEric Cheng return; 3878275SEric Cheng 388269Sericheng failed: 389269Sericheng dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0); 3900Sstevel@tonic-gate } 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate /* 393269Sericheng * DL_BIND_REQ 3940Sstevel@tonic-gate */ 3958275SEric Cheng static void 3968275SEric Cheng proto_bind_req(dld_str_t *dsp, mblk_t *mp) 3970Sstevel@tonic-gate { 3988275SEric Cheng dl_bind_req_t *dlp = (dl_bind_req_t *)mp->b_rptr; 399269Sericheng int err = 0; 4003037Syz147064 uint8_t dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)]; 4013037Syz147064 uint_t dlsap_addr_length; 402269Sericheng t_uscalar_t dl_err; 403269Sericheng t_scalar_t sap; 404269Sericheng queue_t *q = dsp->ds_wq; 4058275SEric Cheng mac_perim_handle_t mph; 4068275SEric Cheng void *mdip; 4078275SEric Cheng int32_t intr_cpu; 408269Sericheng 409269Sericheng if (MBLKL(mp) < sizeof (dl_bind_req_t)) { 410269Sericheng dl_err = DL_BADPRIM; 411269Sericheng goto failed; 412269Sericheng } 413269Sericheng 414269Sericheng if (dlp->dl_xidtest_flg != 0) { 415269Sericheng dl_err = DL_NOAUTO; 416269Sericheng goto failed; 417269Sericheng } 418269Sericheng 419269Sericheng if (dlp->dl_service_mode != DL_CLDLS) { 420269Sericheng dl_err = DL_UNSUPPORTED; 421269Sericheng goto failed; 422269Sericheng } 423269Sericheng 424269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 425269Sericheng dl_err = DL_OUTSTATE; 426269Sericheng goto failed; 427269Sericheng } 4280Sstevel@tonic-gate 4298275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 4308275SEric Cheng 431269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 4328275SEric Cheng ((err = dls_active_set(dsp)) != 0)) { 433269Sericheng dl_err = DL_SYSERR; 4348275SEric Cheng goto failed2; 435269Sericheng } 436269Sericheng 4378275SEric Cheng dsp->ds_dlstate = DL_BIND_PENDING; 438269Sericheng /* 439269Sericheng * Set the receive callback. 440269Sericheng */ 4418275SEric Cheng dls_rx_set(dsp, (dsp->ds_mode == DLD_RAW) ? 442269Sericheng dld_str_rx_raw : dld_str_rx_unitdata, dsp); 4430Sstevel@tonic-gate 444269Sericheng /* 445269Sericheng * Bind the channel such that it can receive packets. 446269Sericheng */ 4475895Syz147064 sap = dlp->dl_sap; 4488275SEric Cheng err = dls_bind(dsp, sap); 449269Sericheng if (err != 0) { 450269Sericheng switch (err) { 451269Sericheng case EINVAL: 452269Sericheng dl_err = DL_BADADDR; 453269Sericheng err = 0; 454269Sericheng break; 455269Sericheng default: 456269Sericheng dl_err = DL_SYSERR; 457269Sericheng break; 458269Sericheng } 4595895Syz147064 4608275SEric Cheng dsp->ds_dlstate = DL_UNBOUND; 461269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 4628275SEric Cheng dls_active_clear(dsp); 4638275SEric Cheng goto failed2; 4648275SEric Cheng } 465269Sericheng 4668275SEric Cheng intr_cpu = mac_client_intr_cpu(dsp->ds_mch); 4678275SEric Cheng mdip = mac_get_devinfo(dsp->ds_mh); 4688275SEric Cheng mac_perim_exit(mph); 4698275SEric Cheng 4708275SEric Cheng /* 4718275SEric Cheng * We do this after we get out of the perim to avoid deadlocks 4728275SEric Cheng * etc. since part of mac_client_retarget_intr is to walk the 4738275SEric Cheng * device tree in order to find and retarget the interrupts. 4748275SEric Cheng */ 4758275SEric Cheng mac_client_set_intr_cpu(mdip, dsp->ds_mch, intr_cpu); 4760Sstevel@tonic-gate 4770Sstevel@tonic-gate /* 4780Sstevel@tonic-gate * Copy in MAC address. 4790Sstevel@tonic-gate */ 4803037Syz147064 dlsap_addr_length = dsp->ds_mip->mi_addr_length; 4818275SEric Cheng mac_unicast_primary_get(dsp->ds_mh, dlsap_addr); 4820Sstevel@tonic-gate 4830Sstevel@tonic-gate /* 4843037Syz147064 * Copy in the SAP. 4850Sstevel@tonic-gate */ 4865895Syz147064 *(uint16_t *)(dlsap_addr + dlsap_addr_length) = sap; 4873037Syz147064 dlsap_addr_length += sizeof (uint16_t); 4880Sstevel@tonic-gate 4890Sstevel@tonic-gate dsp->ds_dlstate = DL_IDLE; 490269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 491269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 492269Sericheng 4933037Syz147064 dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0); 4948275SEric Cheng return; 4958275SEric Cheng 4968275SEric Cheng failed2: 4978275SEric Cheng mac_perim_exit(mph); 4980Sstevel@tonic-gate failed: 499269Sericheng dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err); 5000Sstevel@tonic-gate } 5010Sstevel@tonic-gate 5020Sstevel@tonic-gate /* 503269Sericheng * DL_UNBIND_REQ 5040Sstevel@tonic-gate */ 5058275SEric Cheng static void 5068275SEric Cheng proto_unbind_req(dld_str_t *dsp, mblk_t *mp) 5070Sstevel@tonic-gate { 5085895Syz147064 queue_t *q = dsp->ds_wq; 5095895Syz147064 t_uscalar_t dl_err; 5108275SEric Cheng mac_perim_handle_t mph; 511269Sericheng 5125895Syz147064 if (MBLKL(mp) < sizeof (dl_unbind_req_t)) { 5135895Syz147064 dl_err = DL_BADPRIM; 5145895Syz147064 goto failed; 5155895Syz147064 } 5165895Syz147064 5175895Syz147064 if (dsp->ds_dlstate != DL_IDLE) { 5185895Syz147064 dl_err = DL_OUTSTATE; 5195895Syz147064 goto failed; 5205895Syz147064 } 521269Sericheng 5228275SEric Cheng mutex_enter(&dsp->ds_lock); 5238275SEric Cheng while (dsp->ds_datathr_cnt != 0) 5248275SEric Cheng cv_wait(&dsp->ds_datathr_cv, &dsp->ds_lock); 525269Sericheng 5268275SEric Cheng dsp->ds_dlstate = DL_UNBIND_PENDING; 5278275SEric Cheng mutex_exit(&dsp->ds_lock); 5288275SEric Cheng 5298275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 530269Sericheng /* 531269Sericheng * Unbind the channel to stop packets being received. 532269Sericheng */ 5338275SEric Cheng if (dls_unbind(dsp) != 0) { 5348275SEric Cheng dl_err = DL_OUTSTATE; 5358275SEric Cheng mac_perim_exit(mph); 5368275SEric Cheng goto failed; 5378275SEric Cheng } 5385895Syz147064 5395895Syz147064 /* 540269Sericheng * Disable polling mode, if it is enabled. 541269Sericheng */ 5428275SEric Cheng (void) dld_capab_poll_disable(dsp, NULL); 5435895Syz147064 5445895Syz147064 /* 5453115Syl150051 * Clear LSO flags. 5463115Syl150051 */ 5473115Syl150051 dsp->ds_lso = B_FALSE; 5483115Syl150051 dsp->ds_lso_max = 0; 5493115Syl150051 5503115Syl150051 /* 5518275SEric Cheng * Clear the receive callback. 5528275SEric Cheng */ 5538275SEric Cheng dls_rx_set(dsp, NULL, NULL); 5548275SEric Cheng dsp->ds_direct = B_FALSE; 5558275SEric Cheng 5568275SEric Cheng /* 557269Sericheng * Set the mode back to the default (unitdata). 558269Sericheng */ 559269Sericheng dsp->ds_mode = DLD_UNITDATA; 5601353Sericheng dsp->ds_dlstate = DL_UNBOUND; 5611353Sericheng 5628275SEric Cheng mac_perim_exit(mph); 5638275SEric Cheng dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ); 5648275SEric Cheng return; 565269Sericheng failed: 566269Sericheng dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0); 5670Sstevel@tonic-gate } 5680Sstevel@tonic-gate 5690Sstevel@tonic-gate /* 570269Sericheng * DL_PROMISCON_REQ 5710Sstevel@tonic-gate */ 5728275SEric Cheng static void 5738275SEric Cheng proto_promiscon_req(dld_str_t *dsp, mblk_t *mp) 5740Sstevel@tonic-gate { 5758275SEric Cheng dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)mp->b_rptr; 576269Sericheng int err = 0; 577269Sericheng t_uscalar_t dl_err; 5788275SEric Cheng uint32_t promisc_saved; 579269Sericheng queue_t *q = dsp->ds_wq; 5808275SEric Cheng mac_perim_handle_t mph; 581269Sericheng 582269Sericheng if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) { 583269Sericheng dl_err = DL_BADPRIM; 584269Sericheng goto failed; 585269Sericheng } 586269Sericheng 587269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 588269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 589269Sericheng dl_err = DL_OUTSTATE; 5900Sstevel@tonic-gate goto failed; 591269Sericheng } 5920Sstevel@tonic-gate 5938275SEric Cheng promisc_saved = dsp->ds_promisc; 594269Sericheng switch (dlp->dl_level) { 595269Sericheng case DL_PROMISC_SAP: 5968275SEric Cheng dsp->ds_promisc |= DLS_PROMISC_SAP; 597269Sericheng break; 5988275SEric Cheng 5995895Syz147064 case DL_PROMISC_MULTI: 6008275SEric Cheng dsp->ds_promisc |= DLS_PROMISC_MULTI; 6015895Syz147064 break; 6028275SEric Cheng 603269Sericheng case DL_PROMISC_PHYS: 6048275SEric Cheng dsp->ds_promisc |= DLS_PROMISC_PHYS; 605269Sericheng break; 6068275SEric Cheng 607269Sericheng default: 608269Sericheng dl_err = DL_NOTSUPPORTED; 609269Sericheng goto failed; 610269Sericheng } 6110Sstevel@tonic-gate 6128275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 6138275SEric Cheng 614269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 6158275SEric Cheng ((err = dls_active_set(dsp)) != 0)) { 6168275SEric Cheng dsp->ds_promisc = promisc_saved; 617269Sericheng dl_err = DL_SYSERR; 6188275SEric Cheng goto failed2; 619269Sericheng } 620269Sericheng 621269Sericheng /* 622269Sericheng * Adjust channel promiscuity. 623269Sericheng */ 6248275SEric Cheng err = dls_promisc(dsp, promisc_saved); 6258275SEric Cheng 626269Sericheng if (err != 0) { 627269Sericheng dl_err = DL_SYSERR; 6288275SEric Cheng dsp->ds_promisc = promisc_saved; 629269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 6308275SEric Cheng dls_active_clear(dsp); 6318275SEric Cheng goto failed2; 632269Sericheng } 633269Sericheng 6348275SEric Cheng mac_perim_exit(mph); 6358275SEric Cheng 636269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 637269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 6388275SEric Cheng dlokack(q, mp, DL_PROMISCON_REQ); 6398275SEric Cheng return; 640269Sericheng 6418275SEric Cheng failed2: 6428275SEric Cheng mac_perim_exit(mph); 6430Sstevel@tonic-gate failed: 644269Sericheng dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err); 6450Sstevel@tonic-gate } 6460Sstevel@tonic-gate 6470Sstevel@tonic-gate /* 648269Sericheng * DL_PROMISCOFF_REQ 6490Sstevel@tonic-gate */ 6508275SEric Cheng static void 6518275SEric Cheng proto_promiscoff_req(dld_str_t *dsp, mblk_t *mp) 6520Sstevel@tonic-gate { 6538275SEric Cheng dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)mp->b_rptr; 654269Sericheng int err = 0; 655269Sericheng t_uscalar_t dl_err; 6568275SEric Cheng uint32_t promisc_saved; 657269Sericheng queue_t *q = dsp->ds_wq; 6588275SEric Cheng mac_perim_handle_t mph; 659269Sericheng 660269Sericheng if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) { 661269Sericheng dl_err = DL_BADPRIM; 6620Sstevel@tonic-gate goto failed; 663269Sericheng } 6640Sstevel@tonic-gate 665269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 666269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 667269Sericheng dl_err = DL_OUTSTATE; 6680Sstevel@tonic-gate goto failed; 669269Sericheng } 6700Sstevel@tonic-gate 6718275SEric Cheng promisc_saved = dsp->ds_promisc; 672269Sericheng switch (dlp->dl_level) { 673269Sericheng case DL_PROMISC_SAP: 6748275SEric Cheng if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) { 6758275SEric Cheng dl_err = DL_NOTENAB; 6768275SEric Cheng goto failed; 6778275SEric Cheng } 6788275SEric Cheng dsp->ds_promisc &= ~DLS_PROMISC_SAP; 6790Sstevel@tonic-gate break; 6808275SEric Cheng 681269Sericheng case DL_PROMISC_MULTI: 6828275SEric Cheng if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) { 6838275SEric Cheng dl_err = DL_NOTENAB; 6848275SEric Cheng goto failed; 6858275SEric Cheng } 6868275SEric Cheng dsp->ds_promisc &= ~DLS_PROMISC_MULTI; 687269Sericheng break; 6888275SEric Cheng 689269Sericheng case DL_PROMISC_PHYS: 6908275SEric Cheng if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) { 6918275SEric Cheng dl_err = DL_NOTENAB; 6928275SEric Cheng goto failed; 6938275SEric Cheng } 6948275SEric Cheng dsp->ds_promisc &= ~DLS_PROMISC_PHYS; 6950Sstevel@tonic-gate break; 6968275SEric Cheng 6970Sstevel@tonic-gate default: 698269Sericheng dl_err = DL_NOTSUPPORTED; 699269Sericheng goto failed; 700269Sericheng } 701269Sericheng 7028275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 7038275SEric Cheng /* 7048275SEric Cheng * Adjust channel promiscuity. 7058275SEric Cheng */ 7068275SEric Cheng err = dls_promisc(dsp, promisc_saved); 7078275SEric Cheng mac_perim_exit(mph); 7085895Syz147064 709269Sericheng if (err != 0) { 7100Sstevel@tonic-gate dl_err = DL_SYSERR; 711269Sericheng goto failed; 712269Sericheng } 713269Sericheng dlokack(q, mp, DL_PROMISCOFF_REQ); 7148275SEric Cheng return; 715269Sericheng failed: 716269Sericheng dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err); 717269Sericheng } 718269Sericheng 719269Sericheng /* 720269Sericheng * DL_ENABMULTI_REQ 721269Sericheng */ 7228275SEric Cheng static void 7238275SEric Cheng proto_enabmulti_req(dld_str_t *dsp, mblk_t *mp) 724269Sericheng { 7258275SEric Cheng dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)mp->b_rptr; 726269Sericheng int err = 0; 727269Sericheng t_uscalar_t dl_err; 728269Sericheng queue_t *q = dsp->ds_wq; 7298275SEric Cheng mac_perim_handle_t mph; 730269Sericheng 731269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 732269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 733269Sericheng dl_err = DL_OUTSTATE; 734269Sericheng goto failed; 735269Sericheng } 736269Sericheng 737269Sericheng if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) || 738269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 739269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 740269Sericheng dl_err = DL_BADPRIM; 741269Sericheng goto failed; 742269Sericheng } 743269Sericheng 7448275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 7458275SEric Cheng 746269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 7478275SEric Cheng ((err = dls_active_set(dsp)) != 0)) { 748269Sericheng dl_err = DL_SYSERR; 7498275SEric Cheng goto failed2; 7500Sstevel@tonic-gate } 7510Sstevel@tonic-gate 7528275SEric Cheng err = dls_multicst_add(dsp, mp->b_rptr + dlp->dl_addr_offset); 7538275SEric Cheng 754269Sericheng if (err != 0) { 755269Sericheng switch (err) { 756269Sericheng case EINVAL: 757269Sericheng dl_err = DL_BADADDR; 758269Sericheng err = 0; 759269Sericheng break; 760269Sericheng case ENOSPC: 761269Sericheng dl_err = DL_TOOMANY; 762269Sericheng err = 0; 763269Sericheng break; 764269Sericheng default: 765269Sericheng dl_err = DL_SYSERR; 766269Sericheng break; 767269Sericheng } 7688275SEric Cheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 7698275SEric Cheng dls_active_clear(dsp); 7705895Syz147064 7718275SEric Cheng goto failed2; 772269Sericheng } 773269Sericheng 7748275SEric Cheng mac_perim_exit(mph); 7758275SEric Cheng 776269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 777269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 7788275SEric Cheng dlokack(q, mp, DL_ENABMULTI_REQ); 7798275SEric Cheng return; 780269Sericheng 7818275SEric Cheng failed2: 7828275SEric Cheng mac_perim_exit(mph); 783269Sericheng failed: 784269Sericheng dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err); 785269Sericheng } 786269Sericheng 787269Sericheng /* 788269Sericheng * DL_DISABMULTI_REQ 789269Sericheng */ 7908275SEric Cheng static void 7918275SEric Cheng proto_disabmulti_req(dld_str_t *dsp, mblk_t *mp) 792269Sericheng { 7938275SEric Cheng dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)mp->b_rptr; 794269Sericheng int err = 0; 795269Sericheng t_uscalar_t dl_err; 796269Sericheng queue_t *q = dsp->ds_wq; 7978275SEric Cheng mac_perim_handle_t mph; 798269Sericheng 799269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 800269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 801269Sericheng dl_err = DL_OUTSTATE; 802269Sericheng goto failed; 803269Sericheng } 804269Sericheng 805269Sericheng if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) || 806269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 807269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 808269Sericheng dl_err = DL_BADPRIM; 809269Sericheng goto failed; 810269Sericheng } 811269Sericheng 8128275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 8138275SEric Cheng err = dls_multicst_remove(dsp, mp->b_rptr + dlp->dl_addr_offset); 8148275SEric Cheng mac_perim_exit(mph); 8158275SEric Cheng 816269Sericheng if (err != 0) { 8178275SEric Cheng switch (err) { 818269Sericheng case EINVAL: 819269Sericheng dl_err = DL_BADADDR; 820269Sericheng err = 0; 821269Sericheng break; 8228275SEric Cheng 823269Sericheng case ENOENT: 824269Sericheng dl_err = DL_NOTENAB; 825269Sericheng err = 0; 826269Sericheng break; 8278275SEric Cheng 828269Sericheng default: 829269Sericheng dl_err = DL_SYSERR; 830269Sericheng break; 831269Sericheng } 832269Sericheng goto failed; 833269Sericheng } 834269Sericheng dlokack(q, mp, DL_DISABMULTI_REQ); 8358275SEric Cheng return; 836269Sericheng failed: 837269Sericheng dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err); 8380Sstevel@tonic-gate } 8390Sstevel@tonic-gate 8400Sstevel@tonic-gate /* 841269Sericheng * DL_PHYS_ADDR_REQ 8420Sstevel@tonic-gate */ 8438275SEric Cheng static void 8448275SEric Cheng proto_physaddr_req(dld_str_t *dsp, mblk_t *mp) 8450Sstevel@tonic-gate { 8468275SEric Cheng dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)mp->b_rptr; 847269Sericheng queue_t *q = dsp->ds_wq; 848269Sericheng t_uscalar_t dl_err; 849269Sericheng char *addr; 850269Sericheng uint_t addr_length; 851269Sericheng 852269Sericheng if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) { 853269Sericheng dl_err = DL_BADPRIM; 854269Sericheng goto failed; 855269Sericheng } 856269Sericheng 857269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 858269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 859269Sericheng dl_err = DL_OUTSTATE; 860269Sericheng goto failed; 861269Sericheng } 8620Sstevel@tonic-gate 863269Sericheng if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR && 864269Sericheng dlp->dl_addr_type != DL_FACT_PHYS_ADDR) { 865269Sericheng dl_err = DL_UNSUPPORTED; 8660Sstevel@tonic-gate goto failed; 867269Sericheng } 8680Sstevel@tonic-gate 869269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 8706353Sdr146992 if (addr_length > 0) { 8718275SEric Cheng addr = kmem_alloc(addr_length, KM_SLEEP); 8728275SEric Cheng if (dlp->dl_addr_type == DL_CURR_PHYS_ADDR) 8738275SEric Cheng mac_unicast_primary_get(dsp->ds_mh, (uint8_t *)addr); 8748275SEric Cheng else 8758275SEric Cheng bcopy(dsp->ds_mip->mi_unicst_addr, addr, addr_length); 8760Sstevel@tonic-gate 8776353Sdr146992 dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length); 8786353Sdr146992 kmem_free(addr, addr_length); 8796353Sdr146992 } else { 8806353Sdr146992 dlphysaddrack(q, mp, NULL, 0); 8816353Sdr146992 } 8828275SEric Cheng return; 8830Sstevel@tonic-gate failed: 884269Sericheng dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0); 885269Sericheng } 8860Sstevel@tonic-gate 887269Sericheng /* 888269Sericheng * DL_SET_PHYS_ADDR_REQ 889269Sericheng */ 8908275SEric Cheng static void 8918275SEric Cheng proto_setphysaddr_req(dld_str_t *dsp, mblk_t *mp) 892269Sericheng { 8938275SEric Cheng dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)mp->b_rptr; 894269Sericheng int err = 0; 895269Sericheng t_uscalar_t dl_err; 896269Sericheng queue_t *q = dsp->ds_wq; 8978275SEric Cheng mac_perim_handle_t mph; 8980Sstevel@tonic-gate 899269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 900269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 901269Sericheng dl_err = DL_OUTSTATE; 902269Sericheng goto failed; 903269Sericheng } 904269Sericheng 905269Sericheng if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) || 906269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 907269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 908269Sericheng dl_err = DL_BADPRIM; 909269Sericheng goto failed; 9100Sstevel@tonic-gate } 9110Sstevel@tonic-gate 9128275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 9138275SEric Cheng 914269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 9158275SEric Cheng ((err = dls_active_set(dsp)) != 0)) { 916269Sericheng dl_err = DL_SYSERR; 9178275SEric Cheng goto failed2; 918269Sericheng } 919269Sericheng 9208275SEric Cheng err = mac_unicast_primary_set(dsp->ds_mh, 9218275SEric Cheng mp->b_rptr + dlp->dl_addr_offset); 922269Sericheng if (err != 0) { 923269Sericheng switch (err) { 924269Sericheng case EINVAL: 925269Sericheng dl_err = DL_BADADDR; 926269Sericheng err = 0; 927269Sericheng break; 928269Sericheng 929269Sericheng default: 930269Sericheng dl_err = DL_SYSERR; 931269Sericheng break; 932269Sericheng } 9338275SEric Cheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 9348275SEric Cheng dls_active_clear(dsp); 9355895Syz147064 9368275SEric Cheng goto failed2; 937269Sericheng 938269Sericheng } 9395895Syz147064 9408275SEric Cheng mac_perim_exit(mph); 9418275SEric Cheng 942269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 943269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 9448275SEric Cheng dlokack(q, mp, DL_SET_PHYS_ADDR_REQ); 9458275SEric Cheng return; 946269Sericheng 9478275SEric Cheng failed2: 9488275SEric Cheng mac_perim_exit(mph); 949269Sericheng failed: 950269Sericheng dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err); 951269Sericheng } 952269Sericheng 953269Sericheng /* 954269Sericheng * DL_UDQOS_REQ 955269Sericheng */ 9568275SEric Cheng static void 9578275SEric Cheng proto_udqos_req(dld_str_t *dsp, mblk_t *mp) 958269Sericheng { 9598275SEric Cheng dl_udqos_req_t *dlp = (dl_udqos_req_t *)mp->b_rptr; 960269Sericheng dl_qos_cl_sel1_t *selp; 961269Sericheng int off, len; 962269Sericheng t_uscalar_t dl_err; 963269Sericheng queue_t *q = dsp->ds_wq; 964269Sericheng 965269Sericheng off = dlp->dl_qos_offset; 966269Sericheng len = dlp->dl_qos_length; 967269Sericheng 968269Sericheng if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) { 969269Sericheng dl_err = DL_BADPRIM; 970269Sericheng goto failed; 971269Sericheng } 972269Sericheng 973269Sericheng selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off); 974269Sericheng if (selp->dl_qos_type != DL_QOS_CL_SEL1) { 975269Sericheng dl_err = DL_BADQOSTYPE; 976269Sericheng goto failed; 977269Sericheng } 978269Sericheng 9792760Sdg199075 if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 || 980269Sericheng selp->dl_priority < 0) { 981269Sericheng dl_err = DL_BADQOSPARAM; 982269Sericheng goto failed; 983269Sericheng } 984269Sericheng 9855895Syz147064 dsp->ds_pri = selp->dl_priority; 986269Sericheng dlokack(q, mp, DL_UDQOS_REQ); 9878275SEric Cheng return; 988269Sericheng failed: 989269Sericheng dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0); 9900Sstevel@tonic-gate } 9910Sstevel@tonic-gate 9921184Skrgopi static boolean_t 9931184Skrgopi check_ip_above(queue_t *q) 9941184Skrgopi { 9951184Skrgopi queue_t *next_q; 9961184Skrgopi boolean_t ret = B_TRUE; 9971184Skrgopi 9981184Skrgopi claimstr(q); 9991184Skrgopi next_q = q->q_next; 10001184Skrgopi if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0) 10011184Skrgopi ret = B_FALSE; 10021184Skrgopi releasestr(q); 10031184Skrgopi return (ret); 10041184Skrgopi } 10051184Skrgopi 10060Sstevel@tonic-gate /* 1007269Sericheng * DL_CAPABILITY_REQ 10080Sstevel@tonic-gate */ 10098275SEric Cheng static void 10108275SEric Cheng proto_capability_req(dld_str_t *dsp, mblk_t *mp) 10110Sstevel@tonic-gate { 10128275SEric Cheng dl_capability_req_t *dlp = (dl_capability_req_t *)mp->b_rptr; 1013269Sericheng dl_capability_sub_t *sp; 1014269Sericheng size_t size, len; 1015269Sericheng offset_t off, end; 1016269Sericheng t_uscalar_t dl_err; 1017269Sericheng queue_t *q = dsp->ds_wq; 1018269Sericheng 1019269Sericheng if (MBLKL(mp) < sizeof (dl_capability_req_t)) { 1020269Sericheng dl_err = DL_BADPRIM; 1021269Sericheng goto failed; 1022269Sericheng } 1023269Sericheng 1024269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1025269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1026269Sericheng dl_err = DL_OUTSTATE; 1027269Sericheng goto failed; 1028269Sericheng } 1029269Sericheng 1030269Sericheng /* 1031269Sericheng * This request is overloaded. If there are no requested capabilities 1032269Sericheng * then we just want to acknowledge with all the capabilities we 1033269Sericheng * support. Otherwise we enable the set of capabilities requested. 1034269Sericheng */ 1035269Sericheng if (dlp->dl_sub_length == 0) { 10368275SEric Cheng proto_capability_advertise(dsp, mp); 10378275SEric Cheng return; 1038269Sericheng } 1039269Sericheng 1040269Sericheng if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) { 1041269Sericheng dl_err = DL_BADPRIM; 1042269Sericheng goto failed; 1043269Sericheng } 1044269Sericheng 1045269Sericheng dlp->dl_primitive = DL_CAPABILITY_ACK; 1046269Sericheng 1047269Sericheng off = dlp->dl_sub_offset; 1048269Sericheng len = dlp->dl_sub_length; 10490Sstevel@tonic-gate 10500Sstevel@tonic-gate /* 1051269Sericheng * Walk the list of capabilities to be enabled. 10520Sstevel@tonic-gate */ 1053269Sericheng for (end = off + len; off < end; ) { 1054269Sericheng sp = (dl_capability_sub_t *)(mp->b_rptr + off); 1055269Sericheng size = sizeof (dl_capability_sub_t) + sp->dl_length; 1056269Sericheng 1057269Sericheng if (off + size > end || 1058269Sericheng !IS_P2ALIGNED(off, sizeof (uint32_t))) { 1059269Sericheng dl_err = DL_BADPRIM; 1060269Sericheng goto failed; 1061269Sericheng } 1062269Sericheng 1063269Sericheng switch (sp->dl_cap) { 1064269Sericheng /* 1065269Sericheng * TCP/IP checksum offload to hardware. 1066269Sericheng */ 1067269Sericheng case DL_CAPAB_HCKSUM: { 1068269Sericheng dl_capab_hcksum_t *hcksump; 1069269Sericheng dl_capab_hcksum_t hcksum; 1070269Sericheng 1071269Sericheng hcksump = (dl_capab_hcksum_t *)&sp[1]; 1072269Sericheng /* 1073269Sericheng * Copy for alignment. 1074269Sericheng */ 1075269Sericheng bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t)); 1076269Sericheng dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 1077269Sericheng bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t)); 1078269Sericheng break; 1079269Sericheng } 1080269Sericheng 10818275SEric Cheng case DL_CAPAB_DLD: { 10828275SEric Cheng dl_capab_dld_t *dldp; 10838275SEric Cheng dl_capab_dld_t dld; 10843115Syl150051 10858275SEric Cheng dldp = (dl_capab_dld_t *)&sp[1]; 1086269Sericheng /* 1087269Sericheng * Copy for alignment. 1088269Sericheng */ 10898275SEric Cheng bcopy(dldp, &dld, sizeof (dl_capab_dld_t)); 10908275SEric Cheng dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq); 10918275SEric Cheng bcopy(&dld, dldp, sizeof (dl_capab_dld_t)); 1092269Sericheng break; 1093269Sericheng } 1094269Sericheng default: 1095269Sericheng break; 1096269Sericheng } 1097269Sericheng off += size; 1098269Sericheng } 1099269Sericheng qreply(q, mp); 11008275SEric Cheng return; 1101269Sericheng failed: 1102269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0); 11030Sstevel@tonic-gate } 11040Sstevel@tonic-gate 11050Sstevel@tonic-gate /* 1106269Sericheng * DL_NOTIFY_REQ 11070Sstevel@tonic-gate */ 11088275SEric Cheng static void 11098275SEric Cheng proto_notify_req(dld_str_t *dsp, mblk_t *mp) 11100Sstevel@tonic-gate { 11118275SEric Cheng dl_notify_req_t *dlp = (dl_notify_req_t *)mp->b_rptr; 1112269Sericheng t_uscalar_t dl_err; 1113269Sericheng queue_t *q = dsp->ds_wq; 1114269Sericheng uint_t note = 1115269Sericheng DL_NOTE_PROMISC_ON_PHYS | 1116269Sericheng DL_NOTE_PROMISC_OFF_PHYS | 1117269Sericheng DL_NOTE_PHYS_ADDR | 1118269Sericheng DL_NOTE_LINK_UP | 1119269Sericheng DL_NOTE_LINK_DOWN | 11202311Sseb DL_NOTE_CAPAB_RENEG | 1121*8910SGirish.Moodalbail@Sun.COM DL_NOTE_FASTPATH_FLUSH | 11222311Sseb DL_NOTE_SPEED; 11230Sstevel@tonic-gate 1124269Sericheng if (MBLKL(mp) < sizeof (dl_notify_req_t)) { 1125269Sericheng dl_err = DL_BADPRIM; 1126269Sericheng goto failed; 1127269Sericheng } 11280Sstevel@tonic-gate 1129269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1130269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1131269Sericheng dl_err = DL_OUTSTATE; 1132269Sericheng goto failed; 11330Sstevel@tonic-gate } 11340Sstevel@tonic-gate 11355895Syz147064 note &= ~(mac_no_notification(dsp->ds_mh)); 11365895Syz147064 1137269Sericheng /* 1138269Sericheng * Cache the notifications that are being enabled. 1139269Sericheng */ 1140269Sericheng dsp->ds_notifications = dlp->dl_notifications & note; 1141269Sericheng /* 1142269Sericheng * The ACK carries all notifications regardless of which set is 1143269Sericheng * being enabled. 1144269Sericheng */ 1145269Sericheng dlnotifyack(q, mp, note); 1146269Sericheng 1147269Sericheng /* 11488275SEric Cheng * Generate DL_NOTIFY_IND messages for each enabled notification. 1149269Sericheng */ 1150269Sericheng if (dsp->ds_notifications != 0) { 1151269Sericheng dld_str_notify_ind(dsp); 1152269Sericheng } 11538275SEric Cheng return; 1154269Sericheng failed: 1155269Sericheng dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0); 11560Sstevel@tonic-gate } 11570Sstevel@tonic-gate 11580Sstevel@tonic-gate /* 11598275SEric Cheng * DL_UINTDATA_REQ 11600Sstevel@tonic-gate */ 11615895Syz147064 void 11628275SEric Cheng proto_unitdata_req(dld_str_t *dsp, mblk_t *mp) 11630Sstevel@tonic-gate { 1164269Sericheng queue_t *q = dsp->ds_wq; 11655895Syz147064 dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_rptr; 1166269Sericheng off_t off; 1167269Sericheng size_t len, size; 1168269Sericheng const uint8_t *addr; 1169269Sericheng uint16_t sap; 1170269Sericheng uint_t addr_length; 11712311Sseb mblk_t *bp, *payload; 1172269Sericheng uint32_t start, stuff, end, value, flags; 1173269Sericheng t_uscalar_t dl_err; 11745903Ssowmini uint_t max_sdu; 1175269Sericheng 1176269Sericheng if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) { 11778275SEric Cheng dlerrorack(q, mp, DL_UNITDATA_REQ, DL_BADPRIM, 0); 11788275SEric Cheng return; 1179269Sericheng } 1180269Sericheng 11818275SEric Cheng mutex_enter(&dsp->ds_lock); 11828275SEric Cheng if (dsp->ds_dlstate != DL_IDLE) { 11838275SEric Cheng mutex_exit(&dsp->ds_lock); 11848275SEric Cheng dlerrorack(q, mp, DL_UNITDATA_REQ, DL_OUTSTATE, 0); 11858275SEric Cheng return; 11868275SEric Cheng } 11878275SEric Cheng DLD_DATATHR_INC(dsp); 11888275SEric Cheng mutex_exit(&dsp->ds_lock); 11898275SEric Cheng 1190269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 1191269Sericheng 1192269Sericheng off = dlp->dl_dest_addr_offset; 1193269Sericheng len = dlp->dl_dest_addr_length; 1194269Sericheng 1195269Sericheng if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) { 1196269Sericheng dl_err = DL_BADPRIM; 1197269Sericheng goto failed; 1198269Sericheng } 1199269Sericheng 1200269Sericheng if (len != addr_length + sizeof (uint16_t)) { 1201269Sericheng dl_err = DL_BADADDR; 1202269Sericheng goto failed; 1203269Sericheng } 1204269Sericheng 1205269Sericheng addr = mp->b_rptr + off; 1206269Sericheng sap = *(uint16_t *)(mp->b_rptr + off + addr_length); 1207269Sericheng 1208269Sericheng /* 1209269Sericheng * Check the length of the packet and the block types. 1210269Sericheng */ 1211269Sericheng size = 0; 12122311Sseb payload = mp->b_cont; 12132311Sseb for (bp = payload; bp != NULL; bp = bp->b_cont) { 1214269Sericheng if (DB_TYPE(bp) != M_DATA) 1215269Sericheng goto baddata; 1216269Sericheng 1217269Sericheng size += MBLKL(bp); 1218269Sericheng } 1219269Sericheng 12205903Ssowmini mac_sdu_get(dsp->ds_mh, NULL, &max_sdu); 12215903Ssowmini if (size > max_sdu) 1222269Sericheng goto baddata; 1223269Sericheng 1224269Sericheng /* 1225269Sericheng * Build a packet header. 1226269Sericheng */ 12278275SEric Cheng if ((bp = dls_header(dsp, addr, sap, dlp->dl_priority.dl_max, 12282760Sdg199075 &payload)) == NULL) { 1229269Sericheng dl_err = DL_BADADDR; 1230269Sericheng goto failed; 1231269Sericheng } 1232269Sericheng 1233269Sericheng /* 1234269Sericheng * We no longer need the M_PROTO header, so free it. 1235269Sericheng */ 1236269Sericheng freeb(mp); 1237269Sericheng 1238269Sericheng /* 1239269Sericheng * Transfer the checksum offload information if it is present. 1240269Sericheng */ 12412311Sseb hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value, 1242269Sericheng &flags); 12432311Sseb (void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0); 1244269Sericheng 1245269Sericheng /* 1246269Sericheng * Link the payload onto the new header. 1247269Sericheng */ 1248269Sericheng ASSERT(bp->b_cont == NULL); 12492311Sseb bp->b_cont = payload; 12508275SEric Cheng 12518275SEric Cheng /* 12528275SEric Cheng * No lock can be held across modules and putnext()'s, 12538275SEric Cheng * which can happen here with the call from DLD_TX(). 12548275SEric Cheng */ 12558275SEric Cheng if (DLD_TX(dsp, bp, 0, 0) != NULL) { 12568275SEric Cheng /* flow-controlled */ 12578275SEric Cheng DLD_SETQFULL(dsp); 12588275SEric Cheng } 12598275SEric Cheng DLD_DATATHR_DCR(dsp); 12605895Syz147064 return; 12618275SEric Cheng 1262269Sericheng failed: 1263269Sericheng dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0); 12648275SEric Cheng DLD_DATATHR_DCR(dsp); 12655895Syz147064 return; 1266269Sericheng 1267269Sericheng baddata: 1268269Sericheng dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0); 12698275SEric Cheng DLD_DATATHR_DCR(dsp); 1270269Sericheng } 1271269Sericheng 1272269Sericheng /* 1273269Sericheng * DL_PASSIVE_REQ 1274269Sericheng */ 12758275SEric Cheng static void 12768275SEric Cheng proto_passive_req(dld_str_t *dsp, mblk_t *mp) 1277269Sericheng { 1278269Sericheng t_uscalar_t dl_err; 1279269Sericheng 12805895Syz147064 /* 1281269Sericheng * If we've already become active by issuing an active primitive, 1282269Sericheng * then it's too late to try to become passive. 1283269Sericheng */ 1284269Sericheng if (dsp->ds_passivestate == DLD_ACTIVE) { 1285269Sericheng dl_err = DL_OUTSTATE; 1286269Sericheng goto failed; 1287269Sericheng } 1288269Sericheng 1289269Sericheng if (MBLKL(mp) < sizeof (dl_passive_req_t)) { 1290269Sericheng dl_err = DL_BADPRIM; 1291269Sericheng goto failed; 1292269Sericheng } 1293269Sericheng 1294269Sericheng dsp->ds_passivestate = DLD_PASSIVE; 1295269Sericheng dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ); 12968275SEric Cheng return; 1297269Sericheng failed: 1298269Sericheng dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0); 1299269Sericheng } 1300269Sericheng 13018275SEric Cheng 1302269Sericheng /* 1303269Sericheng * Catch-all handler. 1304269Sericheng */ 13058275SEric Cheng static void 13068275SEric Cheng proto_req(dld_str_t *dsp, mblk_t *mp) 13078275SEric Cheng { 13088275SEric Cheng union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr; 13098275SEric Cheng 13108275SEric Cheng dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0); 13118275SEric Cheng } 13128275SEric Cheng 13138275SEric Cheng static int 13148275SEric Cheng dld_capab_perim(dld_str_t *dsp, void *data, uint_t flags) 1315269Sericheng { 13168275SEric Cheng switch (flags) { 13178275SEric Cheng case DLD_ENABLE: 13188275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, (mac_perim_handle_t *)data); 13198275SEric Cheng return (0); 13208275SEric Cheng 13218275SEric Cheng case DLD_DISABLE: 13228275SEric Cheng mac_perim_exit((mac_perim_handle_t)data); 13238275SEric Cheng return (0); 13248275SEric Cheng 13258275SEric Cheng case DLD_QUERY: 13268275SEric Cheng return (mac_perim_held(dsp->ds_mh)); 13278275SEric Cheng } 13288275SEric Cheng return (0); 13290Sstevel@tonic-gate } 13300Sstevel@tonic-gate 13318275SEric Cheng static int 13328275SEric Cheng dld_capab_direct(dld_str_t *dsp, void *data, uint_t flags) 13330Sstevel@tonic-gate { 13348275SEric Cheng dld_capab_direct_t *direct = data; 13358275SEric Cheng 13368275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); 13370Sstevel@tonic-gate 13388275SEric Cheng switch (flags) { 13398275SEric Cheng case DLD_ENABLE: 13408275SEric Cheng dls_rx_set(dsp, (dls_rx_t)direct->di_rx_cf, 13418275SEric Cheng direct->di_rx_ch); 13428833SVenu.Iyer@Sun.COM 13438275SEric Cheng direct->di_tx_df = (uintptr_t)str_mdata_fastpath_put; 13448275SEric Cheng direct->di_tx_dh = dsp; 13458275SEric Cheng direct->di_tx_cb_df = (uintptr_t)mac_client_tx_notify; 13468275SEric Cheng direct->di_tx_cb_dh = dsp->ds_mch; 13478833SVenu.Iyer@Sun.COM direct->di_tx_fctl_df = (uintptr_t)mac_tx_is_flow_blocked; 13488833SVenu.Iyer@Sun.COM direct->di_tx_fctl_dh = dsp->ds_mch; 13498833SVenu.Iyer@Sun.COM 13508275SEric Cheng dsp->ds_direct = B_TRUE; 13518275SEric Cheng 13528275SEric Cheng return (0); 1353269Sericheng 13548275SEric Cheng case DLD_DISABLE: 13558275SEric Cheng dls_rx_set(dsp, (dsp->ds_mode == DLD_FASTPATH) ? 13568275SEric Cheng dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp); 13578275SEric Cheng dsp->ds_direct = B_FALSE; 13588275SEric Cheng 13598275SEric Cheng return (0); 13608275SEric Cheng } 13618275SEric Cheng return (ENOTSUP); 13628275SEric Cheng } 13630Sstevel@tonic-gate 13648275SEric Cheng /* 13658275SEric Cheng * dld_capab_poll_enable() 13668275SEric Cheng * 13678275SEric Cheng * This function is misnamed. All polling and fanouts are run out of the 13688275SEric Cheng * lower mac (in case of VNIC and the only mac in case of NICs). The 13698275SEric Cheng * availability of Rx ring and promiscous mode is all taken care between 13708275SEric Cheng * the soft ring set (mac_srs), the Rx ring, and S/W classifier. Any 13718275SEric Cheng * fanout necessary is done by the soft rings that are part of the 13728275SEric Cheng * mac_srs (by default mac_srs sends the packets up via a TCP and 13738275SEric Cheng * non TCP soft ring). 13748275SEric Cheng * 13758275SEric Cheng * The mac_srs (or its associated soft rings) always store the ill_rx_ring 13768275SEric Cheng * (the cookie returned when they registered with IP during plumb) as their 13778275SEric Cheng * 2nd argument which is passed up as mac_resource_handle_t. The upcall 13788275SEric Cheng * function and 1st argument is what the caller registered when they 13798275SEric Cheng * called mac_rx_classify_flow_add() to register the flow. For VNIC, 13808275SEric Cheng * the function is vnic_rx and argument is vnic_t. For regular NIC 13818275SEric Cheng * case, it mac_rx_default and mac_handle_t. As explained above, the 13828275SEric Cheng * mac_srs (or its soft ring) will add the ill_rx_ring (mac_resource_handle_t) 13838275SEric Cheng * from its stored 2nd argument. 13848275SEric Cheng */ 13858275SEric Cheng static int 13868275SEric Cheng dld_capab_poll_enable(dld_str_t *dsp, dld_capab_poll_t *poll) 13878275SEric Cheng { 13888275SEric Cheng if (dsp->ds_polling) 13898275SEric Cheng return (EINVAL); 13908275SEric Cheng 13918275SEric Cheng if ((dld_opt & DLD_OPT_NO_POLL) != 0 || dsp->ds_mode == DLD_RAW) 13928275SEric Cheng return (ENOTSUP); 13930Sstevel@tonic-gate 13940Sstevel@tonic-gate /* 13958275SEric Cheng * Enable client polling if and only if DLS bypass is possible. 13968275SEric Cheng * Special cases like VLANs need DLS processing in the Rx data path. 13978275SEric Cheng * In such a case we can neither allow the client (IP) to directly 13988275SEric Cheng * poll the softring (since DLS processing hasn't been done) nor can 13998275SEric Cheng * we allow DLS bypass. 14000Sstevel@tonic-gate */ 14018275SEric Cheng if (!mac_rx_bypass_set(dsp->ds_mch, dsp->ds_rx, dsp->ds_rx_arg)) 14028275SEric Cheng return (ENOTSUP); 14030Sstevel@tonic-gate 14040Sstevel@tonic-gate /* 14058275SEric Cheng * Register soft ring resources. This will come in handy later if 14068275SEric Cheng * the user decides to modify CPU bindings to use more CPUs for the 14078275SEric Cheng * device in which case we will switch to fanout using soft rings. 14080Sstevel@tonic-gate */ 14098275SEric Cheng mac_resource_set_common(dsp->ds_mch, 14108275SEric Cheng (mac_resource_add_t)poll->poll_ring_add_cf, 14118275SEric Cheng (mac_resource_remove_t)poll->poll_ring_remove_cf, 14128275SEric Cheng (mac_resource_quiesce_t)poll->poll_ring_quiesce_cf, 14138275SEric Cheng (mac_resource_restart_t)poll->poll_ring_restart_cf, 14148275SEric Cheng (mac_resource_bind_t)poll->poll_ring_bind_cf, 14158275SEric Cheng poll->poll_ring_ch); 14168275SEric Cheng 14178275SEric Cheng mac_client_poll_enable(dsp->ds_mch); 14180Sstevel@tonic-gate 14190Sstevel@tonic-gate dsp->ds_polling = B_TRUE; 14208275SEric Cheng return (0); 14218275SEric Cheng } 14228275SEric Cheng 14238275SEric Cheng /* ARGSUSED */ 14248275SEric Cheng static int 14258275SEric Cheng dld_capab_poll_disable(dld_str_t *dsp, dld_capab_poll_t *poll) 14268275SEric Cheng { 14278275SEric Cheng if (!dsp->ds_polling) 14288275SEric Cheng return (EINVAL); 14298275SEric Cheng 14308275SEric Cheng mac_client_poll_disable(dsp->ds_mch); 14318275SEric Cheng mac_resource_set(dsp->ds_mch, NULL, NULL); 14328275SEric Cheng 14338275SEric Cheng dsp->ds_polling = B_FALSE; 14348275SEric Cheng return (0); 14350Sstevel@tonic-gate } 14360Sstevel@tonic-gate 14378275SEric Cheng static int 14388275SEric Cheng dld_capab_poll(dld_str_t *dsp, void *data, uint_t flags) 14398275SEric Cheng { 14408275SEric Cheng dld_capab_poll_t *poll = data; 14418275SEric Cheng 14428275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); 14438275SEric Cheng 14448275SEric Cheng switch (flags) { 14458275SEric Cheng case DLD_ENABLE: 14468275SEric Cheng return (dld_capab_poll_enable(dsp, poll)); 14478275SEric Cheng case DLD_DISABLE: 14488275SEric Cheng return (dld_capab_poll_disable(dsp, poll)); 14498275SEric Cheng } 14508275SEric Cheng return (ENOTSUP); 14518275SEric Cheng } 14528275SEric Cheng 14538275SEric Cheng static int 14548275SEric Cheng dld_capab_lso(dld_str_t *dsp, void *data, uint_t flags) 14551184Skrgopi { 14568275SEric Cheng dld_capab_lso_t *lso = data; 14578275SEric Cheng 14588275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); 14598275SEric Cheng 14608275SEric Cheng switch (flags) { 14618275SEric Cheng case DLD_ENABLE: { 14628275SEric Cheng mac_capab_lso_t mac_lso; 14631184Skrgopi 14648275SEric Cheng /* 14658275SEric Cheng * Check if LSO is supported on this MAC & enable LSO 14668275SEric Cheng * accordingly. 14678275SEric Cheng */ 14688275SEric Cheng if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) { 14698275SEric Cheng lso->lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max; 14708275SEric Cheng lso->lso_flags = 0; 14718275SEric Cheng /* translate the flag for mac clients */ 14728275SEric Cheng if ((mac_lso.lso_flags & LSO_TX_BASIC_TCP_IPV4) != 0) 14738275SEric Cheng lso->lso_flags |= DLD_LSO_TX_BASIC_TCP_IPV4; 14748275SEric Cheng dsp->ds_lso = B_TRUE; 14758275SEric Cheng dsp->ds_lso_max = lso->lso_max; 14768275SEric Cheng } else { 14778275SEric Cheng dsp->ds_lso = B_FALSE; 14788275SEric Cheng dsp->ds_lso_max = 0; 14798275SEric Cheng return (ENOTSUP); 14808275SEric Cheng } 14818275SEric Cheng return (0); 14828275SEric Cheng } 14838275SEric Cheng case DLD_DISABLE: { 14848275SEric Cheng dsp->ds_lso = B_FALSE; 14858275SEric Cheng dsp->ds_lso_max = 0; 14868275SEric Cheng return (0); 14878275SEric Cheng } 14888275SEric Cheng } 14898275SEric Cheng return (ENOTSUP); 14908275SEric Cheng } 14918275SEric Cheng 14928275SEric Cheng static int 14938275SEric Cheng dld_capab(dld_str_t *dsp, uint_t type, void *data, uint_t flags) 14948275SEric Cheng { 14958275SEric Cheng int err; 14961184Skrgopi 14971184Skrgopi /* 14988275SEric Cheng * Don't enable direct callback capabilities unless the caller is 14998275SEric Cheng * the IP client. When a module is inserted in a stream (_I_INSERT) 15008275SEric Cheng * the stack initiates capability disable, but due to races, the 15018275SEric Cheng * module insertion may complete before the capability disable 15028275SEric Cheng * completes. So we limit the check to DLD_ENABLE case. 15031184Skrgopi */ 15048275SEric Cheng if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) && 15058275SEric Cheng (dsp->ds_sap != ETHERTYPE_IP || !check_ip_above(dsp->ds_rq))) { 15068275SEric Cheng return (ENOTSUP); 15078275SEric Cheng } 15081184Skrgopi 15098275SEric Cheng switch (type) { 15108275SEric Cheng case DLD_CAPAB_DIRECT: 15118275SEric Cheng err = dld_capab_direct(dsp, data, flags); 15128275SEric Cheng break; 15131184Skrgopi 15148275SEric Cheng case DLD_CAPAB_POLL: 15158275SEric Cheng err = dld_capab_poll(dsp, data, flags); 15168275SEric Cheng break; 15171184Skrgopi 15188275SEric Cheng case DLD_CAPAB_PERIM: 15198275SEric Cheng err = dld_capab_perim(dsp, data, flags); 15208275SEric Cheng break; 15211184Skrgopi 15228275SEric Cheng case DLD_CAPAB_LSO: 15238275SEric Cheng err = dld_capab_lso(dsp, data, flags); 15248275SEric Cheng break; 15258275SEric Cheng 15268275SEric Cheng default: 15278275SEric Cheng err = ENOTSUP; 15288275SEric Cheng break; 15291184Skrgopi } 15308275SEric Cheng 15318275SEric Cheng return (err); 15321184Skrgopi } 15331184Skrgopi 15340Sstevel@tonic-gate /* 15350Sstevel@tonic-gate * DL_CAPABILITY_ACK/DL_ERROR_ACK 15360Sstevel@tonic-gate */ 15378275SEric Cheng static void 1538269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp) 15390Sstevel@tonic-gate { 15400Sstevel@tonic-gate dl_capability_ack_t *dlap; 15410Sstevel@tonic-gate dl_capability_sub_t *dlsp; 15420Sstevel@tonic-gate size_t subsize; 15438275SEric Cheng dl_capab_dld_t dld; 15440Sstevel@tonic-gate dl_capab_hcksum_t hcksum; 15450Sstevel@tonic-gate dl_capab_zerocopy_t zcopy; 15460Sstevel@tonic-gate uint8_t *ptr; 1547269Sericheng queue_t *q = dsp->ds_wq; 1548269Sericheng mblk_t *mp1; 15498275SEric Cheng boolean_t is_vlan; 15505895Syz147064 boolean_t hcksum_capable = B_FALSE; 15515895Syz147064 boolean_t zcopy_capable = B_FALSE; 15528275SEric Cheng boolean_t dld_capable = B_FALSE; 15530Sstevel@tonic-gate 15540Sstevel@tonic-gate /* 15550Sstevel@tonic-gate * Initially assume no capabilities. 15560Sstevel@tonic-gate */ 15570Sstevel@tonic-gate subsize = 0; 15588275SEric Cheng is_vlan = (mac_client_vid(dsp->ds_mch) != VLAN_ID_NONE); 15590Sstevel@tonic-gate 15600Sstevel@tonic-gate /* 15615895Syz147064 * Check if checksum offload is supported on this MAC. Don't 15625895Syz147064 * advertise DL_CAPAB_HCKSUM if the underlying MAC is VLAN incapable, 15635895Syz147064 * since it might not be able to do the hardware checksum offload 15645895Syz147064 * with the correct offset. 15650Sstevel@tonic-gate */ 15665895Syz147064 bzero(&hcksum, sizeof (dl_capab_hcksum_t)); 15675895Syz147064 if ((!is_vlan || (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_NATIVEVLAN, 15685895Syz147064 NULL))) && mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM, 15692311Sseb &hcksum.hcksum_txflags)) { 15705895Syz147064 if (hcksum.hcksum_txflags != 0) { 15715895Syz147064 hcksum_capable = B_TRUE; 15725895Syz147064 subsize += sizeof (dl_capability_sub_t) + 15735895Syz147064 sizeof (dl_capab_hcksum_t); 15745895Syz147064 } 15750Sstevel@tonic-gate } 15760Sstevel@tonic-gate 15770Sstevel@tonic-gate /* 15785895Syz147064 * Check if zerocopy is supported on this interface. 15795895Syz147064 * If advertising DL_CAPAB_ZEROCOPY has not been explicitly disabled 15805895Syz147064 * then reserve space for that capability. 15810Sstevel@tonic-gate */ 15825895Syz147064 if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_ZCOPY, NULL) && 15835895Syz147064 !(dld_opt & DLD_OPT_NO_ZEROCOPY)) { 15845895Syz147064 zcopy_capable = B_TRUE; 15850Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 15860Sstevel@tonic-gate sizeof (dl_capab_zerocopy_t); 15870Sstevel@tonic-gate } 15880Sstevel@tonic-gate 15890Sstevel@tonic-gate /* 15908275SEric Cheng * Direct capability negotiation interface between IP and DLD 15918275SEric Cheng */ 15928275SEric Cheng if (dsp->ds_sap == ETHERTYPE_IP && check_ip_above(dsp->ds_rq)) { 15938275SEric Cheng dld_capable = B_TRUE; 15948275SEric Cheng subsize += sizeof (dl_capability_sub_t) + 15958275SEric Cheng sizeof (dl_capab_dld_t); 15968275SEric Cheng } 15978275SEric Cheng 15988275SEric Cheng /* 1599269Sericheng * If there are no capabilities to advertise or if we 1600269Sericheng * can't allocate a response, send a DL_ERROR_ACK. 16010Sstevel@tonic-gate */ 16021184Skrgopi if ((mp1 = reallocb(mp, 1603269Sericheng sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) { 1604269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0); 16058275SEric Cheng return; 16060Sstevel@tonic-gate } 16070Sstevel@tonic-gate 1608269Sericheng mp = mp1; 1609269Sericheng DB_TYPE(mp) = M_PROTO; 1610269Sericheng mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize; 1611269Sericheng bzero(mp->b_rptr, MBLKL(mp)); 16120Sstevel@tonic-gate dlap = (dl_capability_ack_t *)mp->b_rptr; 16130Sstevel@tonic-gate dlap->dl_primitive = DL_CAPABILITY_ACK; 16140Sstevel@tonic-gate dlap->dl_sub_offset = sizeof (dl_capability_ack_t); 16150Sstevel@tonic-gate dlap->dl_sub_length = subsize; 16160Sstevel@tonic-gate ptr = (uint8_t *)&dlap[1]; 16170Sstevel@tonic-gate 16180Sstevel@tonic-gate /* 16190Sstevel@tonic-gate * TCP/IP checksum offload. 16200Sstevel@tonic-gate */ 16215895Syz147064 if (hcksum_capable) { 16220Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 16230Sstevel@tonic-gate 16240Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_HCKSUM; 16250Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_hcksum_t); 16260Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 16270Sstevel@tonic-gate 16280Sstevel@tonic-gate hcksum.hcksum_version = HCKSUM_VERSION_1; 16290Sstevel@tonic-gate dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 16300Sstevel@tonic-gate bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t)); 16310Sstevel@tonic-gate ptr += sizeof (dl_capab_hcksum_t); 16320Sstevel@tonic-gate } 16330Sstevel@tonic-gate 16340Sstevel@tonic-gate /* 16350Sstevel@tonic-gate * Zero copy 16360Sstevel@tonic-gate */ 16375895Syz147064 if (zcopy_capable) { 16380Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 16390Sstevel@tonic-gate 16400Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_ZEROCOPY; 16410Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_zerocopy_t); 16420Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 16430Sstevel@tonic-gate 16440Sstevel@tonic-gate bzero(&zcopy, sizeof (dl_capab_zerocopy_t)); 16450Sstevel@tonic-gate zcopy.zerocopy_version = ZEROCOPY_VERSION_1; 16460Sstevel@tonic-gate zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM; 16470Sstevel@tonic-gate 16480Sstevel@tonic-gate dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq); 16490Sstevel@tonic-gate bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t)); 16500Sstevel@tonic-gate ptr += sizeof (dl_capab_zerocopy_t); 16510Sstevel@tonic-gate } 16520Sstevel@tonic-gate 16538275SEric Cheng /* 16548275SEric Cheng * Direct capability negotiation interface between IP and DLD. 16558275SEric Cheng * Refer to dld.h for details. 16568275SEric Cheng */ 16578275SEric Cheng if (dld_capable) { 16588275SEric Cheng dlsp = (dl_capability_sub_t *)ptr; 16598275SEric Cheng dlsp->dl_cap = DL_CAPAB_DLD; 16608275SEric Cheng dlsp->dl_length = sizeof (dl_capab_dld_t); 16618275SEric Cheng ptr += sizeof (dl_capability_sub_t); 1662269Sericheng 16638275SEric Cheng bzero(&dld, sizeof (dl_capab_dld_t)); 16648275SEric Cheng dld.dld_version = DLD_CURRENT_VERSION; 16658275SEric Cheng dld.dld_capab = (uintptr_t)dld_capab; 16668275SEric Cheng dld.dld_capab_handle = (uintptr_t)dsp; 16678275SEric Cheng 16688275SEric Cheng dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq); 16698275SEric Cheng bcopy(&dld, ptr, sizeof (dl_capab_dld_t)); 16708275SEric Cheng ptr += sizeof (dl_capab_dld_t); 16718275SEric Cheng } 16728275SEric Cheng 16738275SEric Cheng ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize); 1674269Sericheng qreply(q, mp); 16750Sstevel@tonic-gate } 16765113Syz147064 16775113Syz147064 /* 16785113Syz147064 * Disable any enabled capabilities. 16795113Syz147064 */ 16805113Syz147064 void 16815113Syz147064 dld_capabilities_disable(dld_str_t *dsp) 16825113Syz147064 { 16835113Syz147064 if (dsp->ds_polling) 16848275SEric Cheng (void) dld_capab_poll_disable(dsp, NULL); 16855113Syz147064 } 1686