]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_control.c
xnu-792.1.5.tar.gz
[apple/xnu.git] / bsd / kern / kern_control.c
CommitLineData
9bccf70c 1/*
91447636 2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
9bccf70c
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
e5568f75
A
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
9bccf70c 11 *
e5568f75
A
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
9bccf70c
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
e5568f75
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
9bccf70c
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
9bccf70c
A
22
23/*
91447636
A
24 * Kernel Control domain - allows control connections to
25 * and to read/write data.
9bccf70c 26 *
91447636 27 * Vincent Lubet, 040506
9bccf70c
A
28 * Christophe Allie, 010928
29 * Justin C. Walker, 990319
30 */
31
32#include <sys/types.h>
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/syslog.h>
36#include <sys/socket.h>
37#include <sys/socketvar.h>
38#include <sys/protosw.h>
39#include <sys/domain.h>
40#include <sys/malloc.h>
41#include <sys/mbuf.h>
9bccf70c
A
42#include <sys/sys_domain.h>
43#include <sys/kern_event.h>
44#include <sys/kern_control.h>
45#include <net/if_var.h>
46
47#include <mach/vm_types.h>
48#include <mach/kmod.h>
49
50#include <kern/thread.h>
51
9bccf70c
A
52/*
53 * Definitions and vars for we support
54 */
55
56#define CTL_SENDSIZE (2 * 1024) /* default buffer size */
57#define CTL_RECVSIZE (8 * 1024) /* default buffer size */
58
59/*
91447636
A
60 * Definitions and vars for we support
61 */
9bccf70c 62
91447636
A
63static u_int32_t ctl_last_id = 0;
64static u_int32_t ctl_max = 256;
65static u_int32_t ctl_maxunit = 65536;
66static lck_grp_attr_t *ctl_lck_grp_attr = 0;
67static lck_attr_t *ctl_lck_attr = 0;
68static lck_grp_t *ctl_lck_grp = 0;
69static lck_mtx_t *ctl_mtx;
70
71/*
72 * internal structure maintained for each register controller
73 */
74
75struct ctl_cb;
76
77struct kctl
78{
79 TAILQ_ENTRY(kctl) next; /* controller chain */
80
81 /* controller information provided when registering */
82 char name[MAX_KCTL_NAME]; /* unique nke identifier, provided by DTS */
83 u_int32_t id;
84 u_int32_t reg_unit;
85
86 /* misc communication information */
87 u_int32_t flags; /* support flags */
88 u_int32_t recvbufsize; /* request more than the default buffer size */
89 u_int32_t sendbufsize; /* request more than the default buffer size */
90
91 /* Dispatch functions */
92 ctl_connect_func connect; /* Make contact */
93 ctl_disconnect_func disconnect; /* Break contact */
94 ctl_send_func send; /* Send data to nke */
95 ctl_setopt_func setopt; /* set kctl configuration */
96 ctl_getopt_func getopt; /* get kctl configuration */
97
98 TAILQ_HEAD(, ctl_cb) kcb_head;
99 u_int32_t lastunit;
9bccf70c
A
100};
101
91447636
A
102struct ctl_cb {
103 TAILQ_ENTRY(ctl_cb) next; /* controller chain */
104 lck_mtx_t *mtx;
105 struct socket *so; /* controlling socket */
106 struct kctl *kctl; /* back pointer to controller */
107 u_int32_t unit;
108 void *userdata;
109};
9bccf70c
A
110
111/* all the controllers are chained */
91447636
A
112TAILQ_HEAD(, kctl) ctl_head;
113
114static int ctl_attach(struct socket *, int, struct proc *);
115static int ctl_detach(struct socket *);
116static int ctl_sofreelastref(struct socket *so);
117static int ctl_connect(struct socket *, struct sockaddr *, struct proc *);
118static int ctl_disconnect(struct socket *);
119static int ctl_ioctl(struct socket *so, u_long cmd, caddr_t data,
9bccf70c 120 struct ifnet *ifp, struct proc *p);
91447636 121static int ctl_send(struct socket *, int, struct mbuf *,
9bccf70c 122 struct sockaddr *, struct mbuf *, struct proc *);
91447636
A
123static int ctl_ctloutput(struct socket *, struct sockopt *);
124static int ctl_peeraddr(struct socket *so, struct sockaddr **nam);
125
126static struct kctl *ctl_find_by_id(u_int32_t);
127static struct kctl *ctl_find_by_name(const char *);
128static struct kctl *ctl_find_by_id_unit(u_int32_t id, u_int32_t unit);
9bccf70c 129
91447636
A
130static struct ctl_cb *kcb_find(struct kctl *, u_int32_t unit);
131static void ctl_post_msg(u_long event_code, u_int32_t id);
9bccf70c 132
91447636
A
133static int ctl_lock(struct socket *, int, int);
134static int ctl_unlock(struct socket *, int, int);
135static lck_mtx_t * ctl_getlock(struct socket *, int);
9bccf70c 136
91447636 137static struct pr_usrreqs ctl_usrreqs =
9bccf70c
A
138{
139 pru_abort_notsupp, pru_accept_notsupp, ctl_attach, pru_bind_notsupp,
91447636
A
140 ctl_connect, pru_connect2_notsupp, ctl_ioctl, ctl_detach,
141 ctl_disconnect, pru_listen_notsupp, ctl_peeraddr,
9bccf70c
A
142 pru_rcvd_notsupp, pru_rcvoob_notsupp, ctl_send,
143 pru_sense_null, pru_shutdown_notsupp, pru_sockaddr_notsupp,
91447636
A
144 sosend, soreceive, pru_sopoll_notsupp
145};
146
147static struct protosw kctlswk_dgram =
148{
149 SOCK_DGRAM, &systemdomain, SYSPROTO_CONTROL,
150 PR_ATOMIC|PR_CONNREQUIRED|PR_PCBLOCK,
151 NULL, NULL, NULL, ctl_ctloutput,
152 NULL, NULL,
153 NULL, NULL, NULL, NULL, &ctl_usrreqs,
154 ctl_lock, ctl_unlock, ctl_getlock, { 0, 0 } , 0, { 0 }
9bccf70c
A
155};
156
91447636 157static struct protosw kctlswk_stream =
9bccf70c 158{
91447636
A
159 SOCK_STREAM, &systemdomain, SYSPROTO_CONTROL,
160 PR_CONNREQUIRED|PR_PCBLOCK,
9bccf70c
A
161 NULL, NULL, NULL, ctl_ctloutput,
162 NULL, NULL,
91447636
A
163 NULL, NULL, NULL, NULL, &ctl_usrreqs,
164 ctl_lock, ctl_unlock, ctl_getlock, { 0, 0 } , 0, { 0 }
9bccf70c
A
165};
166
91447636 167
9bccf70c 168/*
91447636 169 * Install the protosw's for the Kernel Control manager.
9bccf70c 170 */
91447636 171__private_extern__ int
9bccf70c
A
172kern_control_init(void)
173{
91447636
A
174 int error = 0;
175
176 ctl_lck_grp_attr = lck_grp_attr_alloc_init();
177 if (ctl_lck_grp_attr == 0) {
178 printf(": lck_grp_attr_alloc_init failed\n");
179 error = ENOMEM;
180 goto done;
181 }
182 lck_grp_attr_setdefault(ctl_lck_grp_attr);
183
184 ctl_lck_grp = lck_grp_alloc_init("Kernel Control Protocol", ctl_lck_grp_attr);
185 if (ctl_lck_grp == 0) {
186 printf("kern_control_init: lck_grp_alloc_init failed\n");
187 error = ENOMEM;
188 goto done;
189 }
190
191 ctl_lck_attr = lck_attr_alloc_init();
192 if (ctl_lck_attr == 0) {
193 printf("kern_control_init: lck_attr_alloc_init failed\n");
194 error = ENOMEM;
195 goto done;
196 }
197 lck_attr_setdefault(ctl_lck_attr);
198
199 ctl_mtx = lck_mtx_alloc_init(ctl_lck_grp, ctl_lck_attr);
200 if (ctl_mtx == 0) {
201 printf("kern_control_init: lck_mtx_alloc_init failed\n");
202 error = ENOMEM;
203 goto done;
204 }
205 TAILQ_INIT(&ctl_head);
206
207 error = net_add_proto(&kctlswk_dgram, &systemdomain);
208 if (error) {
209 log(LOG_WARNING, "kern_control_init: net_add_proto dgram failed (%d)\n", error);
210 }
211 error = net_add_proto(&kctlswk_stream, &systemdomain);
212 if (error) {
213 log(LOG_WARNING, "kern_control_init: net_add_proto stream failed (%d)\n", error);
214 }
215
216 done:
217 if (error != 0) {
218 if (ctl_mtx) {
219 lck_mtx_free(ctl_mtx, ctl_lck_grp);
220 ctl_mtx = 0;
221 }
222 if (ctl_lck_grp) {
223 lck_grp_free(ctl_lck_grp);
224 ctl_lck_grp = 0;
225 }
226 if (ctl_lck_grp_attr) {
227 lck_grp_attr_free(ctl_lck_grp_attr);
228 ctl_lck_grp_attr = 0;
229 }
230 if (ctl_lck_attr) {
231 lck_attr_free(ctl_lck_attr);
232 ctl_lck_attr = 0;
233 }
234 }
235 return error;
236}
9bccf70c 237
91447636
A
238static void
239kcb_delete(struct ctl_cb *kcb)
240{
241 if (kcb != 0) {
242 if (kcb->mtx != 0)
243 lck_mtx_free(kcb->mtx, ctl_lck_grp);
244 FREE(kcb, M_TEMP);
245 }
9bccf70c
A
246}
247
248
249/*
250 * Kernel Controller user-request functions
91447636
A
251 * attach function must exist and succeed
252 * detach not necessary
253 * we need a pcb for the per socket mutex
9bccf70c 254 */
91447636
A
255static int
256ctl_attach(__unused struct socket *so, __unused int proto, __unused struct proc *p)
9bccf70c 257{
91447636
A
258 int error = 0;
259 struct ctl_cb *kcb = 0;
260
261 MALLOC(kcb, struct ctl_cb *, sizeof(struct ctl_cb), M_TEMP, M_WAITOK);
262 if (kcb == NULL) {
263 error = ENOMEM;
264 goto quit;
265 }
266 bzero(kcb, sizeof(struct ctl_cb));
267
268 kcb->mtx = lck_mtx_alloc_init(ctl_lck_grp, ctl_lck_attr);
269 if (kcb->mtx == NULL) {
270 error = ENOMEM;
271 goto quit;
272 }
273 kcb->so = so;
274 so->so_pcb = (caddr_t)kcb;
275
276quit:
277 if (error != 0) {
278 kcb_delete(kcb);
279 kcb = 0;
280 }
281 return error;
282}
283
284static int
285ctl_sofreelastref(struct socket *so)
286{
287 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
288
289 so->so_pcb = 0;
290
291 if (kcb != 0) {
292 struct kctl *kctl;
293 if ((kctl = kcb->kctl) != 0) {
294 lck_mtx_lock(ctl_mtx);
295 TAILQ_REMOVE(&kctl->kcb_head, kcb, next);
296 lck_mtx_lock(ctl_mtx);
297 }
298 kcb_delete(kcb);
299 }
300 return 0;
301}
302
303static int
304ctl_detach(struct socket *so)
305{
306 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
9bccf70c 307
91447636
A
308 if (kcb == 0)
309 return 0;
310
311 soisdisconnected(so);
312 so->so_flags |= SOF_PCBCLEARING;
9bccf70c
A
313 return 0;
314}
315
91447636
A
316
317static int
318ctl_connect(struct socket *so, struct sockaddr *nam, __unused struct proc *p)
9bccf70c 319{
91447636
A
320 struct kctl *kctl;
321 int error = 0;
322 struct sockaddr_ctl sa;
323 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
324
325 if (kcb == 0)
326 panic("ctl_connect so_pcb null\n");
327
328 if (nam->sa_len != sizeof(struct sockaddr_ctl))
329 return(EINVAL);
330
331 bcopy(nam, &sa, sizeof(struct sockaddr_ctl));
332
333 lck_mtx_lock(ctl_mtx);
334 kctl = ctl_find_by_id_unit(sa.sc_id, sa.sc_unit);
335 if (kctl == NULL) {
336 lck_mtx_unlock(ctl_mtx);
337 return ENOENT;
338 }
9bccf70c 339
91447636
A
340 if (((kctl->flags & CTL_FLAG_REG_SOCK_STREAM) && (so->so_type != SOCK_STREAM)) ||
341 (!(kctl->flags & CTL_FLAG_REG_SOCK_STREAM) && (so->so_type != SOCK_DGRAM))) {
342 lck_mtx_unlock(ctl_mtx);
343 return EPROTOTYPE;
344 }
9bccf70c 345
91447636
A
346 if (kctl->flags & CTL_FLAG_PRIVILEGED) {
347 if (p == 0) {
348 lck_mtx_unlock(ctl_mtx);
55e303ae 349 return(EINVAL);
91447636
A
350 }
351 if ((error = proc_suser(p))) {
352 lck_mtx_unlock(ctl_mtx);
55e303ae 353 return error;
91447636
A
354 }
355 }
356
357 if ((kctl->flags & CTL_FLAG_REG_ID_UNIT) || sa.sc_unit != 0) {
358 if (kcb_find(kctl, sa.sc_unit) != NULL) {
359 lck_mtx_unlock(ctl_mtx);
360 return EBUSY;
361 }
362 } else {
363 u_int32_t unit = kctl->lastunit + 1;
364
365 while (1) {
366 if (unit == ctl_maxunit)
367 unit = 1;
368 if (kcb_find(kctl, unit) == NULL) {
369 kctl->lastunit = sa.sc_unit = unit;
370 break;
371 }
372 if (unit++ == kctl->lastunit) {
373 lck_mtx_unlock(ctl_mtx);
374 return EBUSY;
375 }
376 }
55e303ae
A
377 }
378
91447636
A
379 kcb->unit = sa.sc_unit;
380 kcb->kctl = kctl;
381 TAILQ_INSERT_TAIL(&kctl->kcb_head, kcb, next);
382 lck_mtx_unlock(ctl_mtx);
9bccf70c 383
91447636 384 error = soreserve(so, kctl->sendbufsize, kctl->recvbufsize);
9bccf70c 385 if (error)
91447636
A
386 goto done;
387 soisconnecting(so);
9bccf70c 388
91447636
A
389 socket_unlock(so, 0);
390 error = (*kctl->connect)(kctl, &sa, &kcb->userdata);
391 socket_lock(so, 0);
392 if (error)
393 goto done;
9bccf70c 394
91447636
A
395 soisconnected(so);
396
397done:
9bccf70c 398 if (error) {
91447636
A
399 soisdisconnected(so);
400 lck_mtx_lock(ctl_mtx);
401 kcb->kctl = 0;
402 kcb->unit = 0;
403 TAILQ_REMOVE(&kctl->kcb_head, kcb, next);
404 lck_mtx_unlock(ctl_mtx);
9bccf70c 405 }
9bccf70c
A
406 return error;
407}
408
91447636 409static int
9bccf70c
A
410ctl_disconnect(struct socket *so)
411{
91447636
A
412 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
413
414 if ((kcb = (struct ctl_cb *)so->so_pcb)) {
415 struct kctl *kctl = kcb->kctl;
416
417 if (kctl && kctl->disconnect) {
418 socket_unlock(so, 0);
419 (*kctl->disconnect)(kctl, kcb->unit, kcb->userdata);
420 socket_lock(so, 0);
421 }
422 lck_mtx_lock(ctl_mtx);
423 kcb->kctl = 0;
424 kcb->unit = 0;
425 TAILQ_REMOVE(&kctl->kcb_head, kcb, next);
9bccf70c 426 soisdisconnected(so);
91447636 427 lck_mtx_unlock(ctl_mtx);
9bccf70c
A
428 }
429 return 0;
430}
431
91447636
A
432static int
433ctl_peeraddr(struct socket *so, struct sockaddr **nam)
9bccf70c 434{
91447636
A
435 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
436 struct kctl *kctl;
437 struct sockaddr_ctl sc;
438
439 if (kcb == NULL) /* sanity check */
440 return(ENOTCONN);
441
442 if ((kctl = kcb->kctl) == NULL)
443 return(EINVAL);
444
445 bzero(&sc, sizeof(struct sockaddr_ctl));
446 sc.sc_len = sizeof(struct sockaddr_ctl);
447 sc.sc_family = AF_SYSTEM;
448 sc.ss_sysaddr = AF_SYS_CONTROL;
449 sc.sc_id = kctl->id;
450 sc.sc_unit = kcb->unit;
451
452 *nam = dup_sockaddr((struct sockaddr *)&sc, 1);
453
454 return 0;
9bccf70c
A
455}
456
91447636
A
457static int
458ctl_send(struct socket *so, int flags, struct mbuf *m,
459 __unused struct sockaddr *addr, __unused struct mbuf *control,
460 __unused struct proc *p)
9bccf70c 461{
91447636
A
462 int error = 0;
463 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
464 struct kctl *kctl;
465
466 if (kcb == NULL) /* sanity check */
467 return(ENOTCONN);
468
469 if ((kctl = kcb->kctl) == NULL)
470 return(EINVAL);
471
472 if (kctl->send) {
473 socket_unlock(so, 0);
474 error = (*kctl->send)(kctl, kcb->unit, kcb->userdata, m, flags);
475 socket_lock(so, 0);
476 }
477 return error;
9bccf70c
A
478}
479
91447636
A
480errno_t
481ctl_enqueuembuf(void *kctlref, u_int32_t unit, struct mbuf *m, u_int32_t flags)
9bccf70c 482{
91447636
A
483 struct ctl_cb *kcb;
484 struct socket *so;
485 errno_t error = 0;
486 struct kctl *kctl = (struct kctl *)kctlref;
487
488 if (kctl == NULL)
489 return EINVAL;
490
491 kcb = kcb_find(kctl, unit);
492 if (kcb == NULL)
493 return EINVAL;
494
495 so = (struct socket *)kcb->so;
496 if (so == NULL)
497 return EINVAL;
498
499 socket_lock(so, 1);
500 if (sbspace(&so->so_rcv) < m->m_pkthdr.len) {
501 error = ENOBUFS;
502 goto bye;
503 }
504 if ((flags & CTL_DATA_EOR))
505 m->m_flags |= M_EOR;
506 if (sbappend(&so->so_rcv, m) && (flags & CTL_DATA_NOWAKEUP) == 0)
507 sorwakeup(so);
508bye:
509 socket_unlock(so, 1);
510 return error;
511}
9bccf70c 512
91447636
A
513errno_t
514ctl_enqueuedata(void *kctlref, u_int32_t unit, void *data, size_t len, u_int32_t flags)
515{
516 struct ctl_cb *kcb;
517 struct socket *so;
518 struct mbuf *m;
519 errno_t error = 0;
520 struct kctl *kctl = (struct kctl *)kctlref;
521 unsigned int num_needed;
522 struct mbuf *n;
523 size_t curlen = 0;
524
525 if (kctlref == NULL)
526 return EINVAL;
527
528 kcb = kcb_find(kctl, unit);
529 if (kcb == NULL)
530 return EINVAL;
531
532 so = (struct socket *)kcb->so;
533 if (so == NULL)
534 return EINVAL;
535
536 socket_lock(so, 1);
537 if ((size_t)sbspace(&so->so_rcv) < len) {
538 error = ENOBUFS;
539 goto bye;
540 }
541
542 num_needed = 1;
543 m = m_allocpacket_internal(&num_needed, len, NULL, M_NOWAIT, 1, 0);
544 if (m == NULL) {
545 printf("ctl_enqueuedata: m_allocpacket_internal(%lu) failed\n", len);
546 error = ENOBUFS;
547 goto bye;
548 }
549
550 for (n = m; n != NULL; n = n->m_next) {
551 size_t mlen = mbuf_maxlen(n);
552
553 if (mlen + curlen > len)
554 mlen = len - curlen;
555 n->m_len = mlen;
556 bcopy((char *)data + curlen, n->m_data, mlen);
557 curlen += mlen;
558 }
559 mbuf_pkthdr_setlen(m, curlen);
560
561 if ((flags & CTL_DATA_EOR))
562 m->m_flags |= M_EOR;
563 if (sbappend(&so->so_rcv, m) && (flags & CTL_DATA_NOWAKEUP) == 0)
564 sorwakeup(so);
565bye:
566 socket_unlock(so, 1);
567 return error;
568}
9bccf70c 569
55e303ae 570
91447636
A
571errno_t
572ctl_getenqueuespace(kern_ctl_ref kctlref, u_int32_t unit, size_t *space)
573{
574 struct ctl_cb *kcb;
575 struct kctl *kctl = (struct kctl *)kctlref;
576 struct socket *so;
577
578 if (kctlref == NULL || space == NULL)
579 return EINVAL;
580
581 kcb = kcb_find(kctl, unit);
582 if (kcb == NULL)
583 return EINVAL;
584
585 so = (struct socket *)kcb->so;
586 if (so == NULL)
587 return EINVAL;
588
589 socket_lock(so, 1);
590 *space = sbspace(&so->so_rcv);
591 socket_unlock(so, 1);
592
593 return 0;
9bccf70c
A
594}
595
91447636 596static int
9bccf70c
A
597ctl_ctloutput(struct socket *so, struct sockopt *sopt)
598{
91447636
A
599 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
600 struct kctl *kctl;
601 int error = 0;
602 void *data;
603 size_t len;
604
605 if (sopt->sopt_level != SYSPROTO_CONTROL) {
606 return(EINVAL);
607 }
608
609 if (kcb == NULL) /* sanity check */
610 return(ENOTCONN);
611
612 if ((kctl = kcb->kctl) == NULL)
613 return(EINVAL);
614
615 switch (sopt->sopt_dir) {
616 case SOPT_SET:
617 if (kctl->setopt == NULL)
618 return(ENOTSUP);
619 MALLOC(data, void *, sopt->sopt_valsize, M_TEMP, M_WAITOK);
620 if (data == NULL)
621 return(ENOMEM);
622 error = sooptcopyin(sopt, data, sopt->sopt_valsize, sopt->sopt_valsize);
623 if (error == 0) {
624 socket_unlock(so, 0);
625 error = (*kctl->setopt)(kcb->kctl, kcb->unit, kcb->userdata, sopt->sopt_name,
626 data, sopt->sopt_valsize);
627 socket_lock(so, 0);
628 }
629 FREE(data, M_TEMP);
630 break;
631
632 case SOPT_GET:
633 if (kctl->getopt == NULL)
634 return(ENOTSUP);
635 data = NULL;
636 if (sopt->sopt_valsize && sopt->sopt_val) {
637 MALLOC(data, void *, sopt->sopt_valsize, M_TEMP, M_WAITOK);
638 if (data == NULL)
639 return(ENOMEM);
640 }
641 len = sopt->sopt_valsize;
642 socket_unlock(so, 0);
643 error = (*kctl->getopt)(kcb->kctl, kcb->unit, kcb->userdata, sopt->sopt_name,
644 data, &len);
645 socket_lock(so, 0);
646 if (error == 0) {
647 if (data != NULL)
648 error = sooptcopyout(sopt, data, len);
649 else
650 sopt->sopt_valsize = len;
651 }
652 if (data != NULL)
653 FREE(data, M_TEMP);
654 break;
655 }
656 return error;
657}
9bccf70c 658
91447636
A
659static int
660ctl_ioctl(__unused struct socket *so, u_long cmd, caddr_t data,
661 __unused struct ifnet *ifp, __unused struct proc *p)
662{
663 int error = ENOTSUP;
664
665 switch (cmd) {
666 /* get the number of controllers */
667 case CTLIOCGCOUNT: {
668 struct kctl *kctl;
669 int n = 0;
670
671 lck_mtx_lock(ctl_mtx);
672 TAILQ_FOREACH(kctl, &ctl_head, next)
673 n++;
674 lck_mtx_unlock(ctl_mtx);
675
676 *(u_int32_t *)data = n;
677 error = 0;
678 break;
679 }
680 case CTLIOCGINFO: {
681 struct ctl_info *ctl_info = (struct ctl_info *)data;
682 struct kctl *kctl = 0;
683 size_t name_len = strlen(ctl_info->ctl_name);
684
685 if (name_len == 0 || name_len + 1 > MAX_KCTL_NAME) {
686 error = EINVAL;
687 break;
688 }
689 lck_mtx_lock(ctl_mtx);
690 kctl = ctl_find_by_name(ctl_info->ctl_name);
691 lck_mtx_unlock(ctl_mtx);
692 if (kctl == 0) {
693 error = ENOENT;
694 break;
695 }
696 ctl_info->ctl_id = kctl->id;
697 error = 0;
698 break;
699 }
700
701 /* add controls to get list of NKEs */
702
703 }
704
705 return error;
706}
9bccf70c 707
91447636
A
708/*
709 * Register/unregister a NKE
710 */
711errno_t
712ctl_register(struct kern_ctl_reg *userkctl, kern_ctl_ref *kctlref)
713{
714 struct kctl *kctl = 0;
715 u_int32_t id = -1;
716 u_int32_t n;
717 size_t name_len;
718
719 if (userkctl == NULL) /* sanity check */
720 return(EINVAL);
721 if (userkctl->ctl_connect == NULL)
722 return(EINVAL);
723 name_len = strlen(userkctl->ctl_name);
724 if (name_len == 0 || name_len + 1 > MAX_KCTL_NAME)
725 return(EINVAL);
726
727 MALLOC(kctl, struct kctl *, sizeof(*kctl), M_TEMP, M_WAITOK);
728 if (kctl == NULL)
729 return(ENOMEM);
730 bzero((char *)kctl, sizeof(*kctl));
731
732 lck_mtx_lock(ctl_mtx);
733
734 if ((userkctl->ctl_flags & CTL_FLAG_REG_ID_UNIT) == 0) {
735 if (ctl_find_by_name(userkctl->ctl_name) != NULL) {
736 lck_mtx_unlock(ctl_mtx);
737 FREE(kctl, M_TEMP);
738 return(EEXIST);
739 }
740 for (n = 0, id = ctl_last_id + 1; n < ctl_max; id++, n++) {
741 if (id == 0) {
742 n--;
743 continue;
744 }
745 if (ctl_find_by_id(id) == 0)
746 break;
747 }
748 if (id == ctl_max) {
749 lck_mtx_unlock(ctl_mtx);
750 FREE(kctl, M_TEMP);
751 return(ENOBUFS);
752 }
753 userkctl->ctl_id =id;
754 kctl->id = id;
755 kctl->reg_unit = -1;
756 } else {
757 if (ctl_find_by_id_unit(userkctl->ctl_id, userkctl->ctl_unit) != NULL) {
758 lck_mtx_unlock(ctl_mtx);
759 FREE(kctl, M_TEMP);
760 return(EEXIST);
761 }
762 kctl->id = userkctl->ctl_id;
763 kctl->reg_unit = userkctl->ctl_unit;
764 }
765 strcpy(kctl->name, userkctl->ctl_name);
766 kctl->flags = userkctl->ctl_flags;
767
768 /* Let the caller know the default send and receive sizes */
769 if (userkctl->ctl_sendsize == 0)
770 userkctl->ctl_sendsize = CTL_SENDSIZE;
771 kctl->sendbufsize = userkctl->ctl_sendsize;
772
773 if (kctl->recvbufsize == 0)
774 userkctl->ctl_recvsize = CTL_RECVSIZE;
775 kctl->recvbufsize = userkctl->ctl_recvsize;
776
777 kctl->connect = userkctl->ctl_connect;
778 kctl->disconnect = userkctl->ctl_disconnect;
779 kctl->send = userkctl->ctl_send;
780 kctl->setopt = userkctl->ctl_setopt;
781 kctl->getopt = userkctl->ctl_getopt;
782
783 TAILQ_INIT(&kctl->kcb_head);
784
785 TAILQ_INSERT_TAIL(&ctl_head, kctl, next);
786 ctl_max++;
787
788 lck_mtx_unlock(ctl_mtx);
789
790 *kctlref = kctl;
791
792 ctl_post_msg(KEV_CTL_REGISTERED, kctl->id);
793 return(0);
9bccf70c
A
794}
795
91447636
A
796errno_t
797ctl_deregister(void *kctlref)
798{
799 struct kctl *kctl;
9bccf70c 800
91447636
A
801 if (kctlref == NULL) /* sanity check */
802 return(EINVAL);
9bccf70c 803
91447636
A
804 lck_mtx_lock(ctl_mtx);
805 TAILQ_FOREACH(kctl, &ctl_head, next) {
806 if (kctl == (struct kctl *)kctlref)
807 break;
9bccf70c 808 }
91447636
A
809 if (kctl != (struct kctl *)kctlref) {
810 lck_mtx_unlock(ctl_mtx);
811 return EINVAL;
812 }
813 if (!TAILQ_EMPTY(&kctl->kcb_head)) {
814 lck_mtx_unlock(ctl_mtx);
815 return EBUSY;
816 }
817
818 TAILQ_REMOVE(&ctl_head, kctl, next);
819 ctl_max--;
820
821 lck_mtx_unlock(ctl_mtx);
9bccf70c 822
91447636
A
823 ctl_post_msg(KEV_CTL_DEREGISTERED, kctl->id);
824 FREE(kctl, M_TEMP);
825 return(0);
9bccf70c
A
826}
827
828/*
91447636 829 * Must be called with global lock taked
9bccf70c 830 */
91447636
A
831static struct kctl *
832ctl_find_by_id(u_int32_t id)
9bccf70c 833{
91447636 834 struct kctl *kctl;
9bccf70c 835
91447636
A
836 TAILQ_FOREACH(kctl, &ctl_head, next)
837 if (kctl->id == id)
838 return kctl;
9bccf70c 839
91447636 840 return NULL;
9bccf70c
A
841}
842
91447636
A
843/*
844 * Must be called with global ctl_mtx lock taked
845 */
846static struct kctl *
847ctl_find_by_name(const char *name)
9bccf70c 848{
91447636 849 struct kctl *kctl;
9bccf70c 850
91447636
A
851 TAILQ_FOREACH(kctl, &ctl_head, next)
852 if (strcmp(kctl->name, name) == 0)
853 return kctl;
9bccf70c 854
91447636
A
855 return NULL;
856}
9bccf70c 857
91447636
A
858/*
859 * Must be called with global ctl_mtx lock taked
860 *
861 */
862static struct kctl *
863ctl_find_by_id_unit(u_int32_t id, u_int32_t unit)
864{
865 struct kctl *kctl;
866
867 TAILQ_FOREACH(kctl, &ctl_head, next) {
868 if (kctl->id == id && (kctl->flags & CTL_FLAG_REG_ID_UNIT) == 0)
869 return kctl;
870 else if (kctl->id == id && kctl->reg_unit == unit)
871 return kctl;
9bccf70c 872 }
91447636 873 return NULL;
9bccf70c
A
874}
875
876/*
91447636 877 * Must be called with kernel controller lock taken
9bccf70c 878 */
91447636
A
879static struct ctl_cb *
880kcb_find(struct kctl *kctl, u_int32_t unit)
9bccf70c 881{
91447636 882 struct ctl_cb *kcb;
9bccf70c 883
91447636
A
884 TAILQ_FOREACH(kcb, &kctl->kcb_head, next)
885 if ((kcb->unit == unit))
886 return kcb;
9bccf70c
A
887
888 return NULL;
889}
890
91447636
A
891/*
892 * Must be called witout lock
893 */
894static void
895ctl_post_msg(u_long event_code, u_int32_t id)
9bccf70c
A
896{
897 struct ctl_event_data ctl_ev_data;
898 struct kev_msg ev_msg;
899
900 ev_msg.vendor_code = KEV_VENDOR_APPLE;
901
902 ev_msg.kev_class = KEV_SYSTEM_CLASS;
903 ev_msg.kev_subclass = KEV_CTL_SUBCLASS;
904 ev_msg.event_code = event_code;
905
906 /* common nke subclass data */
907 bzero(&ctl_ev_data, sizeof(ctl_ev_data));
908 ctl_ev_data.ctl_id = id;
9bccf70c
A
909 ev_msg.dv[0].data_ptr = &ctl_ev_data;
910 ev_msg.dv[0].data_length = sizeof(ctl_ev_data);
911
912 ev_msg.dv[1].data_length = 0;
913
914 kev_post_msg(&ev_msg);
915}
916
91447636
A
917static int
918ctl_lock(struct socket *so, int refcount, int lr)
919 {
920 int lr_saved;
921#ifdef __ppc__
922 if (lr == 0) {
923 __asm__ volatile("mflr %0" : "=r" (lr_saved));
924 }
925 else lr_saved = lr;
926#endif
927
928 if (so->so_pcb) {
929 lck_mtx_lock(((struct ctl_cb *)so->so_pcb)->mtx);
930 } else {
931 panic("ctl_lock: so=%x NO PCB! lr=%x\n", so, lr_saved);
932 lck_mtx_lock(so->so_proto->pr_domain->dom_mtx);
933 }
934
935 if (so->so_usecount < 0)
936 panic("ctl_lock: so=%x so_pcb=%x lr=%x ref=%x\n",
937 so, so->so_pcb, lr_saved, so->so_usecount);
938
939 if (refcount)
940 so->so_usecount++;
941 so->reserved3 = (void *)lr_saved;
942 return (0);
943}
944
945static int
946ctl_unlock(struct socket *so, int refcount, int lr)
947{
948 int lr_saved;
949 lck_mtx_t * mutex_held;
950
951#ifdef __ppc__
952 if (lr == 0) {
953 __asm__ volatile("mflr %0" : "=r" (lr_saved));
954 }
955 else lr_saved = lr;
956#endif
957
958#ifdef MORE_KCTLLOCK_DEBUG
959 printf("ctl_unlock: so=%x sopcb=%x lock=%x ref=%x lr=%x\n",
960 so, so->so_pcb, ((struct ctl_cb *)so->so_pcb)->mtx, so->so_usecount, lr_saved);
961#endif
962 if (refcount)
963 so->so_usecount--;
964
965 if (so->so_usecount < 0)
966 panic("ctl_unlock: so=%x usecount=%x\n", so, so->so_usecount);
967 if (so->so_pcb == NULL) {
968 panic("ctl_unlock: so=%x NO PCB usecount=%x lr=%x\n", so, so->so_usecount, lr_saved);
969 mutex_held = so->so_proto->pr_domain->dom_mtx;
970 } else {
971 mutex_held = ((struct ctl_cb *)so->so_pcb)->mtx;
972 }
973 lck_mtx_assert(mutex_held, LCK_MTX_ASSERT_OWNED);
974 lck_mtx_unlock(mutex_held);
975 so->reserved4 = (void *)lr_saved;
976
977 if (so->so_usecount == 0)
978 ctl_sofreelastref(so);
979
980 return (0);
981}
982
983static lck_mtx_t *
984ctl_getlock(struct socket *so, __unused int locktype)
985{
986 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
987
988 if (so->so_pcb) {
989 if (so->so_usecount < 0)
990 panic("ctl_getlock: so=%x usecount=%x\n", so, so->so_usecount);
991 return(kcb->mtx);
992 } else {
993 panic("ctl_getlock: so=%x NULL so_pcb\n", so);
994 return (so->so_proto->pr_domain->dom_mtx);
995 }
996}