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.
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.
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]
22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
27 * Sysevent Driver for GPEC
30 #include <sys/types.h>
31 #include <sys/param.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>
45 static dev_info_t
*sysevent_devi
;
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
;
54 * evchan_ctl acts as a container for the binding handle
56 typedef struct evchan_ctl
{
60 static void *evchan_ctlp
;
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.
69 sysevent_isstrend(char *string
, size_t len
)
71 /* Return 0 if string has length of zero */
73 return (string
[len
- 1] == '\0' ? 1 : 0);
80 * Following sysevent_minor_* routines map
81 * a binding handle (evchan_t *) to a minor number
82 * Has to be called w/ locks held.
85 sysevent_minor_alloc(void)
87 ulong_t
*bhst
= sysevent_minor_bitmap
;
89 /* Increase bitmap by one BT_NBIPUL */
90 if (sysevent_minor_bits
+ BT_NBIPUL
> SYSEVENT_MINOR_MAX
) {
91 return ((ulong_t
*)NULL
);
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
;
100 return (sysevent_minor_bitmap
);
104 sysevent_minor_free(ulong_t
*bitmap
)
106 if (bitmap
!= &sysevent_bitmap_initial
)
107 kmem_free(bitmap
, BT_SIZEOFMAP(sysevent_minor_bits
));
111 sysevent_minor_get(void)
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
);
126 sysevent_minor_bitmap
= bhst
;
128 idx
= bt_availbit(sysevent_minor_bitmap
, sysevent_minor_bits
);
130 BT_SET(sysevent_minor_bitmap
, idx
);
131 mutex_exit(&sysevent_minor_mutex
);
136 sysevent_minor_rele(index_t idx
)
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
);
145 sysevent_minor_init(void)
147 mutex_init(&sysevent_minor_mutex
, NULL
, MUTEX_DEFAULT
, NULL
);
152 sysevent_publish(dev_t dev
, int *rvalp
, void *arg
, int flag
, cred_t
*cr
)
155 sev_publish_args_t uargs
;
159 ctl
= ddi_get_soft_state(evchan_ctlp
, getminor(dev
));
160 if (ctl
== NULL
|| ctl
->chp
== NULL
)
163 if (copyin(arg
, &uargs
, sizeof (sev_publish_args_t
)) != 0)
167 * This limits the size of an event
169 if (uargs
.ev
.len
> MAX_EV_SIZE_LEN
)
173 * Check for valid uargs.flags
175 if (uargs
.flags
& ~(EVCH_NOSLEEP
| EVCH_SLEEP
| EVCH_QWAIT
))
179 * Check that at least one of EVCH_NOSLEEP or EVCH_SLEEP is
182 km_flags
= uargs
.flags
& (EVCH_NOSLEEP
| EVCH_SLEEP
);
183 if (km_flags
!= EVCH_NOSLEEP
&& km_flags
!= EVCH_SLEEP
)
186 ev
= evch_usrallocev(uargs
.ev
.len
, uargs
.flags
);
188 if (copyin((void *)(uintptr_t)uargs
.ev
.name
, ev
, uargs
.ev
.len
) != 0) {
193 return (evch_usrpostevent(ctl
->chp
, ev
, uargs
.flags
));
195 /* Event will be freed internally */
199 * sysevent_chan_open - used to open a channel in the GPEC channel layer
204 sysevent_chan_open(dev_t dev
, int *rvalp
, void *arg
, int flag
, cred_t
*cr
)
206 sev_bind_args_t uargs
;
211 ctl
= ddi_get_soft_state(evchan_ctlp
, getminor(dev
));
216 if (copyin(arg
, &uargs
, sizeof (sev_bind_args_t
)) != 0)
219 if (uargs
.chan_name
.len
> MAX_CHNAME_LEN
)
222 chan_name
= kmem_alloc(uargs
.chan_name
.len
, KM_SLEEP
);
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
);
230 if (!sysevent_isstrend(chan_name
, uargs
.chan_name
.len
)) {
231 kmem_free(chan_name
, uargs
.chan_name
.len
);
236 * Check of uargs.flags and uargs.perms just to avoid DoS attacks.
237 * libsysevent does this carefully
239 ctl
->chp
= evch_usrchanopen((const char *)chan_name
,
240 uargs
.flags
& EVCH_B_FLAGS
, &ec
);
242 kmem_free(chan_name
, uargs
.chan_name
.len
);
253 sysevent_chan_control(dev_t dev
, int *rvalp
, void *arg
, int flag
, cred_t
*cr
)
255 sev_control_args_t uargs
;
259 ctl
= ddi_get_soft_state(evchan_ctlp
, getminor(dev
));
260 if (ctl
== NULL
|| ctl
->chp
== NULL
)
263 if (copyin(arg
, &uargs
, sizeof (sev_control_args_t
)) != 0)
267 case EVCH_GET_CHAN_LEN
:
268 case EVCH_GET_CHAN_LEN_MAX
:
269 rc
= evch_usrcontrol_get(ctl
->chp
, uargs
.cmd
, &uargs
.value
);
271 if (copyout((void *)&uargs
, arg
,
272 sizeof (sev_control_args_t
)) != 0) {
277 case EVCH_SET_CHAN_LEN
:
278 rc
= evch_usrcontrol_set(ctl
->chp
, uargs
.cmd
, uargs
.value
);
288 sysevent_subscribe(dev_t dev
, int *rvalp
, void *arg
, int flag
, cred_t
*cr
)
290 sev_subscribe_args_t uargs
;
292 char *class_info
= NULL
;
296 ctl
= ddi_get_soft_state(evchan_ctlp
, getminor(dev
));
297 if (ctl
== NULL
|| ctl
->chp
== NULL
)
300 if (copyin(arg
, &uargs
, sizeof (sev_subscribe_args_t
)) != 0)
303 if (uargs
.sid
.len
> MAX_SUBID_LEN
||
304 uargs
.class_info
.len
> MAX_CLASS_LEN
)
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
);
313 if (!sysevent_isstrend(sid
, uargs
.sid
.len
)) {
314 kmem_free(sid
, uargs
.sid
.len
);
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
);
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
);
335 * Check of uargs.flags just to avoid DoS attacks
336 * libsysevent does this carefully.
338 rc
= evch_usrsubscribe(ctl
->chp
, sid
, class_info
,
339 (int)uargs
.door_desc
, uargs
.flags
);
341 kmem_free(class_info
, uargs
.class_info
.len
);
342 kmem_free(sid
, uargs
.sid
.len
);
349 sysevent_unsubscribe(dev_t dev
, int *rvalp
, void *arg
, int flag
, cred_t
*cr
)
351 sev_unsubscribe_args_t uargs
;
355 if (copyin(arg
, &uargs
, sizeof (sev_unsubscribe_args_t
)) != 0)
358 ctl
= ddi_get_soft_state(evchan_ctlp
, getminor(dev
));
359 if (ctl
== NULL
|| ctl
->chp
== NULL
)
362 if (uargs
.sid
.len
> MAX_SUBID_LEN
)
365 /* Unsubscribe for all */
366 if (uargs
.sid
.len
== 0) {
367 evch_usrunsubscribe(ctl
->chp
, NULL
, 0);
371 sid
= kmem_alloc(uargs
.sid
.len
, KM_SLEEP
);
373 if (copyin((void *)(uintptr_t)uargs
.sid
.name
,
374 sid
, uargs
.sid
.len
) != 0) {
375 kmem_free(sid
, uargs
.sid
.len
);
379 evch_usrunsubscribe(ctl
->chp
, sid
, 0);
381 kmem_free(sid
, uargs
.sid
.len
);
388 sysevent_channames(dev_t dev
, int *rvalp
, void *arg
, int flag
, cred_t
*cr
)
390 sev_chandata_args_t uargs
;
395 if (copyin(arg
, &uargs
, sizeof (sev_chandata_args_t
)) != 0)
398 if (uargs
.out_data
.len
== 0 || uargs
.out_data
.len
> EVCH_MAX_DATA_SIZE
)
401 buf
= kmem_alloc(uargs
.out_data
.len
, KM_SLEEP
);
403 if ((len
= evch_usrgetchnames(buf
, uargs
.out_data
.len
)) == -1) {
408 ASSERT(len
<= uargs
.out_data
.len
);
410 (void *)(uintptr_t)uargs
.out_data
.name
, len
) != 0) {
415 kmem_free(buf
, uargs
.out_data
.len
);
422 sysevent_chandata(dev_t dev
, int *rvalp
, void *arg
, int flag
, cred_t
*cr
)
424 sev_chandata_args_t uargs
;
430 if (copyin(arg
, &uargs
, sizeof (sev_chandata_args_t
)) != 0)
433 if (uargs
.in_data
.len
> MAX_CHNAME_LEN
||
434 uargs
.out_data
.len
> EVCH_MAX_DATA_SIZE
)
437 channel
= kmem_alloc(uargs
.in_data
.len
, KM_SLEEP
);
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
);
445 if (!sysevent_isstrend(channel
, uargs
.in_data
.len
)) {
446 kmem_free(channel
, uargs
.in_data
.len
);
450 buf
= kmem_alloc(uargs
.out_data
.len
, KM_SLEEP
);
452 len
= evch_usrgetchdata(channel
, buf
, uargs
.out_data
.len
);
455 } else if (len
== -1) {
460 ASSERT(len
<= uargs
.out_data
.len
);
462 (void *)(uintptr_t)uargs
.out_data
.name
, len
) != 0) {
467 kmem_free(buf
, uargs
.out_data
.len
);
468 kmem_free(channel
, uargs
.in_data
.len
);
475 sysevent_setpropnvl(dev_t dev
, int *rvalp
, void *arg
, int flag
, cred_t
*cr
)
477 sev_propnvl_args_t uargs
;
478 nvlist_t
*nvl
= NULL
;
483 ctl
= ddi_get_soft_state(evchan_ctlp
, getminor(dev
));
484 if (ctl
== NULL
|| ctl
->chp
== NULL
)
487 if (copyin(arg
, &uargs
, sizeof (uargs
)) != 0)
490 if (uargs
.packednvl
.name
!= 0) {
491 bufsz
= uargs
.packednvl
.len
;
496 if (bufsz
> EVCH_MAX_DATA_SIZE
)
499 buf
= kmem_alloc(bufsz
, KM_SLEEP
);
501 if (copyin((void *)(uintptr_t)uargs
.packednvl
.name
, buf
,
503 nvlist_unpack(buf
, bufsz
, &nvl
, KM_SLEEP
) != 0) {
504 kmem_free(buf
, bufsz
);
508 kmem_free(buf
, bufsz
);
514 evch_usrsetpropnvl(ctl
->chp
, nvl
);
520 sysevent_getpropnvl(dev_t dev
, int *rvalp
, void *arg
, int flag
, cred_t
*cr
)
522 sev_propnvl_args_t uargs
;
529 ctl
= ddi_get_soft_state(evchan_ctlp
, getminor(dev
));
531 if (ctl
== NULL
|| ctl
->chp
== NULL
)
534 if (copyin(arg
, &uargs
, sizeof (uargs
)) != 0)
537 if ((rc
= evch_usrgetpropnvl(ctl
->chp
, &nvl
, &gen
)) != 0)
541 avlsz
= uargs
.packednvl
.len
;
543 if (nvlist_size(nvl
, &reqsz
, NV_ENCODE_NATIVE
) != 0) {
548 if (reqsz
> EVCH_MAX_DATA_SIZE
) {
553 if (reqsz
<= avlsz
) {
554 char *buf
= kmem_alloc(reqsz
, KM_SLEEP
);
556 if (nvlist_pack(nvl
, &buf
, &reqsz
,
557 NV_ENCODE_NATIVE
, 0) != 0 || copyout(buf
,
558 (void *)(uintptr_t)uargs
.packednvl
.name
,
560 kmem_free(buf
, reqsz
);
564 kmem_free(buf
, reqsz
);
569 uargs
.packednvl
.len
= (uint32_t)reqsz
;
572 uargs
.packednvl
.len
= 0;
576 uargs
.generation
= gen
;
577 if (copyout((void *)&uargs
, arg
, sizeof (uargs
)) != 0)
585 sysevent_ioctl(dev_t dev
, int cmd
, intptr_t arg
,
586 int flag
, cred_t
*cr
, int *rvalp
)
592 rc
= sysevent_publish(dev
, rvalp
, (void *)arg
, flag
, cr
);
595 rc
= sysevent_chan_open(dev
, rvalp
, (void *)arg
, flag
, cr
);
597 case SEV_CHAN_CONTROL
:
598 rc
= sysevent_chan_control(dev
, rvalp
, (void *)arg
, flag
, cr
);
601 rc
= sysevent_subscribe(dev
, rvalp
, (void *)arg
, flag
, cr
);
603 case SEV_UNSUBSCRIBE
:
604 rc
= sysevent_unsubscribe(dev
, rvalp
, (void *)arg
, flag
, cr
);
607 rc
= sysevent_channames(dev
, rvalp
, (void *)arg
, flag
, cr
);
610 rc
= sysevent_chandata(dev
, rvalp
, (void *)arg
, flag
, cr
);
613 rc
= sysevent_setpropnvl(dev
, rvalp
, (void *)arg
, flag
, cr
);
616 rc
= sysevent_getpropnvl(dev
, rvalp
, (void *)arg
, flag
, cr
);
627 sysevent_open(dev_t
*devp
, int flag
, int otyp
, cred_t
*cr
)
631 if (otyp
!= OTYP_CHR
)
634 if (getminor(*devp
) != 0)
637 minor
= sysevent_minor_get();
639 /* All minors are busy */
642 if (ddi_soft_state_zalloc(evchan_ctlp
, minor
)
644 sysevent_minor_rele(minor
);
648 *devp
= makedevice(getmajor(*devp
), minor
);
655 sysevent_close(dev_t dev
, int flag
, int otyp
, cred_t
*cr
)
657 int minor
= (int)getminor(dev
);
660 if (otyp
!= OTYP_CHR
)
663 ctl
= ddi_get_soft_state(evchan_ctlp
, minor
);
669 /* Release all non-persistant subscriptions */
670 evch_usrunsubscribe(ctl
->chp
, NULL
, EVCH_SUB_KEEP
);
671 evch_usrchanclose(ctl
->chp
);
674 ddi_soft_state_free(evchan_ctlp
, minor
);
675 sysevent_minor_rele(minor
);
682 sysevent_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
,
683 void *arg
, void **result
)
686 case DDI_INFO_DEVT2DEVINFO
:
687 *result
= sysevent_devi
;
688 return (DDI_SUCCESS
);
689 case DDI_INFO_DEVT2INSTANCE
:
691 return (DDI_SUCCESS
);
693 return (DDI_FAILURE
);
698 sysevent_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
701 if (cmd
!= DDI_ATTACH
) {
702 return (DDI_FAILURE
);
705 if (ddi_create_minor_node(devi
, "sysevent", S_IFCHR
,
706 0, DDI_PSEUDO
, NULL
) == DDI_FAILURE
) {
707 ddi_remove_minor_node(devi
, NULL
);
708 return (DDI_FAILURE
);
710 sysevent_devi
= devi
;
712 sysevent_minor_init();
714 return (DDI_SUCCESS
);
718 sysevent_detach(dev_info_t
*devi
, ddi_detach_cmd_t cmd
)
720 if (cmd
!= DDI_DETACH
) {
721 return (DDI_FAILURE
);
724 sysevent_minor_free(sysevent_minor_bitmap
);
725 ddi_remove_minor_node(devi
, NULL
);
726 return (DDI_SUCCESS
);
729 static struct cb_ops sysevent_cb_ops
= {
730 sysevent_open
, /* open */
731 sysevent_close
, /* close */
732 nodev
, /* strategy */
737 sysevent_ioctl
, /* ioctl */
742 ddi_prop_op
, /* prop_op */
744 D_NEW
|D_MP
, /* flag */
749 static struct dev_ops sysevent_ops
= {
750 DEVO_REV
, /* devo_rev */
752 sysevent_info
, /* info */
753 nulldev
, /* identify */
755 sysevent_attach
, /* attach */
756 sysevent_detach
, /* detach */
758 &sysevent_cb_ops
, /* driver operations */
759 (struct bus_ops
*)0, /* no bus operations */
761 ddi_quiesce_not_needed
, /* quiesce */
764 static struct modldrv modldrv
= {
765 &mod_driverops
, "sysevent driver", &sysevent_ops
768 static struct modlinkage modlinkage
= {
769 MODREV_1
, &modldrv
, NULL
777 s
= ddi_soft_state_init(&evchan_ctlp
, sizeof (evchan_ctl_t
), 1);
781 if ((s
= mod_install(&modlinkage
)) != 0)
782 ddi_soft_state_fini(&evchan_ctlp
);
791 if ((s
= mod_remove(&modlinkage
)) != 0)
794 ddi_soft_state_fini(&evchan_ctlp
);
799 _info(struct modinfo
*modinfop
)
801 return (mod_info(&modlinkage
, modinfop
));