stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * CDDL HEADER START |
| 3 | * |
| 4 | * The contents of this file are subject to the terms of the |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 5 | * Common Development and Distribution License (the "License"). |
| 6 | * You may not use this file except in compliance with the License. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 7 | * |
| 8 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| 9 | * or http://www.opensolaris.org/os/licensing. |
| 10 | * See the License for the specific language governing permissions |
| 11 | * and limitations under the License. |
| 12 | * |
| 13 | * When distributing Covered Code, include this CDDL HEADER in each |
| 14 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| 15 | * If applicable, add the following below this CDDL HEADER, with the |
| 16 | * fields enclosed by brackets "[]" replaced with your own identifying |
| 17 | * information: Portions Copyright [yyyy] [name of copyright owner] |
| 18 | * |
| 19 | * CDDL HEADER END |
| 20 | */ |
| 21 | /* |
Sherry Moore | 1939740 | 2008-09-22 16:30:26 -0700 | [diff] [blame] | 22 | * Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 23 | * Use is subject to license terms. |
| 24 | */ |
| 25 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 26 | |
| 27 | /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ |
| 28 | /* All Rights Reserved */ |
| 29 | |
| 30 | |
| 31 | /* |
| 32 | * STREAMS Administrative Driver |
| 33 | * |
| 34 | * Currently only handles autopush and module name verification. |
| 35 | */ |
| 36 | |
| 37 | #include <sys/types.h> |
| 38 | #include <sys/param.h> |
| 39 | #include <sys/errno.h> |
| 40 | #include <sys/stream.h> |
| 41 | #include <sys/stropts.h> |
| 42 | #include <sys/strsubr.h> |
| 43 | #include <sys/strsun.h> |
| 44 | #include <sys/conf.h> |
| 45 | #include <sys/sad.h> |
| 46 | #include <sys/cred.h> |
| 47 | #include <sys/debug.h> |
| 48 | #include <sys/ddi.h> |
| 49 | #include <sys/sunddi.h> |
| 50 | #include <sys/stat.h> |
| 51 | #include <sys/cmn_err.h> |
| 52 | #include <sys/systm.h> |
| 53 | #include <sys/modctl.h> |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 54 | #include <sys/sysmacros.h> |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 55 | #include <sys/zone.h> |
dh155122 | e6bdcbd | 2007-08-30 01:17:19 -0700 | [diff] [blame] | 56 | #include <sys/policy.h> |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 57 | |
| 58 | static int sadopen(queue_t *, dev_t *, int, int, cred_t *); |
| 59 | static int sadclose(queue_t *, int, cred_t *); |
| 60 | static int sadwput(queue_t *qp, mblk_t *mp); |
| 61 | |
| 62 | static int sad_info(dev_info_t *, ddi_info_cmd_t, void *, void **); |
| 63 | static int sad_attach(dev_info_t *, ddi_attach_cmd_t); |
| 64 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 65 | static void apush_ioctl(), apush_iocdata(); |
| 66 | static void vml_ioctl(), vml_iocdata(); |
| 67 | static int valid_major(major_t); |
| 68 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 69 | static dev_info_t *sad_dip; /* private copy of devinfo pointer */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 70 | |
| 71 | static struct module_info sad_minfo = { |
| 72 | 0x7361, "sad", 0, INFPSZ, 0, 0 |
| 73 | }; |
| 74 | |
| 75 | static struct qinit sad_rinit = { |
| 76 | NULL, NULL, sadopen, sadclose, NULL, &sad_minfo, NULL |
| 77 | }; |
| 78 | |
| 79 | static struct qinit sad_winit = { |
| 80 | sadwput, NULL, NULL, NULL, NULL, &sad_minfo, NULL |
| 81 | }; |
| 82 | |
| 83 | struct streamtab sadinfo = { |
| 84 | &sad_rinit, &sad_winit, NULL, NULL |
| 85 | }; |
| 86 | |
| 87 | DDI_DEFINE_STREAM_OPS(sad_ops, nulldev, nulldev, sad_attach, |
jfrank | d51110b | 2007-05-22 11:48:16 -0700 | [diff] [blame] | 88 | nodev, nodev, sad_info, |
Sherry Moore | 1939740 | 2008-09-22 16:30:26 -0700 | [diff] [blame] | 89 | D_MP | D_MTPERQ | D_MTOUTPERIM | D_MTOCEXCL, &sadinfo, |
| 90 | ddi_quiesce_not_supported); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 91 | |
| 92 | /* |
| 93 | * Module linkage information for the kernel. |
| 94 | */ |
| 95 | |
| 96 | static struct modldrv modldrv = { |
| 97 | &mod_driverops, /* Type of module. This one is a pseudo driver */ |
dh155122 | e6bdcbd | 2007-08-30 01:17:19 -0700 | [diff] [blame] | 98 | "STREAMS Administrative Driver 'sad'", |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 99 | &sad_ops, /* driver ops */ |
| 100 | }; |
| 101 | |
| 102 | static struct modlinkage modlinkage = { |
| 103 | MODREV_1, &modldrv, NULL |
| 104 | }; |
| 105 | |
| 106 | int |
| 107 | _init(void) |
| 108 | { |
| 109 | return (mod_install(&modlinkage)); |
| 110 | } |
| 111 | |
| 112 | int |
| 113 | _fini(void) |
| 114 | { |
| 115 | return (mod_remove(&modlinkage)); |
| 116 | } |
| 117 | |
| 118 | int |
| 119 | _info(struct modinfo *modinfop) |
| 120 | { |
| 121 | return (mod_info(&modlinkage, modinfop)); |
| 122 | } |
| 123 | |
| 124 | static int |
| 125 | sad_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) |
| 126 | { |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 127 | int instance = ddi_get_instance(devi); |
| 128 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 129 | if (cmd != DDI_ATTACH) |
| 130 | return (DDI_FAILURE); |
| 131 | |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 132 | ASSERT(instance == 0); |
| 133 | if (instance != 0) |
| 134 | return (DDI_FAILURE); |
| 135 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 136 | if (ddi_create_minor_node(devi, "user", S_IFCHR, |
| 137 | 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 138 | return (DDI_FAILURE); |
| 139 | } |
dh155122 | e6bdcbd | 2007-08-30 01:17:19 -0700 | [diff] [blame] | 140 | if (ddi_create_minor_node(devi, "admin", S_IFCHR, |
| 141 | 1, DDI_PSEUDO, NULL) == DDI_FAILURE) { |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 142 | ddi_remove_minor_node(devi, NULL); |
| 143 | return (DDI_FAILURE); |
| 144 | } |
| 145 | sad_dip = devi; |
| 146 | return (DDI_SUCCESS); |
| 147 | } |
| 148 | |
| 149 | /* ARGSUSED */ |
| 150 | static int |
| 151 | sad_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) |
| 152 | { |
| 153 | int error; |
| 154 | |
| 155 | switch (infocmd) { |
| 156 | case DDI_INFO_DEVT2DEVINFO: |
| 157 | if (sad_dip == NULL) { |
| 158 | error = DDI_FAILURE; |
| 159 | } else { |
| 160 | *result = sad_dip; |
| 161 | error = DDI_SUCCESS; |
| 162 | } |
| 163 | break; |
| 164 | case DDI_INFO_DEVT2INSTANCE: |
| 165 | *result = (void *)0; |
| 166 | error = DDI_SUCCESS; |
| 167 | break; |
| 168 | default: |
| 169 | error = DDI_FAILURE; |
| 170 | } |
| 171 | return (error); |
| 172 | } |
| 173 | |
| 174 | |
| 175 | /* |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 176 | * sadopen() - |
| 177 | * Allocate a sad device. Only one |
| 178 | * open at a time allowed per device. |
| 179 | */ |
| 180 | /* ARGSUSED */ |
| 181 | static int |
| 182 | sadopen( |
| 183 | queue_t *qp, /* pointer to read queue */ |
| 184 | dev_t *devp, /* major/minor device of stream */ |
| 185 | int flag, /* file open flags */ |
| 186 | int sflag, /* stream open flags */ |
| 187 | cred_t *credp) /* user credentials */ |
| 188 | { |
| 189 | int i; |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 190 | netstack_t *ns; |
| 191 | str_stack_t *ss; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 192 | |
| 193 | if (sflag) /* no longer called from clone driver */ |
| 194 | return (EINVAL); |
| 195 | |
dh155122 | e6bdcbd | 2007-08-30 01:17:19 -0700 | [diff] [blame] | 196 | /* Only privileged process can access ADMINDEV */ |
| 197 | if (getminor(*devp) == ADMMIN) { |
| 198 | int err; |
| 199 | |
| 200 | err = secpolicy_sadopen(credp); |
| 201 | |
| 202 | if (err != 0) |
| 203 | return (err); |
| 204 | } |
| 205 | |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 206 | ns = netstack_find_by_cred(credp); |
| 207 | ASSERT(ns != NULL); |
| 208 | ss = ns->netstack_str; |
| 209 | ASSERT(ss != NULL); |
| 210 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 211 | /* |
| 212 | * Both USRMIN and ADMMIN are clone interfaces. |
| 213 | */ |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 214 | for (i = 0; i < ss->ss_sadcnt; i++) |
| 215 | if (ss->ss_saddev[i].sa_qp == NULL) |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 216 | break; |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 217 | if (i >= ss->ss_sadcnt) { /* no such device */ |
| 218 | netstack_rele(ss->ss_netstack); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 219 | return (ENXIO); |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 220 | } |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 221 | switch (getminor(*devp)) { |
| 222 | case USRMIN: /* mere mortal */ |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 223 | ss->ss_saddev[i].sa_flags = 0; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 224 | break; |
| 225 | |
| 226 | case ADMMIN: /* privileged user */ |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 227 | ss->ss_saddev[i].sa_flags = SADPRIV; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 228 | break; |
| 229 | |
| 230 | default: |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 231 | netstack_rele(ss->ss_netstack); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 232 | return (EINVAL); |
| 233 | } |
| 234 | |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 235 | ss->ss_saddev[i].sa_qp = qp; |
| 236 | ss->ss_saddev[i].sa_ss = ss; |
| 237 | qp->q_ptr = (caddr_t)&ss->ss_saddev[i]; |
| 238 | WR(qp)->q_ptr = (caddr_t)&ss->ss_saddev[i]; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 239 | |
| 240 | /* |
| 241 | * NOTE: should the ADMMIN or USRMIN minors change |
| 242 | * then so should the offset of 2 below |
| 243 | * Both USRMIN and ADMMIN are clone interfaces and |
| 244 | * therefore their minor numbers (0 and 1) are reserved. |
| 245 | */ |
| 246 | *devp = makedevice(getemajor(*devp), i + 2); |
| 247 | qprocson(qp); |
| 248 | return (0); |
| 249 | } |
| 250 | |
| 251 | /* |
| 252 | * sadclose() - |
| 253 | * Clean up the data structures. |
| 254 | */ |
| 255 | /* ARGSUSED */ |
| 256 | static int |
| 257 | sadclose( |
| 258 | queue_t *qp, /* pointer to read queue */ |
| 259 | int flag, /* file open flags */ |
| 260 | cred_t *credp) /* user credentials */ |
| 261 | { |
| 262 | struct saddev *sadp; |
| 263 | |
| 264 | qprocsoff(qp); |
| 265 | sadp = (struct saddev *)qp->q_ptr; |
| 266 | sadp->sa_qp = NULL; |
| 267 | sadp->sa_addr = NULL; |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 268 | netstack_rele(sadp->sa_ss->ss_netstack); |
| 269 | sadp->sa_ss = NULL; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 270 | qp->q_ptr = NULL; |
| 271 | WR(qp)->q_ptr = NULL; |
| 272 | return (0); |
| 273 | } |
| 274 | |
| 275 | /* |
| 276 | * sadwput() - |
| 277 | * Write side put procedure. |
| 278 | */ |
| 279 | static int |
| 280 | sadwput( |
| 281 | queue_t *qp, /* pointer to write queue */ |
| 282 | mblk_t *mp) /* message pointer */ |
| 283 | { |
| 284 | struct iocblk *iocp; |
| 285 | |
| 286 | switch (mp->b_datap->db_type) { |
| 287 | case M_FLUSH: |
| 288 | if (*mp->b_rptr & FLUSHR) { |
| 289 | *mp->b_rptr &= ~FLUSHW; |
| 290 | qreply(qp, mp); |
| 291 | } else |
| 292 | freemsg(mp); |
| 293 | break; |
| 294 | |
| 295 | case M_IOCTL: |
| 296 | iocp = (struct iocblk *)mp->b_rptr; |
| 297 | switch (SAD_CMD(iocp->ioc_cmd)) { |
| 298 | case SAD_CMD(SAD_SAP): |
| 299 | case SAD_CMD(SAD_GAP): |
| 300 | apush_ioctl(qp, mp); |
| 301 | break; |
| 302 | |
| 303 | case SAD_VML: |
| 304 | vml_ioctl(qp, mp); |
| 305 | break; |
| 306 | |
| 307 | default: |
| 308 | miocnak(qp, mp, 0, EINVAL); |
| 309 | break; |
| 310 | } |
| 311 | break; |
| 312 | |
| 313 | case M_IOCDATA: |
| 314 | iocp = (struct iocblk *)mp->b_rptr; |
| 315 | switch (SAD_CMD(iocp->ioc_cmd)) { |
| 316 | case SAD_CMD(SAD_SAP): |
| 317 | case SAD_CMD(SAD_GAP): |
| 318 | apush_iocdata(qp, mp); |
| 319 | break; |
| 320 | |
| 321 | case SAD_VML: |
| 322 | vml_iocdata(qp, mp); |
| 323 | break; |
| 324 | |
| 325 | default: |
| 326 | cmn_err(CE_WARN, |
| 327 | "sadwput: invalid ioc_cmd in case M_IOCDATA: %d", |
| 328 | iocp->ioc_cmd); |
| 329 | freemsg(mp); |
| 330 | break; |
| 331 | } |
| 332 | break; |
| 333 | |
| 334 | default: |
| 335 | freemsg(mp); |
| 336 | break; |
| 337 | } /* switch (db_type) */ |
| 338 | return (0); |
| 339 | } |
| 340 | |
| 341 | /* |
| 342 | * apush_ioctl() - |
| 343 | * Handle the M_IOCTL messages associated with |
| 344 | * the autopush feature. |
| 345 | */ |
| 346 | static void |
| 347 | apush_ioctl( |
| 348 | queue_t *qp, /* pointer to write queue */ |
| 349 | mblk_t *mp) /* message pointer */ |
| 350 | { |
| 351 | struct iocblk *iocp; |
| 352 | struct saddev *sadp; |
| 353 | uint_t size; |
| 354 | |
| 355 | iocp = (struct iocblk *)mp->b_rptr; |
| 356 | if (iocp->ioc_count != TRANSPARENT) { |
| 357 | miocnak(qp, mp, 0, EINVAL); |
| 358 | return; |
| 359 | } |
| 360 | if (SAD_VER(iocp->ioc_cmd) > AP_VERSION) { |
| 361 | miocnak(qp, mp, 0, EINVAL); |
| 362 | return; |
| 363 | } |
| 364 | |
| 365 | sadp = (struct saddev *)qp->q_ptr; |
| 366 | switch (SAD_CMD(iocp->ioc_cmd)) { |
| 367 | case SAD_CMD(SAD_SAP): |
| 368 | if (!(sadp->sa_flags & SADPRIV)) { |
| 369 | miocnak(qp, mp, 0, EPERM); |
| 370 | break; |
| 371 | } |
| 372 | /* FALLTHRU */ |
| 373 | |
| 374 | case SAD_CMD(SAD_GAP): |
| 375 | sadp->sa_addr = (caddr_t)*(uintptr_t *)mp->b_cont->b_rptr; |
| 376 | if (SAD_VER(iocp->ioc_cmd) == 1) |
| 377 | size = STRAPUSH_V1_LEN; |
| 378 | else |
| 379 | size = STRAPUSH_V0_LEN; |
| 380 | mcopyin(mp, (void *)GETSTRUCT, size, NULL); |
| 381 | qreply(qp, mp); |
| 382 | break; |
| 383 | |
| 384 | default: |
| 385 | ASSERT(0); |
| 386 | miocnak(qp, mp, 0, EINVAL); |
| 387 | break; |
| 388 | } /* switch (ioc_cmd) */ |
| 389 | } |
| 390 | |
| 391 | /* |
| 392 | * apush_iocdata() - |
| 393 | * Handle the M_IOCDATA messages associated with |
| 394 | * the autopush feature. |
| 395 | */ |
| 396 | static void |
| 397 | apush_iocdata( |
| 398 | queue_t *qp, /* pointer to write queue */ |
| 399 | mblk_t *mp) /* message pointer */ |
| 400 | { |
| 401 | int i, ret; |
| 402 | struct copyresp *csp; |
edp | 4121995 | 2006-11-04 19:31:35 -0800 | [diff] [blame] | 403 | struct strapush *sap = NULL; |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 404 | struct autopush *ap, *ap_tmp; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 405 | struct saddev *sadp; |
| 406 | uint_t size; |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 407 | dev_t dev; |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 408 | str_stack_t *ss; |
| 409 | |
| 410 | sadp = (struct saddev *)qp->q_ptr; |
| 411 | ss = sadp->sa_ss; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 412 | |
| 413 | csp = (struct copyresp *)mp->b_rptr; |
| 414 | if (csp->cp_rval) { /* if there was an error */ |
| 415 | freemsg(mp); |
| 416 | return; |
| 417 | } |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 418 | if (mp->b_cont) { |
| 419 | /* |
edp | 4121995 | 2006-11-04 19:31:35 -0800 | [diff] [blame] | 420 | * sap needed only if mp->b_cont is set. figure out the |
| 421 | * size of the expected sap structure and make sure |
| 422 | * enough data was supplied. |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 423 | */ |
edp | 4121995 | 2006-11-04 19:31:35 -0800 | [diff] [blame] | 424 | if (SAD_VER(csp->cp_cmd) == 1) |
| 425 | size = STRAPUSH_V1_LEN; |
| 426 | else |
| 427 | size = STRAPUSH_V0_LEN; |
| 428 | if (MBLKL(mp->b_cont) < size) { |
| 429 | miocnak(qp, mp, 0, EINVAL); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 430 | return; |
| 431 | } |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 432 | sap = (struct strapush *)mp->b_cont->b_rptr; |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 433 | dev = makedevice(sap->sap_major, sap->sap_minor); |
| 434 | } |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 435 | switch (SAD_CMD(csp->cp_cmd)) { |
| 436 | case SAD_CMD(SAD_SAP): |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 437 | |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 438 | /* currently we only support one SAD_SAP command */ |
| 439 | if (((long)csp->cp_private) != GETSTRUCT) { |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 440 | cmn_err(CE_WARN, |
| 441 | "apush_iocdata: cp_private bad in SAD_SAP: %p", |
| 442 | (void *)csp->cp_private); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 443 | miocnak(qp, mp, 0, EINVAL); |
| 444 | return; |
| 445 | } |
| 446 | |
| 447 | switch (sap->sap_cmd) { |
| 448 | default: |
| 449 | miocnak(qp, mp, 0, EINVAL); |
| 450 | return; |
| 451 | case SAP_ONE: |
| 452 | case SAP_RANGE: |
| 453 | case SAP_ALL: |
| 454 | /* allocate and initialize a new config */ |
| 455 | ap = sad_ap_alloc(); |
| 456 | ap->ap_common = sap->sap_common; |
| 457 | if (SAD_VER(csp->cp_cmd) > 0) |
| 458 | ap->ap_anchor = sap->sap_anchor; |
| 459 | for (i = 0; i < MIN(sap->sap_npush, MAXAPUSH); i++) |
| 460 | (void) strncpy(ap->ap_list[i], |
| 461 | sap->sap_list[i], FMNAMESZ); |
| 462 | |
| 463 | /* sanity check the request */ |
| 464 | if (((ret = sad_ap_verify(ap)) != 0) || |
| 465 | ((ret = valid_major(ap->ap_major)) != 0)) { |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 466 | sad_ap_rele(ap, ss); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 467 | miocnak(qp, mp, 0, ret); |
| 468 | return; |
| 469 | } |
| 470 | |
| 471 | /* check for overlapping configs */ |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 472 | mutex_enter(&ss->ss_sad_lock); |
| 473 | ap_tmp = sad_ap_find(&ap->ap_common, ss); |
| 474 | if (ap_tmp != NULL) { |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 475 | /* already configured */ |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 476 | mutex_exit(&ss->ss_sad_lock); |
| 477 | sad_ap_rele(ap_tmp, ss); |
| 478 | sad_ap_rele(ap, ss); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 479 | miocnak(qp, mp, 0, EEXIST); |
| 480 | return; |
| 481 | } |
| 482 | |
| 483 | /* add the new config to our hash */ |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 484 | sad_ap_insert(ap, ss); |
| 485 | mutex_exit(&ss->ss_sad_lock); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 486 | miocack(qp, mp, 0, 0); |
| 487 | return; |
| 488 | |
| 489 | case SAP_CLEAR: |
| 490 | /* sanity check the request */ |
| 491 | if (ret = valid_major(sap->sap_major)) { |
| 492 | miocnak(qp, mp, 0, ret); |
| 493 | return; |
| 494 | } |
| 495 | |
| 496 | /* search for a matching config */ |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 497 | if ((ap = sad_ap_find_by_dev(dev, ss)) == NULL) { |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 498 | /* no config found */ |
| 499 | miocnak(qp, mp, 0, ENODEV); |
| 500 | return; |
| 501 | } |
| 502 | |
| 503 | /* |
| 504 | * If we matched a SAP_RANGE config |
| 505 | * the minor passed in must match the |
| 506 | * beginning of the range exactly. |
| 507 | */ |
| 508 | if ((ap->ap_type == SAP_RANGE) && |
| 509 | (ap->ap_minor != sap->sap_minor)) { |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 510 | sad_ap_rele(ap, ss); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 511 | miocnak(qp, mp, 0, ERANGE); |
| 512 | return; |
| 513 | } |
| 514 | |
| 515 | /* |
| 516 | * If we matched a SAP_ALL config |
| 517 | * the minor passed in must be 0. |
| 518 | */ |
| 519 | if ((ap->ap_type == SAP_ALL) && |
| 520 | (sap->sap_minor != 0)) { |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 521 | sad_ap_rele(ap, ss); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 522 | miocnak(qp, mp, 0, EINVAL); |
| 523 | return; |
| 524 | } |
| 525 | |
| 526 | /* |
| 527 | * make sure someone else hasn't already |
| 528 | * removed this config from the hash. |
| 529 | */ |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 530 | mutex_enter(&ss->ss_sad_lock); |
| 531 | ap_tmp = sad_ap_find(&ap->ap_common, ss); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 532 | if (ap_tmp != ap) { |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 533 | mutex_exit(&ss->ss_sad_lock); |
| 534 | sad_ap_rele(ap_tmp, ss); |
| 535 | sad_ap_rele(ap, ss); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 536 | miocnak(qp, mp, 0, ENODEV); |
| 537 | return; |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 538 | } |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 539 | |
| 540 | /* remove the config from the hash and return */ |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 541 | sad_ap_remove(ap, ss); |
| 542 | mutex_exit(&ss->ss_sad_lock); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 543 | |
| 544 | /* |
| 545 | * Release thrice, once for sad_ap_find_by_dev(), |
| 546 | * once for sad_ap_find(), and once to free. |
| 547 | */ |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 548 | sad_ap_rele(ap, ss); |
| 549 | sad_ap_rele(ap, ss); |
| 550 | sad_ap_rele(ap, ss); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 551 | miocack(qp, mp, 0, 0); |
| 552 | return; |
| 553 | } /* switch (sap_cmd) */ |
| 554 | /*NOTREACHED*/ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 555 | |
| 556 | case SAD_CMD(SAD_GAP): |
| 557 | switch ((long)csp->cp_private) { |
| 558 | |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 559 | case GETSTRUCT: |
| 560 | /* sanity check the request */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 561 | if (ret = valid_major(sap->sap_major)) { |
| 562 | miocnak(qp, mp, 0, ret); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 563 | return; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 564 | } |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 565 | |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 566 | /* search for a matching config */ |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 567 | if ((ap = sad_ap_find_by_dev(dev, ss)) == NULL) { |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 568 | /* no config found */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 569 | miocnak(qp, mp, 0, ENODEV); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 570 | return; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 571 | } |
| 572 | |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 573 | /* copy out the contents of the config */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 574 | sap->sap_common = ap->ap_common; |
| 575 | if (SAD_VER(csp->cp_cmd) > 0) |
| 576 | sap->sap_anchor = ap->ap_anchor; |
| 577 | for (i = 0; i < ap->ap_npush; i++) |
| 578 | (void) strcpy(sap->sap_list[i], ap->ap_list[i]); |
| 579 | for (; i < MAXAPUSH; i++) |
| 580 | bzero(sap->sap_list[i], FMNAMESZ + 1); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 581 | |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 582 | /* release our hold on the config */ |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 583 | sad_ap_rele(ap, ss); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 584 | |
| 585 | /* copyout the results */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 586 | if (SAD_VER(csp->cp_cmd) == 1) |
| 587 | size = STRAPUSH_V1_LEN; |
| 588 | else |
| 589 | size = STRAPUSH_V0_LEN; |
| 590 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 591 | mcopyout(mp, (void *)GETRESULT, size, sadp->sa_addr, |
| 592 | NULL); |
| 593 | qreply(qp, mp); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 594 | return; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 595 | case GETRESULT: |
| 596 | miocack(qp, mp, 0, 0); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 597 | return; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 598 | |
| 599 | default: |
| 600 | cmn_err(CE_WARN, |
| 601 | "apush_iocdata: cp_private bad case SAD_GAP: %p", |
| 602 | (void *)csp->cp_private); |
| 603 | freemsg(mp); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 604 | return; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 605 | } /* switch (cp_private) */ |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 606 | /*NOTREACHED*/ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 607 | default: /* can't happen */ |
| 608 | ASSERT(0); |
| 609 | freemsg(mp); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 610 | return; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 611 | } /* switch (cp_cmd) */ |
| 612 | } |
| 613 | |
| 614 | /* |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 615 | * vml_ioctl() - |
| 616 | * Handle the M_IOCTL message associated with a request |
| 617 | * to validate a module list. |
| 618 | */ |
| 619 | static void |
| 620 | vml_ioctl( |
| 621 | queue_t *qp, /* pointer to write queue */ |
| 622 | mblk_t *mp) /* message pointer */ |
| 623 | { |
| 624 | struct iocblk *iocp; |
| 625 | |
| 626 | iocp = (struct iocblk *)mp->b_rptr; |
| 627 | if (iocp->ioc_count != TRANSPARENT) { |
| 628 | miocnak(qp, mp, 0, EINVAL); |
| 629 | return; |
| 630 | } |
ethindra | 64248fb | 2006-12-22 11:52:51 -0800 | [diff] [blame] | 631 | ASSERT(SAD_CMD(iocp->ioc_cmd) == SAD_VML); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 632 | mcopyin(mp, (void *)GETSTRUCT, |
| 633 | SIZEOF_STRUCT(str_list, iocp->ioc_flag), NULL); |
| 634 | qreply(qp, mp); |
| 635 | } |
| 636 | |
| 637 | /* |
| 638 | * vml_iocdata() - |
| 639 | * Handle the M_IOCDATA messages associated with |
| 640 | * a request to validate a module list. |
| 641 | */ |
| 642 | static void |
| 643 | vml_iocdata( |
| 644 | queue_t *qp, /* pointer to write queue */ |
| 645 | mblk_t *mp) /* message pointer */ |
| 646 | { |
| 647 | long i; |
| 648 | int nmods; |
| 649 | struct copyresp *csp; |
| 650 | struct str_mlist *lp; |
| 651 | STRUCT_HANDLE(str_list, slp); |
| 652 | struct saddev *sadp; |
| 653 | |
| 654 | csp = (struct copyresp *)mp->b_rptr; |
| 655 | if (csp->cp_rval) { /* if there was an error */ |
| 656 | freemsg(mp); |
| 657 | return; |
| 658 | } |
| 659 | |
ethindra | 64248fb | 2006-12-22 11:52:51 -0800 | [diff] [blame] | 660 | ASSERT(SAD_CMD(csp->cp_cmd) == SAD_VML); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 661 | sadp = (struct saddev *)qp->q_ptr; |
| 662 | switch ((long)csp->cp_private) { |
| 663 | case GETSTRUCT: |
| 664 | STRUCT_SET_HANDLE(slp, csp->cp_flag, |
| 665 | (struct str_list *)mp->b_cont->b_rptr); |
| 666 | nmods = STRUCT_FGET(slp, sl_nmods); |
| 667 | if (nmods <= 0) { |
| 668 | miocnak(qp, mp, 0, EINVAL); |
| 669 | break; |
| 670 | } |
| 671 | sadp->sa_addr = (caddr_t)(uintptr_t)nmods; |
| 672 | |
| 673 | mcopyin(mp, (void *)GETLIST, nmods * sizeof (struct str_mlist), |
| 674 | STRUCT_FGETP(slp, sl_modlist)); |
| 675 | qreply(qp, mp); |
| 676 | break; |
| 677 | |
| 678 | case GETLIST: |
| 679 | lp = (struct str_mlist *)mp->b_cont->b_rptr; |
| 680 | for (i = 0; i < (long)sadp->sa_addr; i++, lp++) { |
| 681 | lp->l_name[FMNAMESZ] = '\0'; |
| 682 | if (fmodsw_find(lp->l_name, FMODSW_LOAD) == NULL) { |
| 683 | miocack(qp, mp, 0, 1); |
| 684 | return; |
| 685 | } |
| 686 | } |
| 687 | miocack(qp, mp, 0, 0); |
| 688 | break; |
| 689 | |
| 690 | default: |
| 691 | cmn_err(CE_WARN, "vml_iocdata: invalid cp_private value: %p", |
| 692 | (void *)csp->cp_private); |
| 693 | freemsg(mp); |
| 694 | break; |
| 695 | } /* switch (cp_private) */ |
| 696 | } |
| 697 | |
| 698 | /* |
| 699 | * Validate a major number and also verify if |
| 700 | * it is a STREAMS device. |
| 701 | * Return values: 0 if a valid STREAMS dev |
| 702 | * error code otherwise |
| 703 | */ |
| 704 | static int |
| 705 | valid_major(major_t major) |
| 706 | { |
| 707 | int ret = 0; |
| 708 | |
| 709 | if (etoimajor(major) == -1) |
| 710 | return (EINVAL); |
| 711 | |
| 712 | /* |
| 713 | * attempt to load the driver 'major' and verify that |
| 714 | * it is a STREAMS driver. |
| 715 | */ |
| 716 | if (ddi_hold_driver(major) == NULL) |
| 717 | return (EINVAL); |
| 718 | |
| 719 | if (!STREAMSTAB(major)) |
| 720 | ret = ENOSTR; |
| 721 | |
| 722 | ddi_rele_driver(major); |
| 723 | |
| 724 | return (ret); |
| 725 | } |