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_info_pid_list(coalition_t coal,user_addr_t buffer,user_size_t * bufsize_inout_p)339 coalition_info_pid_list(coalition_t coal, user_addr_t buffer, user_size_t *bufsize_inout_p)
340 {
341 int error;
342
343 user_size_t size_in = *bufsize_inout_p;
344 if (size_in == 0) {
345 return 0;
346 }
347
348 /* We return at most COALITION_INFO_PID_LIST_MAX_PIDS. */
349 size_t alloc_count = MIN(size_in / sizeof(pid_t), (user_size_t)COALITION_INFO_PID_LIST_MAX_PIDS);
350
351 pid_t *pid_list = kalloc_type(pid_t, alloc_count, Z_WAITOK | Z_ZERO);
352 if (!pid_list) {
353 return ENOMEM;
354 }
355
356 int ntasks = coalition_get_pid_list(coal, COALITION_ROLEMASK_ALLROLES, COALITION_SORT_NOSORT, pid_list, (int)alloc_count);
357 if (ntasks < 0) {
358 /* coalition_get_pid_list returns negative errno */
359 error = -ntasks;
360 goto out_free;
361 }
362
363 /* Ensure size_in >= alloc_len * sizeof(pid_t) >= size_out */
364 user_size_t size_out = MIN(alloc_count * sizeof(pid_t), ntasks * sizeof(pid_t));
365 assert(size_in >= alloc_count * sizeof(pid_t));
366 assert(alloc_count * sizeof(pid_t) >= size_out);
367
368 *bufsize_inout_p = size_out;
369 error = copyout(pid_list, buffer, size_out);
370
371 out_free:
372 kfree_type(pid_t, alloc_count, pid_list);
373 return error;
374 }
375
376 static int
coalition_ledger_logical_writes_limit(coalition_t coal,user_addr_t buffer,user_size_t bufsize)377 coalition_ledger_logical_writes_limit(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
378 {
379 int error = 0;
380 int64_t limit = 0;
381
382 if (coalition_type(coal) != COALITION_TYPE_RESOURCE) {
383 error = EINVAL;
384 goto out;
385 }
386 error = copyin(buffer, &limit, MIN(bufsize, sizeof(limit)));
387 if (error) {
388 goto out;
389 }
390
391
392 error = coalition_ledger_set_logical_writes_limit(coal, limit);
393 out:
394 return error;
395 }
396
397 int
coalition_info(proc_t p,struct coalition_info_args * uap,__unused int32_t * retval)398 coalition_info(proc_t p, struct coalition_info_args *uap, __unused int32_t *retval)
399 {
400 user_addr_t cidp = uap->cid;
401 user_addr_t buffer = uap->buffer;
402 user_addr_t bufsizep = uap->bufsize;
403 user_size_t bufsize = 0;
404 uint32_t flavor = uap->flavor;
405 int error;
406 uint64_t cid;
407 coalition_t coal;
408
409 error = copyin(cidp, &cid, sizeof(cid));
410 if (error) {
411 return error;
412 }
413
414 coal = coalition_find_by_id(cid);
415 if (coal == COALITION_NULL) {
416 return ESRCH;
417 }
418 /* TODO: priv check? EPERM or ESRCH? */
419
420 if (IS_64BIT_PROCESS(p)) {
421 user64_size_t size64;
422 error = copyin(bufsizep, &size64, sizeof(size64));
423 bufsize = (user_size_t)size64;
424 } else {
425 user32_size_t size32;
426 error = copyin(bufsizep, &size32, sizeof(size32));
427 bufsize = (user_size_t)size32;
428 }
429 if (error) {
430 goto bad;
431 }
432
433 switch (flavor) {
434 case COALITION_INFO_RESOURCE_USAGE:
435 error = coalition_info_resource_usage(coal, buffer, bufsize);
436 break;
437 case COALITION_INFO_SET_NAME:
438 error = coalition_info_set_name_internal(coal, buffer, bufsize);
439 break;
440 case COALITION_INFO_SET_EFFICIENCY:
441 error = coalition_info_efficiency(coal, buffer, bufsize);
442 break;
443 case COALITION_INFO_PID_LIST:
444 /* bufsize is used as inout for this operation */
445 error = coalition_info_pid_list(coal, buffer, &bufsize);
446 if (error) {
447 goto bad;
448 }
449
450 /* When successful, the size of the returned list is bounded */
451 assert(bufsize <= COALITION_INFO_PID_LIST_MAX_PIDS * sizeof(pid_t));
452
453 if (IS_64BIT_PROCESS(p)) {
454 user64_size_t size64out = bufsize;
455 error = copyout(&size64out, bufsizep, sizeof(user64_size_t));
456 } else {
457 /* bufsize < UINT32_MAX is guaranteed due to the assertion above */
458 user32_size_t size32out = (user32_size_t)bufsize;
459 error = copyout(&size32out, bufsizep, sizeof(user32_size_t));
460 }
461 break;
462 #if DEVELOPMENT || DEBUG
463 case COALITION_INFO_GET_DEBUG_INFO:
464 error = coalition_info_get_debug_info(coal, buffer, bufsize);
465 break;
466 #endif /* DEVELOPMENT || DEBUG */
467 default:
468 error = EINVAL;
469 }
470
471 bad:
472 coalition_release(coal);
473 return error;
474 }
475
476 int
coalition_ledger(__unused proc_t p,__unused struct coalition_ledger_args * uap,__unused int32_t * retval)477 coalition_ledger(__unused proc_t p, __unused struct coalition_ledger_args *uap, __unused int32_t *retval)
478 {
479 user_addr_t cidp = uap->cid;
480 user_addr_t buffer = uap->buffer;
481 user_addr_t bufsizep = uap->bufsize;
482 user_size_t bufsize;
483 uint32_t operation = uap->operation;
484 int error;
485 uint64_t cid;
486 coalition_t coal = COALITION_NULL;
487
488 if (!kauth_cred_issuser(kauth_cred_get())) {
489 error = EPERM;
490 goto out;
491 }
492
493 error = copyin(cidp, &cid, sizeof(cid));
494 if (error) {
495 goto out;
496 }
497
498 coal = coalition_find_by_id(cid);
499 if (coal == COALITION_NULL) {
500 error = ESRCH;
501 goto out;
502 }
503
504 if (IS_64BIT_PROCESS(p)) {
505 user64_size_t size64;
506 error = copyin(bufsizep, &size64, sizeof(size64));
507 bufsize = (user_size_t)size64;
508 } else {
509 user32_size_t size32;
510 error = copyin(bufsizep, &size32, sizeof(size32));
511 bufsize = (user_size_t)size32;
512 }
513 if (error) {
514 goto out;
515 }
516
517 switch (operation) {
518 case COALITION_LEDGER_SET_LOGICAL_WRITES_LIMIT:
519 error = coalition_ledger_logical_writes_limit(coal, buffer, bufsize);
520 break;
521 default:
522 error = EINVAL;
523 }
524 out:
525 if (coal != COALITION_NULL) {
526 coalition_release(coal);
527 }
528 return error;
529 }
530
531 static int
coalition_policy_set_suppress(coalition_t coal,coalition_policy_suppress_t value)532 coalition_policy_set_suppress(coalition_t coal, coalition_policy_suppress_t value)
533 {
534 int error = 0;
535
536 kern_return_t kr;
537
538 switch (value) {
539 case COALITION_POLICY_SUPPRESS_NONE:
540 kr = jetsam_coalition_set_policy(coal, TASK_POLICY_DARWIN_BG, TASK_POLICY_DISABLE);
541 error = mach_to_bsd_errno(kr);
542 break;
543 case COALITION_POLICY_SUPPRESS_DARWIN_BG:
544 kr = jetsam_coalition_set_policy(coal, TASK_POLICY_DARWIN_BG, TASK_POLICY_ENABLE);
545 error = mach_to_bsd_errno(kr);
546 break;
547 default:
548 error = EINVAL;
549 break;
550 }
551
552 return error;
553 }
554
555 static int
coalition_policy_get_suppress(coalition_t coal,int32_t * retval)556 coalition_policy_get_suppress(coalition_t coal, int32_t *retval)
557 {
558 int value = 0;
559 kern_return_t kr = jetsam_coalition_get_policy(coal,
560 TASK_POLICY_DARWIN_BG, &value);
561
562 if (kr != KERN_SUCCESS) {
563 return mach_to_bsd_errno(kr);
564 }
565
566 switch (value) {
567 case TASK_POLICY_DISABLE:
568 *retval = (int32_t)COALITION_POLICY_SUPPRESS_NONE;
569 break;
570 case TASK_POLICY_ENABLE:
571 *retval = (int32_t)COALITION_POLICY_SUPPRESS_DARWIN_BG;
572 break;
573 default:
574 panic("unknown coalition policy suppress value %d", value);
575 break;
576 }
577
578 return 0;
579 }
580
581 int
sys_coalition_policy_set(proc_t p,struct coalition_policy_set_args * uap,__unused int32_t * retval)582 sys_coalition_policy_set(proc_t p, struct coalition_policy_set_args *uap, __unused int32_t *retval)
583 {
584 uint64_t cid = uap->cid;
585 coalition_policy_flavor_t flavor = uap->flavor;
586 uint32_t value = uap->value;
587
588 int error = 0;
589
590 if (!IOTaskHasEntitlement(proc_task(p), COALITION_POLICY_ENTITLEMENT)) {
591 return EPERM;
592 }
593
594 coalition_t coal = coalition_find_by_id(cid);
595 if (coal == COALITION_NULL) {
596 return ESRCH;
597 }
598
599 if (coalition_type(coal) != COALITION_TYPE_JETSAM) {
600 error = ENOTSUP;
601 goto bad;
602 }
603
604 switch (flavor) {
605 case COALITION_POLICY_SUPPRESS:
606 error = coalition_policy_set_suppress(coal, (coalition_policy_suppress_t)value);
607 break;
608 default:
609 error = EINVAL;
610 break;
611 }
612
613 bad:
614 coalition_release(coal);
615 return error;
616 }
617
618 int
sys_coalition_policy_get(proc_t p,struct coalition_policy_get_args * uap,int32_t * retval)619 sys_coalition_policy_get(proc_t p, struct coalition_policy_get_args *uap, int32_t *retval)
620 {
621 uint64_t cid = uap->cid;
622 coalition_policy_flavor_t flavor = uap->flavor;
623
624 int error = 0;
625
626 if (!IOTaskHasEntitlement(proc_task(p), COALITION_POLICY_ENTITLEMENT)) {
627 return EPERM;
628 }
629
630 coalition_t coal = coalition_find_by_id(cid);
631 if (coal == COALITION_NULL) {
632 return ESRCH;
633 }
634
635 if (coalition_type(coal) != COALITION_TYPE_JETSAM) {
636 error = ENOTSUP;
637 goto bad;
638 }
639
640 switch (flavor) {
641 case COALITION_POLICY_SUPPRESS:
642 error = coalition_policy_get_suppress(coal, retval);
643 break;
644 default:
645 error = EINVAL;
646 break;
647 }
648
649 bad:
650 coalition_release(coal);
651 return error;
652 }
653
654
655
656 #if DEVELOPMENT || DEBUG
657 static int sysctl_coalition_get_ids SYSCTL_HANDLER_ARGS
658 {
659 #pragma unused(oidp, arg1, arg2)
660 int error, pid;
661 proc_t tproc;
662 uint64_t value;
663 uint64_t ids[COALITION_NUM_TYPES] = {};
664
665
666 error = SYSCTL_IN(req, &value, sizeof(value));
667 if (error) {
668 return error;
669 }
670 if (!req->newptr) {
671 pid = proc_getpid(req->p);
672 } else {
673 pid = (int)value;
674 }
675
676 coal_dbg("looking up coalitions for pid:%d", pid);
677 tproc = proc_find(pid);
678 if (tproc == NULL) {
679 coal_dbg("ERROR: Couldn't find pid:%d", pid);
680 return ESRCH;
681 }
682
683 task_coalition_ids(proc_task(tproc), ids);
684 proc_rele(tproc);
685
686 return SYSCTL_OUT(req, ids, sizeof(ids));
687 }
688
689 SYSCTL_PROC(_kern, OID_AUTO, coalitions, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
690 0, 0, sysctl_coalition_get_ids, "Q", "coalition ids of a given process");
691
692
693 static int sysctl_coalition_get_roles SYSCTL_HANDLER_ARGS
694 {
695 #pragma unused(oidp, arg1, arg2)
696 int error, pid;
697 proc_t tproc;
698 int value;
699 int roles[COALITION_NUM_TYPES] = {};
700
701
702 error = SYSCTL_IN(req, &value, sizeof(value));
703 if (error) {
704 return error;
705 }
706 if (!req->newptr) {
707 pid = proc_getpid(req->p);
708 } else {
709 pid = (int)value;
710 }
711
712 coal_dbg("looking up coalitions for pid:%d", pid);
713 tproc = proc_find(pid);
714 if (tproc == NULL) {
715 coal_dbg("ERROR: Couldn't find pid:%d", pid);
716 return ESRCH;
717 }
718
719 task_coalition_roles(proc_task(tproc), roles);
720 proc_rele(tproc);
721
722 return SYSCTL_OUT(req, roles, sizeof(roles));
723 }
724
725 SYSCTL_PROC(_kern, OID_AUTO, coalition_roles, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
726 0, 0, sysctl_coalition_get_roles, "I", "coalition roles of a given process");
727
728
729 static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
730 {
731 #pragma unused(oidp, arg1, arg2)
732 int error, pid;
733 proc_t tproc;
734 coalition_t coal;
735 uint64_t value;
736 uint64_t pgcount[COALITION_NUM_TYPES];
737
738
739 error = SYSCTL_IN(req, &value, sizeof(value));
740 if (error) {
741 return error;
742 }
743 if (!req->newptr) {
744 pid = proc_getpid(req->p);
745 } else {
746 pid = (int)value;
747 }
748
749 coal_dbg("looking up coalitions for pid:%d", pid);
750 tproc = proc_find(pid);
751 if (tproc == NULL) {
752 coal_dbg("ERROR: Couldn't find pid:%d", pid);
753 return ESRCH;
754 }
755
756 memset(pgcount, 0, sizeof(pgcount));
757
758 for (int t = 0; t < COALITION_NUM_TYPES; t++) {
759 coal = task_get_coalition(proc_task(tproc), t);
760 if (coal != COALITION_NULL) {
761 int ntasks = 0;
762 pgcount[t] = coalition_get_page_count(coal, &ntasks);
763 coal_dbg("PID:%d, Coalition:%lld, type:%d, pgcount:%lld",
764 pid, coalition_id(coal), t, pgcount[t]);
765 }
766 }
767
768 proc_rele(tproc);
769
770 return SYSCTL_OUT(req, pgcount, sizeof(pgcount));
771 }
772
773 SYSCTL_PROC(_kern, OID_AUTO, coalition_page_count, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
774 0, 0, sysctl_coalition_get_page_count, "Q", "coalition page count of a specified process");
775
776
777 static int sysctl_coalition_get_pid_list SYSCTL_HANDLER_ARGS
778 {
779 #pragma unused(oidp, arg1, arg2)
780 int error, type, sort_order, pid;
781 int value[3];
782 int has_pid = 1;
783
784 coalition_t coal = COALITION_NULL;
785 proc_t tproc = PROC_NULL;
786 int npids = 0;
787 int pidlist[100] = { 0, };
788
789
790 error = SYSCTL_IN(req, &value, sizeof(value));
791 if (error) {
792 has_pid = 0;
793 error = SYSCTL_IN(req, &value, sizeof(value) - sizeof(value[0]));
794 }
795 if (error) {
796 return error;
797 }
798 if (!req->newptr) {
799 type = COALITION_TYPE_RESOURCE;
800 sort_order = COALITION_SORT_DEFAULT;
801 pid = proc_getpid(req->p);
802 } else {
803 type = value[0];
804 sort_order = value[1];
805 if (has_pid) {
806 pid = value[2];
807 } else {
808 pid = proc_getpid(req->p);
809 }
810 }
811
812 if (type < 0 || type >= COALITION_NUM_TYPES) {
813 return EINVAL;
814 }
815
816 coal_dbg("getting constituent PIDS for coalition of type %d "
817 "containing pid:%d (sort:%d)", type, pid, sort_order);
818 tproc = proc_find(pid);
819 if (tproc == NULL) {
820 coal_dbg("ERROR: Couldn't find pid:%d", pid);
821 return ESRCH;
822 }
823
824 coal = task_get_coalition(proc_task(tproc), type);
825 if (coal == COALITION_NULL) {
826 goto out;
827 }
828
829 npids = coalition_get_pid_list(coal, COALITION_ROLEMASK_ALLROLES, sort_order,
830 pidlist, sizeof(pidlist) / sizeof(pidlist[0]));
831 if (npids > (int)(sizeof(pidlist) / sizeof(pidlist[0]))) {
832 coal_dbg("Too many members in coalition %llu (from pid:%d): %d!",
833 coalition_id(coal), pid, npids);
834 npids = sizeof(pidlist) / sizeof(pidlist[0]);
835 }
836
837 out:
838 proc_rele(tproc);
839
840 if (npids < 0) {
841 /* npids is a negative errno */
842 return -npids;
843 }
844
845 if (npids == 0) {
846 return ENOENT;
847 }
848
849 return SYSCTL_OUT(req, pidlist, sizeof(pidlist[0]) * npids);
850 }
851
852 SYSCTL_PROC(_kern, OID_AUTO, coalition_pid_list, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
853 0, 0, sysctl_coalition_get_pid_list, "I", "list of PIDS which are members of the coalition of the current process");
854
855 #if DEVELOPMENT
856 static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
857 {
858 #pragma unused(oidp, arg1, arg2)
859 int error, should_set;
860 coalition_t coal;
861 uint64_t value[2];
862
863 should_set = 1;
864 error = SYSCTL_IN(req, value, sizeof(value));
865 if (error) {
866 error = SYSCTL_IN(req, value, sizeof(value) - sizeof(value[0]));
867 if (error) {
868 return error;
869 }
870 should_set = 0;
871 }
872 if (!req->newptr) {
873 return error;
874 }
875
876 coal = coalition_find_by_id(value[0]);
877 if (coal == COALITION_NULL) {
878 coal_dbg("Can't find coalition with ID:%lld", value[0]);
879 return ESRCH;
880 }
881
882 if (should_set) {
883 coalition_set_notify(coal, (int)value[1]);
884 }
885
886 value[0] = (uint64_t)coalition_should_notify(coal);
887
888 coalition_release(coal);
889
890 return SYSCTL_OUT(req, value, sizeof(value[0]));
891 }
892
893 SYSCTL_PROC(_kern, OID_AUTO, coalition_notify, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
894 0, 0, sysctl_coalition_notify, "Q", "get/set coalition notification flag");
895
896 extern int unrestrict_coalition_syscalls;
897 SYSCTL_INT(_kern, OID_AUTO, unrestrict_coalitions,
898 CTLFLAG_RW, &unrestrict_coalition_syscalls, 0,
899 "unrestrict the coalition interface");
900
901 #endif /* DEVELOPMENT */
902
903 #include <kern/energy_perf.h>
904
905 static int sysctl_coalition_gpu_energy_test SYSCTL_HANDLER_ARGS
906 {
907 #pragma unused(oidp, arg1, arg2)
908 if (!req->newptr) {
909 return ENOTSUP;
910 }
911
912 uint64_t value[4] = {};
913
914 int error = SYSCTL_IN(req, value, sizeof(value));
915 if (error) {
916 return error;
917 }
918
919 kern_return_t kr = KERN_SUCCESS;
920 energy_id_t energy_id_out = 0;
921
922 switch (value[0]) {
923 case 1:
924 kr = current_energy_id(&energy_id_out);
925 break;
926 case 2:
927 kr = task_id_token_to_energy_id((mach_port_name_t) value[1], &energy_id_out);
928 break;
929 case 3:
930 kr = energy_id_report_energy(ENERGY_ID_SOURCE_GPU,
931 (energy_id_t)value[1], (energy_id_t)value[2], value[3]);
932 break;
933 }
934
935 if (kr != KERN_SUCCESS) {
936 return mach_to_bsd_errno(kr);
937 }
938
939 value[0] = energy_id_out;
940
941 return SYSCTL_OUT(req, value, sizeof(value[0]));
942 }
943
944 SYSCTL_PROC(_kern, OID_AUTO, coalition_gpu_energy_test, CTLFLAG_MASKED | CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
945 0, 0, sysctl_coalition_gpu_energy_test, "Q", "test coalition gpu energy");
946
947
948 #endif /* DEVELOPMENT || DEBUG */
949