]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_sfi.c
xnu-2782.40.9.tar.gz
[apple/xnu.git] / bsd / kern / kern_sfi.c
CommitLineData
fe8ab488
A
1/*
2 * Copyright (c) 2013 Apple 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/assert.h>
31#include <kern/locks.h>
32#include <kern/task.h>
33#include <kern/thread.h>
34#include <kern/sfi.h>
35#include <libkern/libkern.h>
36#include <mach/mach_time.h>
37#include <pexpert/pexpert.h>
38#include <sys/proc.h>
39#include <sys/proc_info.h>
40#include <sys/sysproto.h>
41#include <sys/sfi.h>
42#include <sys/kdebug.h>
43#include <sys/priv.h>
44
45/*
46 * This file provides the syscall-based configuration facility
47 * for Selective Forced Idle (SFI). Input arguments have basic checking
48 * applied here, although more specific semantic checking is done in
49 * osfmk/kern/sfi.c. All copyin()/copyout() operations are performed
50 * in this source file.
51 */
52
53#define SFI_DEBUG 0
54
55#if SFI_DEBUG
56#define dprintf(...) printf(__VA_ARGS__)
57#else
58#define dprintf(...) do { } while(0)
59#endif
60
61static int proc_apply_sfi_managed(proc_t p, void * arg);
62
63int sfi_ctl(struct proc *p __unused, struct sfi_ctl_args *uap, int32_t *retval __unused)
64{
65 uint32_t operation = uap->operation;
66 int error = 0;
67 kern_return_t kret = KERN_SUCCESS;
68 uint64_t out_time = 0;
69
70 switch (operation) {
71 case SFI_CTL_OPERATION_SFI_SET_WINDOW:
72 if (uap->out_time != USER_ADDR_NULL) {
73 return EINVAL;
74 }
75 if (uap->sfi_class != SFI_CLASS_UNSPECIFIED) {
76 return EINVAL;
77 }
78
79 error = priv_check_cred(kauth_cred_get(), PRIV_SELECTIVE_FORCED_IDLE, 0);
80 if (error) {
81 dprintf("%s failed privilege check for sfi_ctl: %d\n", p->p_comm, error);
82 return (error);
83 } else {
84 dprintf("%s succeeded privilege check for sfi_ctl\n", p->p_comm);
85 }
86
87 if (uap->time == 0) {
88 /* actually a cancel */
89 kret = sfi_window_cancel();
90 } else {
91 kret = sfi_set_window(uap->time);
92 }
93
94 if (kret) {
95 error = EINVAL;
96 }
97
98 break;
99 case SFI_CTL_OPERATION_SFI_GET_WINDOW:
100 if (uap->time != 0) {
101 return EINVAL;
102 }
103 if (uap->sfi_class != SFI_CLASS_UNSPECIFIED) {
104 return EINVAL;
105 }
106
107 kret = sfi_get_window(&out_time);
108 if (kret == KERN_SUCCESS) {
109 error = copyout(&out_time, uap->out_time, sizeof(out_time));
110 } else {
111 error = EINVAL;
112 }
113
114 break;
115 case SFI_CTL_OPERATION_SET_CLASS_OFFTIME:
116 if (uap->out_time != USER_ADDR_NULL) {
117 return EINVAL;
118 }
119
120 error = priv_check_cred(kauth_cred_get(), PRIV_SELECTIVE_FORCED_IDLE, 0);
121 if (error) {
122 dprintf("%s failed privilege check for sfi_ctl: %d\n", p->p_comm, error);
123 return (error);
124 } else {
125 dprintf("%s succeeded privilege check for sfi_ctl\n", p->p_comm);
126 }
127
128 if (uap->time == 0) {
129 /* actually a cancel */
130 kret = sfi_class_offtime_cancel(uap->sfi_class);
131 } else {
132 kret = sfi_set_class_offtime(uap->sfi_class, uap->time);
133 }
134
135 if (kret) {
136 error = EINVAL;
137 }
138
139 break;
140 case SFI_CTL_OPERATION_GET_CLASS_OFFTIME:
141 if (uap->time != 0) {
142 return EINVAL;
143 }
144
145 kret = sfi_get_class_offtime(uap->sfi_class, &out_time);
146 if (kret == KERN_SUCCESS) {
147 error = copyout(&out_time, uap->out_time, sizeof(out_time));
148 } else {
149 error = EINVAL;
150 }
151
152 break;
153 default:
154 error = ENOTSUP;
155 break;
156 }
157
158 return error;
159}
160
161static int proc_apply_sfi_managed(proc_t p, void * arg)
162{
163 uint32_t flags = *(uint32_t *)arg;
164 pid_t pid = p->p_pid;
165 boolean_t managed_enabled = (flags == SFI_PROCESS_SET_MANAGED)? TRUE : FALSE;
166
167 if (pid == 0) { /* ignore setting on kernproc */
168 return PROC_RETURNED;
169 }
170
171 if (managed_enabled) {
172 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SFI, SFI_PID_SET_MANAGED) | DBG_FUNC_NONE, pid, 0, 0, 0, 0);
173 } else {
174 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SFI, SFI_PID_CLEAR_MANAGED) | DBG_FUNC_NONE, pid, 0, 0, 0, 0);
175 }
176
177 proc_set_task_policy(p->task, THREAD_NULL,
178 TASK_POLICY_ATTRIBUTE, TASK_POLICY_SFI_MANAGED,
179 managed_enabled ? TASK_POLICY_ENABLE : TASK_POLICY_DISABLE);
180 return PROC_RETURNED;
181}
182
183int sfi_pidctl(struct proc *p __unused, struct sfi_pidctl_args *uap, int32_t *retval __unused)
184{
185 uint32_t operation = uap->operation;
186 pid_t pid = uap->pid;
187 int error = 0;
188 uint32_t out_flags = 0;
189 boolean_t managed_enabled;
190 proc_t targetp;
191
192 switch (operation) {
193 case SFI_PIDCTL_OPERATION_PID_SET_FLAGS:
194 if (uap->out_sfi_flags != USER_ADDR_NULL
195 || !(uap->sfi_flags & SFI_PROCESS_SET_MANAGED_MASK)
196 || uap->sfi_flags == SFI_PROCESS_SET_MANAGED_MASK) {
197 return EINVAL;
198 }
199
200 error = priv_check_cred(kauth_cred_get(), PRIV_SELECTIVE_FORCED_IDLE, 0);
201 if (error) {
202 dprintf("%s failed privilege check for sfi_pidctl: %d\n", p->p_comm, error);
203 return (error);
204 } else {
205 dprintf("%s succeeded privilege check for sfi_pidctl\n", p->p_comm);
206 }
207
208 if (uap->pid == 0) {
209 /* only allow SFI_PROCESS_SET_UNMANAGED for pid 0 */
210 if (uap->sfi_flags != SFI_PROCESS_SET_UNMANAGED) {
211 return EINVAL;
212 }
213
214 proc_iterate(PROC_ALLPROCLIST, proc_apply_sfi_managed, (void *)&uap->sfi_flags, NULL, NULL);
215 break;
216 }
217
218 targetp = proc_find(pid);
219 if (!targetp) {
220 error = ESRCH;
221 break;
222 }
223
224 proc_apply_sfi_managed(targetp, (void *)&uap->sfi_flags);
225
226 proc_rele(targetp);
227
228 break;
229 case SFI_PIDCTL_OPERATION_PID_GET_FLAGS:
230 if (uap->sfi_flags != 0) {
231 return EINVAL;
232 }
233 if (uap->pid == 0) {
234 return EINVAL;
235 }
236
237 targetp = proc_find(pid);
238 if (!targetp) {
239 error = ESRCH;
240 break;
241 }
242
243 managed_enabled = proc_get_task_policy(targetp->task, THREAD_NULL, TASK_POLICY_ATTRIBUTE, TASK_POLICY_SFI_MANAGED);
244
245 proc_rele(targetp);
246
247 out_flags = managed_enabled ? SFI_PROCESS_SET_MANAGED : SFI_PROCESS_SET_UNMANAGED;
248
249 error = copyout(&out_flags, uap->out_sfi_flags, sizeof(out_flags));
250
251 break;
252 default:
253 error = ENOTSUP;
254 break;
255 }
256
257 return error;
258}