- struct ctl *ctl = (struct ctl *)so->so_pcb;
- int error = 0, s;
- void *data;
- size_t len;
-
- if (sopt->sopt_level != SYSPROTO_CONTROL) {
- return(EINVAL);
- }
-
- if (ctl == NULL)
- return(ENOTCONN);
-
- switch (sopt->sopt_dir) {
- case SOPT_SET:
- if (ctl->set == NULL)
- return(ENOTSUP);
- MALLOC(data, void *, sopt->sopt_valsize, M_TEMP, M_WAITOK);
- if (data == NULL)
- return(ENOMEM);
- error = sooptcopyin(sopt, data, sopt->sopt_valsize, sopt->sopt_valsize);
- if (error == 0)
- error = (*ctl->set)(ctl, ctl->userdata, sopt->sopt_name, data, sopt->sopt_valsize);
- FREE(data, M_TEMP);
- break;
-
- case SOPT_GET:
- if (ctl->get == NULL)
- return(ENOTSUP);
- data = NULL;
- if (sopt->sopt_valsize && sopt->sopt_val) {
- MALLOC(data, void *, sopt->sopt_valsize, M_TEMP, M_WAITOK);
- if (data == NULL)
- return(ENOMEM);
- }
- len = sopt->sopt_valsize;
- error = (*ctl->get)(ctl, ctl->userdata, sopt->sopt_name, data, &len);
- if (error == 0) {
- if (data != NULL)
- error = sooptcopyout(sopt, data, len);
- else
- sopt->sopt_valsize = len;
- }
- if (data != NULL)
- FREE(data, M_TEMP);
- break;
- }
- return error;
-}
-
-int ctl_ioctl(struct socket *so, u_long cmd, caddr_t data,
- struct ifnet *ifp, struct proc *p)
-{
- int error = ENOTSUP, s, n;
- struct ctl *ctl = (struct ctl *)so->so_pcb;
-
- switch (cmd) {
- /* get the number of controllers */
- case CTLIOCGCOUNT:
- n = 0;
- TAILQ_FOREACH(ctl, &ctl_head, next)
- n++;
- *(u_int32_t *)data = n;
- error = 0;
- break;
-
-
- /* add controls to get list of NKEs */
-
- }
-
- return error;
+ struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
+ struct kctl *kctl;
+ int error = 0;
+ void *data = NULL;
+ size_t len;
+
+ if (sopt->sopt_level != SYSPROTO_CONTROL) {
+ return (EINVAL);
+ }
+
+ if (kcb == NULL) /* sanity check */
+ return (ENOTCONN);
+
+ if ((kctl = kcb->kctl) == NULL)
+ return (EINVAL);
+
+ switch (sopt->sopt_dir) {
+ case SOPT_SET:
+ if (kctl->setopt == NULL)
+ return (ENOTSUP);
+ if (sopt->sopt_valsize != 0) {
+ MALLOC(data, void *, sopt->sopt_valsize, M_TEMP,
+ M_WAITOK | M_ZERO);
+ if (data == NULL)
+ return (ENOMEM);
+ error = sooptcopyin(sopt, data,
+ sopt->sopt_valsize, sopt->sopt_valsize);
+ }
+ if (error == 0) {
+ socket_unlock(so, 0);
+ error = (*kctl->setopt)(kctl->kctlref,
+ kcb->sac.sc_unit, kcb->userdata, sopt->sopt_name,
+ data, sopt->sopt_valsize);
+ socket_lock(so, 0);
+ }
+
+ if (data != NULL)
+ FREE(data, M_TEMP);
+ break;
+
+ case SOPT_GET:
+ if (kctl->getopt == NULL)
+ return (ENOTSUP);
+
+ if (sopt->sopt_valsize && sopt->sopt_val) {
+ MALLOC(data, void *, sopt->sopt_valsize, M_TEMP,
+ M_WAITOK | M_ZERO);
+ if (data == NULL)
+ return (ENOMEM);
+ /*
+ * 4108337 - copy user data in case the
+ * kernel control needs it
+ */
+ error = sooptcopyin(sopt, data,
+ sopt->sopt_valsize, sopt->sopt_valsize);
+ }
+
+ if (error == 0) {
+ len = sopt->sopt_valsize;
+ socket_unlock(so, 0);
+ error = (*kctl->getopt)(kctl->kctlref, kcb->sac.sc_unit,
+ kcb->userdata, sopt->sopt_name,
+ data, &len);
+ if (data != NULL && len > sopt->sopt_valsize)
+ panic_plain("ctl_ctloutput: ctl %s returned "
+ "len (%lu) > sopt_valsize (%lu)\n",
+ kcb->kctl->name, len,
+ sopt->sopt_valsize);
+ socket_lock(so, 0);
+ if (error == 0) {
+ if (data != NULL)
+ error = sooptcopyout(sopt, data, len);
+ else
+ sopt->sopt_valsize = len;
+ }
+ }
+ if (data != NULL)
+ FREE(data, M_TEMP);
+ break;
+ }
+ return (error);
+}
+
+static int
+ctl_ioctl(struct socket *so, u_long cmd, caddr_t data,
+ struct ifnet *ifp, struct proc *p)
+{
+#pragma unused(so, ifp, p)
+ int error = ENOTSUP;
+
+ switch (cmd) {
+ /* get the number of controllers */
+ case CTLIOCGCOUNT: {
+ struct kctl *kctl;
+ u_int32_t n = 0;
+
+ lck_mtx_lock(ctl_mtx);
+ TAILQ_FOREACH(kctl, &ctl_head, next)
+ n++;
+ lck_mtx_unlock(ctl_mtx);
+
+ bcopy(&n, data, sizeof (n));
+ error = 0;
+ break;
+ }
+ case CTLIOCGINFO: {
+ struct ctl_info ctl_info;
+ struct kctl *kctl = 0;
+ size_t name_len;
+
+ bcopy(data, &ctl_info, sizeof (ctl_info));
+ name_len = strnlen(ctl_info.ctl_name, MAX_KCTL_NAME);
+
+ if (name_len == 0 || name_len + 1 > MAX_KCTL_NAME) {
+ error = EINVAL;
+ break;
+ }
+ lck_mtx_lock(ctl_mtx);
+ kctl = ctl_find_by_name(ctl_info.ctl_name);
+ lck_mtx_unlock(ctl_mtx);
+ if (kctl == 0) {
+ error = ENOENT;
+ break;
+ }
+ ctl_info.ctl_id = kctl->id;
+ bcopy(&ctl_info, data, sizeof (ctl_info));
+ error = 0;
+ break;
+ }
+
+ /* add controls to get list of NKEs */
+
+ }
+
+ return (error);
+}
+
+static void
+kctl_tbl_grow()
+{
+ struct kctl **new_table;
+ uintptr_t new_size;
+
+ lck_mtx_assert(ctl_mtx, LCK_MTX_ASSERT_OWNED);
+
+ if (kctl_tbl_growing) {
+ /* Another thread is allocating */
+ kctl_tbl_growing_waiting++;
+
+ do {
+ (void) msleep((caddr_t) &kctl_tbl_growing, ctl_mtx,
+ PSOCK | PCATCH, "kctl_tbl_growing", 0);
+ } while (kctl_tbl_growing);
+ kctl_tbl_growing_waiting--;
+ }
+ /* Another thread grew the table */
+ if (kctl_table != NULL && kctl_tbl_count < kctl_tbl_size)
+ return;
+
+ /* Verify we have a sane size */
+ if (kctl_tbl_size + KCTL_TBL_INC >= UINT16_MAX) {
+ kctlstat.kcs_tbl_size_too_big++;
+ if (ctl_debug)
+ printf("%s kctl_tbl_size %lu too big\n",
+ __func__, kctl_tbl_size);
+ return;
+ }
+ kctl_tbl_growing = 1;
+
+ new_size = kctl_tbl_size + KCTL_TBL_INC;
+
+ lck_mtx_unlock(ctl_mtx);
+ new_table = _MALLOC(sizeof(struct kctl *) * new_size,
+ M_TEMP, M_WAIT | M_ZERO);
+ lck_mtx_lock(ctl_mtx);
+
+ if (new_table != NULL) {
+ if (kctl_table != NULL) {
+ bcopy(kctl_table, new_table,
+ kctl_tbl_size * sizeof(struct kctl *));
+
+ _FREE(kctl_table, M_TEMP);
+ }
+ kctl_table = new_table;
+ kctl_tbl_size = new_size;
+ }
+
+ kctl_tbl_growing = 0;
+
+ if (kctl_tbl_growing_waiting) {
+ wakeup(&kctl_tbl_growing);
+ }
+}
+
+#define KCTLREF_INDEX_MASK 0x0000FFFF
+#define KCTLREF_GENCNT_MASK 0xFFFF0000
+#define KCTLREF_GENCNT_SHIFT 16
+
+static kern_ctl_ref
+kctl_make_ref(struct kctl *kctl)
+{
+ uintptr_t i;
+
+ lck_mtx_assert(ctl_mtx, LCK_MTX_ASSERT_OWNED);
+
+ if (kctl_tbl_count >= kctl_tbl_size)
+ kctl_tbl_grow();
+
+ kctl->kctlref = NULL;
+ for (i = 0; i < kctl_tbl_size; i++) {
+ if (kctl_table[i] == NULL) {
+ uintptr_t ref;
+
+ /*
+ * Reference is index plus one
+ */
+ kctl_ref_gencnt += 1;
+
+ /*
+ * Add generation count as salt to reference to prevent
+ * use after deregister
+ */
+ ref = ((kctl_ref_gencnt << KCTLREF_GENCNT_SHIFT) &
+ KCTLREF_GENCNT_MASK) +
+ ((i + 1) & KCTLREF_INDEX_MASK);
+
+ kctl->kctlref = (void *)(ref);
+ kctl_table[i] = kctl;
+ kctl_tbl_count++;
+ break;
+ }
+ }
+
+ if (kctl->kctlref == NULL)
+ panic("%s no space in table", __func__);
+
+ if (ctl_debug > 0)
+ printf("%s %p for %p\n",
+ __func__, kctl->kctlref, kctl);
+
+ return (kctl->kctlref);
+}
+
+static void
+kctl_delete_ref(kern_ctl_ref kctlref)
+{
+ /*
+ * Reference is index plus one
+ */
+ uintptr_t i = (((uintptr_t)kctlref) & KCTLREF_INDEX_MASK) - 1;
+
+ lck_mtx_assert(ctl_mtx, LCK_MTX_ASSERT_OWNED);
+
+ if (i < kctl_tbl_size) {
+ struct kctl *kctl = kctl_table[i];
+
+ if (kctl->kctlref == kctlref) {
+ kctl_table[i] = NULL;
+ kctl_tbl_count--;
+ } else {
+ kctlstat.kcs_bad_kctlref++;
+ }
+ } else {
+ kctlstat.kcs_bad_kctlref++;
+ }
+}
+
+static struct kctl *
+kctl_from_ref(kern_ctl_ref kctlref)
+{
+ /*
+ * Reference is index plus one
+ */
+ uintptr_t i = (((uintptr_t)kctlref) & KCTLREF_INDEX_MASK) - 1;
+ struct kctl *kctl = NULL;
+
+ lck_mtx_assert(ctl_mtx, LCK_MTX_ASSERT_OWNED);
+
+ if (i >= kctl_tbl_size) {
+ kctlstat.kcs_bad_kctlref++;
+ return (NULL);
+ }
+ kctl = kctl_table[i];
+ if (kctl->kctlref != kctlref) {
+ kctlstat.kcs_bad_kctlref++;
+ return (NULL);
+ }
+ return (kctl);