1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25
26 /*
27 * Sysevent Driver for GPEC
28 */
29
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/cred.h>
33 #include <sys/file.h>
34 #include <sys/stat.h>
35 #include <sys/conf.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 #include <sys/modctl.h>
39 #include <sys/open.h> /* OTYP_CHR definition */
40 #include <sys/sysmacros.h> /* L_BITSMINOR definition */
41 #include <sys/bitmap.h>
42 #include <sys/sysevent.h>
43 #include <sys/sysevent_impl.h>
44
45 static dev_info_t *sysevent_devi;
46
47 /* Definitions for binding handle array */
48 static ulong_t sysevent_bitmap_initial = 1; /* index 0 indicates error */
49 static ulong_t *sysevent_minor_bitmap = &sysevent_bitmap_initial;
50 static size_t sysevent_minor_bits = BT_NBIPUL;
51 static kmutex_t sysevent_minor_mutex;
52
53 /*
54 * evchan_ctl acts as a container for the binding handle
55 */
56 typedef struct evchan_ctl {
57 evchan_t *chp;
58 } evchan_ctl_t;
59
60 static void *evchan_ctlp;
61
62 /*
63 * Check if it's a null terminated array - to avoid DoS attack
64 * It is supposed that string points to an array with
65 * a minimum length of len. len must be strlen + 1.
66 * Checks for printable characters are already done in library.
67 */
68 static int
sysevent_isstrend(char * string,size_t len)69 sysevent_isstrend(char *string, size_t len)
70 {
71 /* Return 0 if string has length of zero */
72 if (len > 0) {
73 return (string[len - 1] == '\0' ? 1 : 0);
74 } else {
75 return (0);
76 }
77 }
78
79 /*
80 * Following sysevent_minor_* routines map
81 * a binding handle (evchan_t *) to a minor number
82 * Has to be called w/ locks held.
83 */
84 static ulong_t *
sysevent_minor_alloc(void)85 sysevent_minor_alloc(void)
86 {
87 ulong_t *bhst = sysevent_minor_bitmap;
88
89 /* Increase bitmap by one BT_NBIPUL */
90 if (sysevent_minor_bits + BT_NBIPUL > SYSEVENT_MINOR_MAX) {
91 return ((ulong_t *)NULL);
92 }
93 sysevent_minor_bitmap = kmem_zalloc(
94 BT_SIZEOFMAP(sysevent_minor_bits + BT_NBIPUL), KM_SLEEP);
95 bcopy(bhst, sysevent_minor_bitmap, BT_SIZEOFMAP(sysevent_minor_bits));
96 if (bhst != &sysevent_bitmap_initial)
97 kmem_free(bhst, BT_SIZEOFMAP(sysevent_minor_bits));
98 sysevent_minor_bits += BT_NBIPUL;
99
100 return (sysevent_minor_bitmap);
101 }
102
103 static void
sysevent_minor_free(ulong_t * bitmap)104 sysevent_minor_free(ulong_t *bitmap)
105 {
106 if (bitmap != &sysevent_bitmap_initial)
107 kmem_free(bitmap, BT_SIZEOFMAP(sysevent_minor_bits));
108 }
109
110 static index_t
sysevent_minor_get(void)111 sysevent_minor_get(void)
112 {
113 index_t idx;
114 ulong_t *bhst;
115
116 /* Search for an available index */
117 mutex_enter(&sysevent_minor_mutex);
118 if ((idx = bt_availbit(sysevent_minor_bitmap,
119 sysevent_minor_bits)) == -1) {
120 /* All busy - allocate additional binding handle bitmap space */
121 if ((bhst = sysevent_minor_alloc()) == NULL) {
122 /* Reached our maximum of id's == SHRT_MAX */
123 mutex_exit(&sysevent_minor_mutex);
124 return (0);
125 } else {
126 sysevent_minor_bitmap = bhst;
127 }
128 idx = bt_availbit(sysevent_minor_bitmap, sysevent_minor_bits);
129 }
130 BT_SET(sysevent_minor_bitmap, idx);
131 mutex_exit(&sysevent_minor_mutex);
132 return (idx);
133 }
134
135 static void
sysevent_minor_rele(index_t idx)136 sysevent_minor_rele(index_t idx)
137 {
138 mutex_enter(&sysevent_minor_mutex);
139 ASSERT(BT_TEST(sysevent_minor_bitmap, idx) == 1);
140 BT_CLEAR(sysevent_minor_bitmap, idx);
141 mutex_exit(&sysevent_minor_mutex);
142 }
143
144 static void
sysevent_minor_init(void)145 sysevent_minor_init(void)
146 {
147 mutex_init(&sysevent_minor_mutex, NULL, MUTEX_DEFAULT, NULL);
148 }
149
150 /* ARGSUSED */
151 static int
sysevent_publish(dev_t dev,int * rvalp,void * arg,int flag,cred_t * cr)152 sysevent_publish(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr)
153 {
154 int km_flags;
155 sev_publish_args_t uargs;
156 sysevent_impl_t *ev;
157 evchan_ctl_t *ctl;
158
159 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev));
160 if (ctl == NULL || ctl->chp == NULL)
161 return (ENXIO);
162
163 if (copyin(arg, &uargs, sizeof (sev_publish_args_t)) != 0)
164 return (EFAULT);
165
166 /*
167 * This limits the size of an event
168 */
169 if (uargs.ev.len > MAX_EV_SIZE_LEN)
170 return (EOVERFLOW);
171
172 /*
173 * Check for valid uargs.flags
174 */
175 if (uargs.flags & ~(EVCH_NOSLEEP | EVCH_SLEEP | EVCH_QWAIT))
176 return (EINVAL);
177
178 /*
179 * Check that at least one of EVCH_NOSLEEP or EVCH_SLEEP is
180 * specified
181 */
182 km_flags = uargs.flags & (EVCH_NOSLEEP | EVCH_SLEEP);
183 if (km_flags != EVCH_NOSLEEP && km_flags != EVCH_SLEEP)
184 return (EINVAL);
185
186 ev = evch_usrallocev(uargs.ev.len, uargs.flags);
187
188 if (copyin((void *)(uintptr_t)uargs.ev.name, ev, uargs.ev.len) != 0) {
189 evch_usrfreeev(ev);
190 return (EFAULT);
191 }
192
193 return (evch_usrpostevent(ctl->chp, ev, uargs.flags));
194
195 /* Event will be freed internally */
196 }
197
198 /*
199 * sysevent_chan_open - used to open a channel in the GPEC channel layer
200 */
201
202 /* ARGSUSED */
203 static int
sysevent_chan_open(dev_t dev,int * rvalp,void * arg,int flag,cred_t * cr)204 sysevent_chan_open(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr)
205 {
206 sev_bind_args_t uargs;
207 evchan_ctl_t *ctl;
208 char *chan_name;
209 int ec;
210
211 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev));
212 if (ctl == NULL) {
213 return (ENXIO);
214 }
215
216 if (copyin(arg, &uargs, sizeof (sev_bind_args_t)) != 0)
217 return (EFAULT);
218
219 if (uargs.chan_name.len > MAX_CHNAME_LEN)
220 return (EINVAL);
221
222 chan_name = kmem_alloc(uargs.chan_name.len, KM_SLEEP);
223
224 if (copyin((void *)(uintptr_t)uargs.chan_name.name, chan_name,
225 uargs.chan_name.len) != 0) {
226 kmem_free(chan_name, uargs.chan_name.len);
227 return (EFAULT);
228 }
229
230 if (!sysevent_isstrend(chan_name, uargs.chan_name.len)) {
231 kmem_free(chan_name, uargs.chan_name.len);
232 return (EINVAL);
233 }
234
235 /*
236 * Check of uargs.flags and uargs.perms just to avoid DoS attacks.
237 * libsysevent does this carefully
238 */
239 ctl->chp = evch_usrchanopen((const char *)chan_name,
240 uargs.flags & EVCH_B_FLAGS, &ec);
241
242 kmem_free(chan_name, uargs.chan_name.len);
243
244 if (ec != 0) {
245 return (ec);
246 }
247
248 return (0);
249 }
250
251 /* ARGSUSED */
252 static int
sysevent_chan_control(dev_t dev,int * rvalp,void * arg,int flag,cred_t * cr)253 sysevent_chan_control(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr)
254 {
255 sev_control_args_t uargs;
256 evchan_ctl_t *ctl;
257 int rc;
258
259 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev));
260 if (ctl == NULL || ctl->chp == NULL)
261 return (ENXIO);
262
263 if (copyin(arg, &uargs, sizeof (sev_control_args_t)) != 0)
264 return (EFAULT);
265
266 switch (uargs.cmd) {
267 case EVCH_GET_CHAN_LEN:
268 case EVCH_GET_CHAN_LEN_MAX:
269 rc = evch_usrcontrol_get(ctl->chp, uargs.cmd, &uargs.value);
270 if (rc == 0) {
271 if (copyout((void *)&uargs, arg,
272 sizeof (sev_control_args_t)) != 0) {
273 rc = EFAULT;
274 }
275 }
276 break;
277 case EVCH_SET_CHAN_LEN:
278 rc = evch_usrcontrol_set(ctl->chp, uargs.cmd, uargs.value);
279 break;
280 default:
281 rc = EINVAL;
282 }
283 return (rc);
284 }
285
286 /* ARGSUSED */
287 static int
sysevent_subscribe(dev_t dev,int * rvalp,void * arg,int flag,cred_t * cr)288 sysevent_subscribe(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr)
289 {
290 sev_subscribe_args_t uargs;
291 char *sid;
292 char *class_info = NULL;
293 evchan_ctl_t *ctl;
294 int rc;
295
296 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev));
297 if (ctl == NULL || ctl->chp == NULL)
298 return (ENXIO);
299
300 if (copyin(arg, &uargs, sizeof (sev_subscribe_args_t)) != 0)
301 return (EFAULT);
302
303 if (uargs.sid.len > MAX_SUBID_LEN ||
304 uargs.class_info.len > MAX_CLASS_LEN)
305 return (EINVAL);
306
307 sid = kmem_alloc(uargs.sid.len, KM_SLEEP);
308 if (copyin((void *)(uintptr_t)uargs.sid.name,
309 sid, uargs.sid.len) != 0) {
310 kmem_free(sid, uargs.sid.len);
311 return (EFAULT);
312 }
313 if (!sysevent_isstrend(sid, uargs.sid.len)) {
314 kmem_free(sid, uargs.sid.len);
315 return (EINVAL);
316 }
317
318 /* If class string empty then class EC_ALL is assumed */
319 if (uargs.class_info.len != 0) {
320 class_info = kmem_alloc(uargs.class_info.len, KM_SLEEP);
321 if (copyin((void *)(uintptr_t)uargs.class_info.name, class_info,
322 uargs.class_info.len) != 0) {
323 kmem_free(class_info, uargs.class_info.len);
324 kmem_free(sid, uargs.sid.len);
325 return (EFAULT);
326 }
327 if (!sysevent_isstrend(class_info, uargs.class_info.len)) {
328 kmem_free(class_info, uargs.class_info.len);
329 kmem_free(sid, uargs.sid.len);
330 return (EINVAL);
331 }
332 }
333
334 /*
335 * Check of uargs.flags just to avoid DoS attacks
336 * libsysevent does this carefully.
337 */
338 rc = evch_usrsubscribe(ctl->chp, sid, class_info,
339 (int)uargs.door_desc, uargs.flags);
340
341 kmem_free(class_info, uargs.class_info.len);
342 kmem_free(sid, uargs.sid.len);
343
344 return (rc);
345 }
346
347 /* ARGSUSED */
348 static int
sysevent_unsubscribe(dev_t dev,int * rvalp,void * arg,int flag,cred_t * cr)349 sysevent_unsubscribe(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr)
350 {
351 sev_unsubscribe_args_t uargs;
352 char *sid;
353 evchan_ctl_t *ctl;
354
355 if (copyin(arg, &uargs, sizeof (sev_unsubscribe_args_t)) != 0)
356 return (EFAULT);
357
358 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev));
359 if (ctl == NULL || ctl->chp == NULL)
360 return (ENXIO);
361
362 if (uargs.sid.len > MAX_SUBID_LEN)
363 return (EINVAL);
364
365 /* Unsubscribe for all */
366 if (uargs.sid.len == 0) {
367 evch_usrunsubscribe(ctl->chp, NULL, 0);
368 return (0);
369 }
370
371 sid = kmem_alloc(uargs.sid.len, KM_SLEEP);
372
373 if (copyin((void *)(uintptr_t)uargs.sid.name,
374 sid, uargs.sid.len) != 0) {
375 kmem_free(sid, uargs.sid.len);
376 return (EFAULT);
377 }
378
379 evch_usrunsubscribe(ctl->chp, sid, 0);
380
381 kmem_free(sid, uargs.sid.len);
382
383 return (0);
384 }
385
386 /* ARGSUSED */
387 static int
sysevent_channames(dev_t dev,int * rvalp,void * arg,int flag,cred_t * cr)388 sysevent_channames(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr)
389 {
390 sev_chandata_args_t uargs;
391 char *buf;
392 int len;
393 int rc = 0;
394
395 if (copyin(arg, &uargs, sizeof (sev_chandata_args_t)) != 0)
396 return (EFAULT);
397
398 if (uargs.out_data.len == 0 || uargs.out_data.len > EVCH_MAX_DATA_SIZE)
399 return (EINVAL);
400
401 buf = kmem_alloc(uargs.out_data.len, KM_SLEEP);
402
403 if ((len = evch_usrgetchnames(buf, uargs.out_data.len)) == -1) {
404 rc = EOVERFLOW;
405 }
406
407 if (rc == 0) {
408 ASSERT(len <= uargs.out_data.len);
409 if (copyout(buf,
410 (void *)(uintptr_t)uargs.out_data.name, len) != 0) {
411 rc = EFAULT;
412 }
413 }
414
415 kmem_free(buf, uargs.out_data.len);
416
417 return (rc);
418 }
419
420 /* ARGSUSED */
421 static int
sysevent_chandata(dev_t dev,int * rvalp,void * arg,int flag,cred_t * cr)422 sysevent_chandata(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr)
423 {
424 sev_chandata_args_t uargs;
425 char *channel;
426 char *buf;
427 int len;
428 int rc = 0;
429
430 if (copyin(arg, &uargs, sizeof (sev_chandata_args_t)) != 0)
431 return (EFAULT);
432
433 if (uargs.in_data.len > MAX_CHNAME_LEN ||
434 uargs.out_data.len > EVCH_MAX_DATA_SIZE)
435 return (EINVAL);
436
437 channel = kmem_alloc(uargs.in_data.len, KM_SLEEP);
438
439 if (copyin((void *)(uintptr_t)uargs.in_data.name, channel,
440 uargs.in_data.len) != 0) {
441 kmem_free(channel, uargs.in_data.len);
442 return (EFAULT);
443 }
444
445 if (!sysevent_isstrend(channel, uargs.in_data.len)) {
446 kmem_free(channel, uargs.in_data.len);
447 return (EINVAL);
448 }
449
450 buf = kmem_alloc(uargs.out_data.len, KM_SLEEP);
451
452 len = evch_usrgetchdata(channel, buf, uargs.out_data.len);
453 if (len == 0) {
454 rc = EOVERFLOW;
455 } else if (len == -1) {
456 rc = ENOENT;
457 }
458
459 if (rc == 0) {
460 ASSERT(len <= uargs.out_data.len);
461 if (copyout(buf,
462 (void *)(uintptr_t)uargs.out_data.name, len) != 0) {
463 rc = EFAULT;
464 }
465 }
466
467 kmem_free(buf, uargs.out_data.len);
468 kmem_free(channel, uargs.in_data.len);
469
470 return (rc);
471 }
472
473 /* ARGSUSED */
474 static int
sysevent_setpropnvl(dev_t dev,int * rvalp,void * arg,int flag,cred_t * cr)475 sysevent_setpropnvl(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr)
476 {
477 sev_propnvl_args_t uargs;
478 nvlist_t *nvl = NULL;
479 evchan_ctl_t *ctl;
480 size_t bufsz;
481 char *buf;
482
483 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev));
484 if (ctl == NULL || ctl->chp == NULL)
485 return (ENXIO);
486
487 if (copyin(arg, &uargs, sizeof (uargs)) != 0)
488 return (EFAULT);
489
490 if (uargs.packednvl.name != 0) {
491 bufsz = uargs.packednvl.len;
492
493 if (bufsz == 0)
494 return (EINVAL);
495
496 if (bufsz > EVCH_MAX_DATA_SIZE)
497 return (EOVERFLOW);
498
499 buf = kmem_alloc(bufsz, KM_SLEEP);
500
501 if (copyin((void *)(uintptr_t)uargs.packednvl.name, buf,
502 bufsz) != 0 ||
503 nvlist_unpack(buf, bufsz, &nvl, KM_SLEEP) != 0) {
504 kmem_free(buf, bufsz);
505 return (EFAULT);
506 }
507
508 kmem_free(buf, bufsz);
509
510 if (nvl == NULL)
511 return (EINVAL);
512 }
513
514 evch_usrsetpropnvl(ctl->chp, nvl);
515 return (0);
516 }
517
518 /* ARGSUSED */
519 static int
sysevent_getpropnvl(dev_t dev,int * rvalp,void * arg,int flag,cred_t * cr)520 sysevent_getpropnvl(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr)
521 {
522 sev_propnvl_args_t uargs;
523 size_t reqsz, avlsz;
524 evchan_ctl_t *ctl;
525 nvlist_t *nvl;
526 int64_t gen;
527 int rc;
528
529 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev));
530
531 if (ctl == NULL || ctl->chp == NULL)
532 return (ENXIO);
533
534 if (copyin(arg, &uargs, sizeof (uargs)) != 0)
535 return (EFAULT);
536
537 if ((rc = evch_usrgetpropnvl(ctl->chp, &nvl, &gen)) != 0)
538 return (rc);
539
540 if (nvl != NULL) {
541 avlsz = uargs.packednvl.len;
542
543 if (nvlist_size(nvl, &reqsz, NV_ENCODE_NATIVE) != 0) {
544 nvlist_free(nvl);
545 return (EINVAL);
546 }
547
548 if (reqsz > EVCH_MAX_DATA_SIZE) {
549 nvlist_free(nvl);
550 return (E2BIG);
551 }
552
553 if (reqsz <= avlsz) {
554 char *buf = kmem_alloc(reqsz, KM_SLEEP);
555
556 if (nvlist_pack(nvl, &buf, &reqsz,
557 NV_ENCODE_NATIVE, 0) != 0 || copyout(buf,
558 (void *)(uintptr_t)uargs.packednvl.name,
559 reqsz) != 0) {
560 kmem_free(buf, reqsz);
561 nvlist_free(nvl);
562 return (EFAULT);
563 }
564 kmem_free(buf, reqsz);
565 rc = 0;
566 } else {
567 rc = EOVERFLOW;
568 }
569 uargs.packednvl.len = (uint32_t)reqsz;
570 nvlist_free(nvl);
571 } else {
572 uargs.packednvl.len = 0;
573 rc = 0;
574 }
575
576 uargs.generation = gen;
577 if (copyout((void *)&uargs, arg, sizeof (uargs)) != 0)
578 rc = EFAULT;
579
580 return (rc);
581 }
582
583 /*ARGSUSED*/
584 static int
sysevent_ioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cr,int * rvalp)585 sysevent_ioctl(dev_t dev, int cmd, intptr_t arg,
586 int flag, cred_t *cr, int *rvalp)
587 {
588 int rc;
589
590 switch (cmd) {
591 case SEV_PUBLISH:
592 rc = sysevent_publish(dev, rvalp, (void *)arg, flag, cr);
593 break;
594 case SEV_CHAN_OPEN:
595 rc = sysevent_chan_open(dev, rvalp, (void *)arg, flag, cr);
596 break;
597 case SEV_CHAN_CONTROL:
598 rc = sysevent_chan_control(dev, rvalp, (void *)arg, flag, cr);
599 break;
600 case SEV_SUBSCRIBE:
601 rc = sysevent_subscribe(dev, rvalp, (void *)arg, flag, cr);
602 break;
603 case SEV_UNSUBSCRIBE:
604 rc = sysevent_unsubscribe(dev, rvalp, (void *)arg, flag, cr);
605 break;
606 case SEV_CHANNAMES:
607 rc = sysevent_channames(dev, rvalp, (void *)arg, flag, cr);
608 break;
609 case SEV_CHANDATA:
610 rc = sysevent_chandata(dev, rvalp, (void *)arg, flag, cr);
611 break;
612 case SEV_SETPROPNVL:
613 rc = sysevent_setpropnvl(dev, rvalp, (void *)arg, flag, cr);
614 break;
615 case SEV_GETPROPNVL:
616 rc = sysevent_getpropnvl(dev, rvalp, (void *)arg, flag, cr);
617 break;
618 default:
619 rc = EINVAL;
620 }
621
622 return (rc);
623 }
624
625 /*ARGSUSED*/
626 static int
sysevent_open(dev_t * devp,int flag,int otyp,cred_t * cr)627 sysevent_open(dev_t *devp, int flag, int otyp, cred_t *cr)
628 {
629 int minor;
630
631 if (otyp != OTYP_CHR)
632 return (EINVAL);
633
634 if (getminor(*devp) != 0)
635 return (ENXIO);
636
637 minor = sysevent_minor_get();
638 if (minor == 0)
639 /* All minors are busy */
640 return (EBUSY);
641
642 if (ddi_soft_state_zalloc(evchan_ctlp, minor)
643 != DDI_SUCCESS) {
644 sysevent_minor_rele(minor);
645 return (ENOMEM);
646 }
647
648 *devp = makedevice(getmajor(*devp), minor);
649
650 return (0);
651 }
652
653 /*ARGSUSED*/
654 static int
sysevent_close(dev_t dev,int flag,int otyp,cred_t * cr)655 sysevent_close(dev_t dev, int flag, int otyp, cred_t *cr)
656 {
657 int minor = (int)getminor(dev);
658 evchan_ctl_t *ctl;
659
660 if (otyp != OTYP_CHR)
661 return (EINVAL);
662
663 ctl = ddi_get_soft_state(evchan_ctlp, minor);
664 if (ctl == NULL) {
665 return (ENXIO);
666 }
667
668 if (ctl->chp) {
669 /* Release all non-persistant subscriptions */
670 evch_usrunsubscribe(ctl->chp, NULL, EVCH_SUB_KEEP);
671 evch_usrchanclose(ctl->chp);
672 }
673
674 ddi_soft_state_free(evchan_ctlp, minor);
675 sysevent_minor_rele(minor);
676
677 return (0);
678 }
679
680 /* ARGSUSED */
681 static int
sysevent_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)682 sysevent_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
683 void *arg, void **result)
684 {
685 switch (infocmd) {
686 case DDI_INFO_DEVT2DEVINFO:
687 *result = sysevent_devi;
688 return (DDI_SUCCESS);
689 case DDI_INFO_DEVT2INSTANCE:
690 *result = 0;
691 return (DDI_SUCCESS);
692 }
693 return (DDI_FAILURE);
694 }
695
696 /* ARGSUSED */
697 static int
sysevent_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)698 sysevent_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
699 {
700
701 if (cmd != DDI_ATTACH) {
702 return (DDI_FAILURE);
703 }
704
705 if (ddi_create_minor_node(devi, "sysevent", S_IFCHR,
706 0, DDI_PSEUDO, 0) == DDI_FAILURE) {
707 ddi_remove_minor_node(devi, NULL);
708 return (DDI_FAILURE);
709 }
710 sysevent_devi = devi;
711
712 sysevent_minor_init();
713
714 return (DDI_SUCCESS);
715 }
716
717 static int
sysevent_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)718 sysevent_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
719 {
720 if (cmd != DDI_DETACH) {
721 return (DDI_FAILURE);
722 }
723
724 sysevent_minor_free(sysevent_minor_bitmap);
725 ddi_remove_minor_node(devi, NULL);
726 return (DDI_SUCCESS);
727 }
728
729 static struct cb_ops sysevent_cb_ops = {
730 .cb_open = sysevent_open,
731 .cb_close = sysevent_close,
732 .cb_strategy = nodev,
733 .cb_print = nodev,
734 .cb_dump = nodev,
735 .cb_read = nodev,
736 .cb_write = nodev,
737 .cb_ioctl = sysevent_ioctl,
738 .cb_devmap = nodev,
739 .cb_mmap = nodev,
740 .cb_segmap = nodev,
741 .cb_chpoll = nochpoll,
742 .cb_prop_op = ddi_prop_op,
743 .cb_str = NULL,
744 .cb_flag = D_NEW | D_MP,
745 .cb_rev = CB_REV,
746 .cb_aread = NULL,
747 .cb_awrite = NULL
748 };
749
750 static struct dev_ops sysevent_ops = {
751 DEVO_REV, /* devo_rev */
752 0, /* refcnt */
753 sysevent_info, /* info */
754 nulldev, /* identify */
755 nulldev, /* probe */
756 sysevent_attach, /* attach */
757 sysevent_detach, /* detach */
758 nodev, /* reset */
759 &sysevent_cb_ops, /* driver operations */
760 NULL, /* no bus operations */
761 nulldev, /* power */
762 ddi_quiesce_not_needed, /* quiesce */
763 };
764
765 static struct modldrv modldrv = {
766 &mod_driverops, "sysevent driver", &sysevent_ops
767 };
768
769 static struct modlinkage modlinkage = {
770 MODREV_1, &modldrv, NULL
771 };
772
773 int
_init(void)774 _init(void)
775 {
776 int s;
777
778 s = ddi_soft_state_init(&evchan_ctlp, sizeof (evchan_ctl_t), 1);
779 if (s != 0)
780 return (s);
781
782 if ((s = mod_install(&modlinkage)) != 0)
783 ddi_soft_state_fini(&evchan_ctlp);
784 return (s);
785 }
786
787 int
_fini(void)788 _fini(void)
789 {
790 int s;
791
792 if ((s = mod_remove(&modlinkage)) != 0)
793 return (s);
794
795 ddi_soft_state_fini(&evchan_ctlp);
796 return (s);
797 }
798
799 int
_info(struct modinfo * modinfop)800 _info(struct modinfo *modinfop)
801 {
802 return (mod_info(&modlinkage, modinfop));
803 }
804