xref: /xnu-12377.61.12/bsd/kern/sys_coalition.c (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
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