xref: /xnu-11417.140.69/bsd/kern/sys_coalition.c (revision 43a90889846e00bfb5cf1d255cdc0a701a1e05a4)
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