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} 259