1 /*
2 * Copyright (c) 2000-2024 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29
30 #include <kern/kern_types.h>
31 #include <kern/thread_group.h>
32 #include <mach/mach_types.h>
33 #include <mach/boolean.h>
34
35 #include <kern/coalition.h>
36
37 #include <sys/coalition.h>
38 #include <sys/coalition_private.h>
39 #include <sys/errno.h>
40 #include <sys/kauth.h>
41 #include <sys/kernel.h>
42 #include <sys/sysproto.h>
43 #include <sys/systm.h>
44 #include <sys/ubc.h> /* mach_to_bsd_errno */
45
46 #include <kern/policy_internal.h>
47
48 #include <IOKit/IOBSD.h> /* IOTaskHasEntitlement */
49
50 /* Coalitions syscalls */
51
52 /*
53 * Create a new, empty coalition and return its ID.
54 *
55 * Returns:
56 * EINVAL Flags parameter was invalid
57 * ENOMEM Unable to allocate kernel resources for a new coalition
58 * EFAULT cidp parameter pointed to invalid memory.
59 *
60 * Returns with reference held for userspace caller.
61 */
62 static
63 int
coalition_create_syscall(user_addr_t cidp,uint32_t flags)64 coalition_create_syscall(user_addr_t cidp, uint32_t flags)
65 {
66 int error = 0;
67 kern_return_t kr;
68 uint64_t cid;
69 coalition_t coal;
70 int type = COALITION_CREATE_FLAGS_GET_TYPE(flags);
71 int role = COALITION_CREATE_FLAGS_GET_ROLE(flags);
72 boolean_t privileged = !!(flags & COALITION_CREATE_FLAGS_PRIVILEGED);
73 boolean_t efficient = !!(flags & COALITION_CREATE_FLAGS_EFFICIENT);
74
75 if ((flags & (~COALITION_CREATE_FLAGS_MASK)) != 0) {
76 return EINVAL;
77 }
78 if (type < 0 || type > COALITION_TYPE_MAX) {
79 return EINVAL;
80 }
81
82 kr = coalition_create_internal(type, role, privileged, efficient, &coal, &cid);
83 if (kr != KERN_SUCCESS) {
84 /* for now, the only kr is KERN_RESOURCE_SHORTAGE */
85 error = ENOMEM;
86 goto out;
87 }
88
89 coal_dbg("(addr, %u) -> %llu", flags, cid);
90 error = copyout(&cid, cidp, sizeof(cid));
91 out:
92 return error;
93 }
94
95 /*
96 * Request to terminate the coalition identified by ID.
97 * Attempts to spawn into this coalition using the posix_spawnattr will begin
98 * failing. Processes already within the coalition may still fork.
99 * Arms the 'coalition is empty' notification when the coalition's active
100 * count reaches zero.
101 *
102 * Returns:
103 * ESRCH No coalition with that ID could be found.
104 * EALREADY The coalition with that ID has already been terminated.
105 * EFAULT cidp parameter pointed to invalid memory.
106 * EPERM Caller doesn't have permission to terminate that coalition.
107 */
108 static
109 int
coalition_request_terminate_syscall(user_addr_t cidp,uint32_t flags)110 coalition_request_terminate_syscall(user_addr_t cidp, uint32_t flags)
111 {
112 kern_return_t kr;
113 int error = 0;
114 uint64_t cid;
115 coalition_t coal;
116
117 if (flags != 0) {
118 return EINVAL;
119 }
120
121 error = copyin(cidp, &cid, sizeof(cid));
122 if (error) {
123 return error;
124 }
125
126 coal = coalition_find_by_id(cid);
127 if (coal == COALITION_NULL) {
128 return ESRCH;
129 }
130
131 kr = coalition_request_terminate_internal(coal);
132 coalition_release(coal);
133
134 switch (kr) {
135 case KERN_SUCCESS:
136 break;
137 case KERN_DEFAULT_SET:
138 error = EPERM;
139 break;
140 case KERN_TERMINATED:
141 error = EALREADY;
142 break;
143 case KERN_INVALID_NAME:
144 error = ESRCH;
145 break;
146 default:
147 error = EIO;
148 break;
149 }
150
151 coal_dbg("(%llu, %u) -> %d", cid, flags, error);
152
153 return error;
154 }
155
156 /*
157 * Request the kernel to deallocate the coalition identified by ID, which
158 * must be both terminated and empty. This balances the reference taken
159 * in coalition_create.
160 * The memory containing the coalition object may not be freed just yet, if
161 * other kernel operations still hold references to it.
162 *
163 * Returns:
164 * EINVAL Flags parameter was invalid
165 * ESRCH Coalition ID refers to a coalition that doesn't exist.
166 * EBUSY Coalition has not yet been terminated.
167 * EBUSY Coalition is still active.
168 * EFAULT cidp parameter pointed to invalid memory.
169 * EPERM Caller doesn't have permission to terminate that coalition.
170 * Consumes one reference, "held" by caller since coalition_create
171 */
172 static
173 int
coalition_reap_syscall(user_addr_t cidp,uint32_t flags)174 coalition_reap_syscall(user_addr_t cidp, uint32_t flags)
175 {
176 kern_return_t kr;
177 int error = 0;
178 uint64_t cid;
179 coalition_t coal;
180
181 if (flags != 0) {
182 return EINVAL;
183 }
184
185 error = copyin(cidp, &cid, sizeof(cid));
186 if (error) {
187 return error;
188 }
189
190 coal = coalition_find_by_id(cid);
191 if (coal == COALITION_NULL) {
192 return ESRCH;
193 }
194
195 kr = coalition_reap_internal(coal);
196 coalition_release(coal);
197
198 switch (kr) {
199 case KERN_SUCCESS:
200 break;
201 case KERN_DEFAULT_SET:
202 error = EPERM;
203 break;
204 case KERN_TERMINATED:
205 error = ESRCH;
206 break;
207 case KERN_FAILURE:
208 error = EBUSY;
209 break;
210 default:
211 error = EIO;
212 break;
213 }
214
215 coal_dbg("(%llu, %u) -> %d", cid, flags, error);
216
217 return error;
218 }
219
220 /* Syscall demux.
221 * Returns EPERM if the calling process is not privileged to make this call.
222 */
223 int
coalition(proc_t p,struct coalition_args * cap,__unused int32_t * retval)224 coalition(proc_t p, struct coalition_args *cap, __unused int32_t *retval)
225 {
226 uint32_t operation = cap->operation;
227 user_addr_t cidp = cap->cid;
228 uint32_t flags = cap->flags;
229 int error = 0;
230 int type = COALITION_CREATE_FLAGS_GET_TYPE(flags);
231
232 if (!task_is_in_privileged_coalition(proc_task(p), type)) {
233 return EPERM;
234 }
235
236 switch (operation) {
237 case COALITION_OP_CREATE:
238 error = coalition_create_syscall(cidp, flags);
239 break;
240 case COALITION_OP_REAP:
241 error = coalition_reap_syscall(cidp, flags);
242 break;
243 case COALITION_OP_TERMINATE:
244 error = coalition_request_terminate_syscall(cidp, flags);
245 break;
246 default:
247 error = ENOSYS;
248 }
249 return error;
250 }
251
252 /* This is a temporary interface, likely to be changed by 15385642. */
253 static int __attribute__ ((noinline))
coalition_info_resource_usage(coalition_t coal,user_addr_t buffer,user_size_t bufsize)254 coalition_info_resource_usage(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
255 {
256 kern_return_t kr;
257 struct coalition_resource_usage cru = {};
258
259 kr = coalition_resource_usage_internal(coal, &cru);
260
261 switch (kr) {
262 case KERN_INVALID_ARGUMENT:
263 return EINVAL;
264 case KERN_RESOURCE_SHORTAGE:
265 return ENOMEM;
266 case KERN_SUCCESS:
267 break;
268 default:
269 return EIO; /* shrug */
270 }
271
272 return copyout(&cru, buffer, MIN(bufsize, sizeof(cru)));
273 }
274
275 #if DEVELOPMENT || DEBUG
276 static int __attribute__ ((noinline))
coalition_info_get_debug_info(coalition_t coal,user_addr_t buffer,user_size_t bufsize)277 coalition_info_get_debug_info(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
278 {
279 kern_return_t kr;
280 struct coalinfo_debuginfo c_debuginfo = {};
281
282 kr = coalition_debug_info_internal(coal, &c_debuginfo);
283
284 if (kr != KERN_SUCCESS) {
285 return mach_to_bsd_errno(kr);
286 }
287
288 return copyout(&c_debuginfo, buffer, MIN(bufsize, sizeof(c_debuginfo)));
289 }
290 #endif /* DEVELOPMENT || DEBUG */
291
292 #if CONFIG_THREAD_GROUPS
293 static int
coalition_info_set_name_internal(coalition_t coal,user_addr_t buffer,user_size_t bufsize)294 coalition_info_set_name_internal(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
295 {
296 int error;
297 char name[THREAD_GROUP_MAXNAME];
298
299 if (coalition_type(coal) != COALITION_TYPE_JETSAM) {
300 return EINVAL;
301 }
302 bzero(name, sizeof(name));
303 error = copyin(buffer, name, MIN(bufsize, sizeof(name) - 1));
304 if (error) {
305 return error;
306 }
307 struct thread_group *tg = coalition_get_thread_group(coal);
308 thread_group_set_name(tg, name);
309 return error;
310 }
311
312 #else /* CONFIG_THREAD_GROUPS */
313 #define coalition_info_set_name_internal(...) 0
314 #endif /* CONFIG_THREAD_GROUPS */
315
316 static int
coalition_info_efficiency(coalition_t coal,user_addr_t buffer,user_size_t bufsize)317 coalition_info_efficiency(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
318 {
319 int error = 0;
320 if (coalition_type(coal) != COALITION_TYPE_JETSAM) {
321 return EINVAL;
322 }
323 uint64_t flags = 0;
324 error = copyin(buffer, &flags, MIN(bufsize, sizeof(flags)));
325 if (error) {
326 return error;
327 }
328 if ((flags & COALITION_EFFICIENCY_VALID_FLAGS) == 0) {
329 return EINVAL;
330 }
331 if (flags & COALITION_FLAGS_EFFICIENT) {
332 // No longer supported; this flag must be set during create.
333 return ENOTSUP;
334 }
335 return error;
336 }
337
338 static int
coalition_ledger_logical_writes_limit(coalition_t coal,user_addr_t buffer,user_size_t bufsize)339 coalition_ledger_logical_writes_limit(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
340 {
341 int error = 0;
342 int64_t limit = 0;
343
344 if (coalition_type(coal) != COALITION_TYPE_RESOURCE) {
345 error = EINVAL;
346 goto out;
347 }
348 error = copyin(buffer, &limit, MIN(bufsize, sizeof(limit)));
349 if (error) {
350 goto out;
351 }
352
353
354 error = coalition_ledger_set_logical_writes_limit(coal, limit);
355 out:
356 return error;
357 }
358
359 int
coalition_info(proc_t p,struct coalition_info_args * uap,__unused int32_t * retval)360 coalition_info(proc_t p, struct coalition_info_args *uap, __unused int32_t *retval)
361 {
362 user_addr_t cidp = uap->cid;
363 user_addr_t buffer = uap->buffer;
364 user_addr_t bufsizep = uap->bufsize;
365 user_size_t bufsize;
366 uint32_t flavor = uap->flavor;
367 int error;
368 uint64_t cid;
369 coalition_t coal;
370
371 error = copyin(cidp, &cid, sizeof(cid));
372 if (error) {
373 return error;
374 }
375
376 coal = coalition_find_by_id(cid);
377 if (coal == COALITION_NULL) {
378 return ESRCH;
379 }
380 /* TODO: priv check? EPERM or ESRCH? */
381
382 if (IS_64BIT_PROCESS(p)) {
383 user64_size_t size64;
384 error = copyin(bufsizep, &size64, sizeof(size64));
385 bufsize = (user_size_t)size64;
386 } else {
387 user32_size_t size32;
388 error = copyin(bufsizep, &size32, sizeof(size32));
389 bufsize = (user_size_t)size32;
390 }
391 if (error) {
392 goto bad;
393 }
394
395 switch (flavor) {
396 case COALITION_INFO_RESOURCE_USAGE:
397 error = coalition_info_resource_usage(coal, buffer, bufsize);
398 break;
399 case COALITION_INFO_SET_NAME:
400 error = coalition_info_set_name_internal(coal, buffer, bufsize);
401 break;
402 case COALITION_INFO_SET_EFFICIENCY:
403 error = coalition_info_efficiency(coal, buffer, bufsize);
404 break;
405 #if DEVELOPMENT || DEBUG
406 case COALITION_INFO_GET_DEBUG_INFO:
407 error = coalition_info_get_debug_info(coal, buffer, bufsize);
408 break;
409 #endif /* DEVELOPMENT || DEBUG */
410 default:
411 error = EINVAL;
412 }
413
414 bad:
415 coalition_release(coal);
416 return error;
417 }
418
419 int
coalition_ledger(__unused proc_t p,__unused struct coalition_ledger_args * uap,__unused int32_t * retval)420 coalition_ledger(__unused proc_t p, __unused struct coalition_ledger_args *uap, __unused int32_t *retval)
421 {
422 user_addr_t cidp = uap->cid;
423 user_addr_t buffer = uap->buffer;
424 user_addr_t bufsizep = uap->bufsize;
425 user_size_t bufsize;
426 uint32_t operation = uap->operation;
427 int error;
428 uint64_t cid;
429 coalition_t coal = COALITION_NULL;
430
431 if (!kauth_cred_issuser(kauth_cred_get())) {
432 error = EPERM;
433 goto out;
434 }
435
436 error = copyin(cidp, &cid, sizeof(cid));
437 if (error) {
438 goto out;
439 }
440
441 coal = coalition_find_by_id(cid);
442 if (coal == COALITION_NULL) {
443 error = ESRCH;
444 goto out;
445 }
446
447 if (IS_64BIT_PROCESS(p)) {
448 user64_size_t size64;
449 error = copyin(bufsizep, &size64, sizeof(size64));
450 bufsize = (user_size_t)size64;
451 } else {
452 user32_size_t size32;
453 error = copyin(bufsizep, &size32, sizeof(size32));
454 bufsize = (user_size_t)size32;
455 }
456 if (error) {
457 goto out;
458 }
459
460 switch (operation) {
461 case COALITION_LEDGER_SET_LOGICAL_WRITES_LIMIT:
462 error = coalition_ledger_logical_writes_limit(coal, buffer, bufsize);
463 break;
464 default:
465 error = EINVAL;
466 }
467 out:
468 if (coal != COALITION_NULL) {
469 coalition_release(coal);
470 }
471 return error;
472 }
473
474 static int
coalition_policy_set_suppress(coalition_t coal,coalition_policy_suppress_t value)475 coalition_policy_set_suppress(coalition_t coal, coalition_policy_suppress_t value)
476 {
477 int error = 0;
478
479 kern_return_t kr;
480
481 switch (value) {
482 case COALITION_POLICY_SUPPRESS_NONE:
483 kr = jetsam_coalition_set_policy(coal, TASK_POLICY_DARWIN_BG, TASK_POLICY_DISABLE);
484 error = mach_to_bsd_errno(kr);
485 break;
486 case COALITION_POLICY_SUPPRESS_DARWIN_BG:
487 kr = jetsam_coalition_set_policy(coal, TASK_POLICY_DARWIN_BG, TASK_POLICY_ENABLE);
488 error = mach_to_bsd_errno(kr);
489 break;
490 default:
491 error = EINVAL;
492 break;
493 }
494
495 return error;
496 }
497
498 static int
coalition_policy_get_suppress(coalition_t coal,int32_t * retval)499 coalition_policy_get_suppress(coalition_t coal, int32_t *retval)
500 {
501 int value = 0;
502 kern_return_t kr = jetsam_coalition_get_policy(coal,
503 TASK_POLICY_DARWIN_BG, &value);
504
505 if (kr != KERN_SUCCESS) {
506 return mach_to_bsd_errno(kr);
507 }
508
509 switch (value) {
510 case TASK_POLICY_DISABLE:
511 *retval = (int32_t)COALITION_POLICY_SUPPRESS_NONE;
512 break;
513 case TASK_POLICY_ENABLE:
514 *retval = (int32_t)COALITION_POLICY_SUPPRESS_DARWIN_BG;
515 break;
516 default:
517 panic("unknown coalition policy suppress value %d", value);
518 break;
519 }
520
521 return 0;
522 }
523
524 int
sys_coalition_policy_set(proc_t p,struct coalition_policy_set_args * uap,__unused int32_t * retval)525 sys_coalition_policy_set(proc_t p, struct coalition_policy_set_args *uap, __unused int32_t *retval)
526 {
527 uint64_t cid = uap->cid;
528 coalition_policy_flavor_t flavor = uap->flavor;
529 uint32_t value = uap->value;
530
531 int error = 0;
532
533 if (!IOTaskHasEntitlement(proc_task(p), COALITION_POLICY_ENTITLEMENT)) {
534 return EPERM;
535 }
536
537 coalition_t coal = coalition_find_by_id(cid);
538 if (coal == COALITION_NULL) {
539 return ESRCH;
540 }
541
542 if (coalition_type(coal) != COALITION_TYPE_JETSAM) {
543 error = ENOTSUP;
544 goto bad;
545 }
546
547 switch (flavor) {
548 case COALITION_POLICY_SUPPRESS:
549 error = coalition_policy_set_suppress(coal, (coalition_policy_suppress_t)value);
550 break;
551 default:
552 error = EINVAL;
553 break;
554 }
555
556 bad:
557 coalition_release(coal);
558 return error;
559 }
560
561 int
sys_coalition_policy_get(proc_t p,struct coalition_policy_get_args * uap,int32_t * retval)562 sys_coalition_policy_get(proc_t p, struct coalition_policy_get_args *uap, int32_t *retval)
563 {
564 uint64_t cid = uap->cid;
565 coalition_policy_flavor_t flavor = uap->flavor;
566
567 int error = 0;
568
569 if (!IOTaskHasEntitlement(proc_task(p), COALITION_POLICY_ENTITLEMENT)) {
570 return EPERM;
571 }
572
573 coalition_t coal = coalition_find_by_id(cid);
574 if (coal == COALITION_NULL) {
575 return ESRCH;
576 }
577
578 if (coalition_type(coal) != COALITION_TYPE_JETSAM) {
579 error = ENOTSUP;
580 goto bad;
581 }
582
583 switch (flavor) {
584 case COALITION_POLICY_SUPPRESS:
585 error = coalition_policy_get_suppress(coal, retval);
586 break;
587 default:
588 error = EINVAL;
589 break;
590 }
591
592 bad:
593 coalition_release(coal);
594 return error;
595 }
596
597
598
599 #if DEVELOPMENT || DEBUG
600 static int sysctl_coalition_get_ids SYSCTL_HANDLER_ARGS
601 {
602 #pragma unused(oidp, arg1, arg2)
603 int error, pid;
604 proc_t tproc;
605 uint64_t value;
606 uint64_t ids[COALITION_NUM_TYPES] = {};
607
608
609 error = SYSCTL_IN(req, &value, sizeof(value));
610 if (error) {
611 return error;
612 }
613 if (!req->newptr) {
614 pid = proc_getpid(req->p);
615 } else {
616 pid = (int)value;
617 }
618
619 coal_dbg("looking up coalitions for pid:%d", pid);
620 tproc = proc_find(pid);
621 if (tproc == NULL) {
622 coal_dbg("ERROR: Couldn't find pid:%d", pid);
623 return ESRCH;
624 }
625
626 task_coalition_ids(proc_task(tproc), ids);
627 proc_rele(tproc);
628
629 return SYSCTL_OUT(req, ids, sizeof(ids));
630 }
631
632 SYSCTL_PROC(_kern, OID_AUTO, coalitions, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
633 0, 0, sysctl_coalition_get_ids, "Q", "coalition ids of a given process");
634
635
636 static int sysctl_coalition_get_roles SYSCTL_HANDLER_ARGS
637 {
638 #pragma unused(oidp, arg1, arg2)
639 int error, pid;
640 proc_t tproc;
641 int value;
642 int roles[COALITION_NUM_TYPES] = {};
643
644
645 error = SYSCTL_IN(req, &value, sizeof(value));
646 if (error) {
647 return error;
648 }
649 if (!req->newptr) {
650 pid = proc_getpid(req->p);
651 } else {
652 pid = (int)value;
653 }
654
655 coal_dbg("looking up coalitions for pid:%d", pid);
656 tproc = proc_find(pid);
657 if (tproc == NULL) {
658 coal_dbg("ERROR: Couldn't find pid:%d", pid);
659 return ESRCH;
660 }
661
662 task_coalition_roles(proc_task(tproc), roles);
663 proc_rele(tproc);
664
665 return SYSCTL_OUT(req, roles, sizeof(roles));
666 }
667
668 SYSCTL_PROC(_kern, OID_AUTO, coalition_roles, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
669 0, 0, sysctl_coalition_get_roles, "I", "coalition roles of a given process");
670
671
672 static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
673 {
674 #pragma unused(oidp, arg1, arg2)
675 int error, pid;
676 proc_t tproc;
677 coalition_t coal;
678 uint64_t value;
679 uint64_t pgcount[COALITION_NUM_TYPES];
680
681
682 error = SYSCTL_IN(req, &value, sizeof(value));
683 if (error) {
684 return error;
685 }
686 if (!req->newptr) {
687 pid = proc_getpid(req->p);
688 } else {
689 pid = (int)value;
690 }
691
692 coal_dbg("looking up coalitions for pid:%d", pid);
693 tproc = proc_find(pid);
694 if (tproc == NULL) {
695 coal_dbg("ERROR: Couldn't find pid:%d", pid);
696 return ESRCH;
697 }
698
699 memset(pgcount, 0, sizeof(pgcount));
700
701 for (int t = 0; t < COALITION_NUM_TYPES; t++) {
702 coal = task_get_coalition(proc_task(tproc), t);
703 if (coal != COALITION_NULL) {
704 int ntasks = 0;
705 pgcount[t] = coalition_get_page_count(coal, &ntasks);
706 coal_dbg("PID:%d, Coalition:%lld, type:%d, pgcount:%lld",
707 pid, coalition_id(coal), t, pgcount[t]);
708 }
709 }
710
711 proc_rele(tproc);
712
713 return SYSCTL_OUT(req, pgcount, sizeof(pgcount));
714 }
715
716 SYSCTL_PROC(_kern, OID_AUTO, coalition_page_count, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
717 0, 0, sysctl_coalition_get_page_count, "Q", "coalition page count of a specified process");
718
719
720 static int sysctl_coalition_get_pid_list SYSCTL_HANDLER_ARGS
721 {
722 #pragma unused(oidp, arg1, arg2)
723 int error, type, sort_order, pid;
724 int value[3];
725 int has_pid = 1;
726
727 coalition_t coal = COALITION_NULL;
728 proc_t tproc = PROC_NULL;
729 int npids = 0;
730 int pidlist[100] = { 0, };
731
732
733 error = SYSCTL_IN(req, &value, sizeof(value));
734 if (error) {
735 has_pid = 0;
736 error = SYSCTL_IN(req, &value, sizeof(value) - sizeof(value[0]));
737 }
738 if (error) {
739 return error;
740 }
741 if (!req->newptr) {
742 type = COALITION_TYPE_RESOURCE;
743 sort_order = COALITION_SORT_DEFAULT;
744 pid = proc_getpid(req->p);
745 } else {
746 type = value[0];
747 sort_order = value[1];
748 if (has_pid) {
749 pid = value[2];
750 } else {
751 pid = proc_getpid(req->p);
752 }
753 }
754
755 if (type < 0 || type >= COALITION_NUM_TYPES) {
756 return EINVAL;
757 }
758
759 coal_dbg("getting constituent PIDS for coalition of type %d "
760 "containing pid:%d (sort:%d)", type, pid, sort_order);
761 tproc = proc_find(pid);
762 if (tproc == NULL) {
763 coal_dbg("ERROR: Couldn't find pid:%d", pid);
764 return ESRCH;
765 }
766
767 coal = task_get_coalition(proc_task(tproc), type);
768 if (coal == COALITION_NULL) {
769 goto out;
770 }
771
772 npids = coalition_get_pid_list(coal, COALITION_ROLEMASK_ALLROLES, sort_order,
773 pidlist, sizeof(pidlist) / sizeof(pidlist[0]));
774 if (npids > (int)(sizeof(pidlist) / sizeof(pidlist[0]))) {
775 coal_dbg("Too many members in coalition %llu (from pid:%d): %d!",
776 coalition_id(coal), pid, npids);
777 npids = sizeof(pidlist) / sizeof(pidlist[0]);
778 }
779
780 out:
781 proc_rele(tproc);
782
783 if (npids < 0) {
784 /* npids is a negative errno */
785 return -npids;
786 }
787
788 if (npids == 0) {
789 return ENOENT;
790 }
791
792 return SYSCTL_OUT(req, pidlist, sizeof(pidlist[0]) * npids);
793 }
794
795 SYSCTL_PROC(_kern, OID_AUTO, coalition_pid_list, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
796 0, 0, sysctl_coalition_get_pid_list, "I", "list of PIDS which are members of the coalition of the current process");
797
798 #if DEVELOPMENT
799 static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
800 {
801 #pragma unused(oidp, arg1, arg2)
802 int error, should_set;
803 coalition_t coal;
804 uint64_t value[2];
805
806 should_set = 1;
807 error = SYSCTL_IN(req, value, sizeof(value));
808 if (error) {
809 error = SYSCTL_IN(req, value, sizeof(value) - sizeof(value[0]));
810 if (error) {
811 return error;
812 }
813 should_set = 0;
814 }
815 if (!req->newptr) {
816 return error;
817 }
818
819 coal = coalition_find_by_id(value[0]);
820 if (coal == COALITION_NULL) {
821 coal_dbg("Can't find coalition with ID:%lld", value[0]);
822 return ESRCH;
823 }
824
825 if (should_set) {
826 coalition_set_notify(coal, (int)value[1]);
827 }
828
829 value[0] = (uint64_t)coalition_should_notify(coal);
830
831 coalition_release(coal);
832
833 return SYSCTL_OUT(req, value, sizeof(value[0]));
834 }
835
836 SYSCTL_PROC(_kern, OID_AUTO, coalition_notify, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
837 0, 0, sysctl_coalition_notify, "Q", "get/set coalition notification flag");
838
839 extern int unrestrict_coalition_syscalls;
840 SYSCTL_INT(_kern, OID_AUTO, unrestrict_coalitions,
841 CTLFLAG_RW, &unrestrict_coalition_syscalls, 0,
842 "unrestrict the coalition interface");
843
844 #endif /* DEVELOPMENT */
845
846 #include <kern/energy_perf.h>
847
848 static int sysctl_coalition_gpu_energy_test SYSCTL_HANDLER_ARGS
849 {
850 #pragma unused(oidp, arg1, arg2)
851 if (!req->newptr) {
852 return ENOTSUP;
853 }
854
855 uint64_t value[4] = {};
856
857 int error = SYSCTL_IN(req, value, sizeof(value));
858 if (error) {
859 return error;
860 }
861
862 kern_return_t kr = KERN_SUCCESS;
863 energy_id_t energy_id_out = 0;
864
865 switch (value[0]) {
866 case 1:
867 kr = current_energy_id(&energy_id_out);
868 break;
869 case 2:
870 kr = task_id_token_to_energy_id((mach_port_name_t) value[1], &energy_id_out);
871 break;
872 case 3:
873 kr = energy_id_report_energy(ENERGY_ID_SOURCE_GPU,
874 (energy_id_t)value[1], (energy_id_t)value[2], value[3]);
875 break;
876 }
877
878 if (kr != KERN_SUCCESS) {
879 return mach_to_bsd_errno(kr);
880 }
881
882 value[0] = energy_id_out;
883
884 return SYSCTL_OUT(req, value, sizeof(value[0]));
885 }
886
887 SYSCTL_PROC(_kern, OID_AUTO, coalition_gpu_energy_test, CTLFLAG_MASKED | CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
888 0, 0, sysctl_coalition_gpu_energy_test, "Q", "test coalition gpu energy");
889
890
891 #endif /* DEVELOPMENT || DEBUG */
892