1/* 2 * Copyright (c) 2003-2006 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#include <stdint.h> 30#include <mach/boolean.h> 31#include <mach/mach_types.h> 32 33#include <sys/syscall.h> 34#include <sys/types.h> /* u_int */ 35#include <sys/proc.h> /* proc_t */ 36#include <sys/systm.h> /* struct sysent */ 37#include <sys/sysproto.h> 38#include <sys/kdebug.h> /* KDEBUG_ENABLE_CHUD */ 39#include <sys/kauth.h> /* kauth_cred_get */ 40#include <libkern/OSAtomic.h> 41#if CONFIG_MACF 42#include <security/mac_framework.h> /* mac_system_check_chud */ 43#endif 44 45#pragma mark **** kern debug **** 46typedef void (*chudxnu_kdebug_callback_func_t)(uint32_t debugid, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4); 47static void chud_null_kdebug(uint32_t debugid, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4); 48static chudxnu_kdebug_callback_func_t kdebug_callback_fn = chud_null_kdebug; 49 50kern_return_t chudxnu_kdebug_callback_enter(chudxnu_kdebug_callback_func_t); 51kern_return_t chudxnu_kdebug_callback_cancel(void); 52 53extern void kdbg_control_chud(int val, void *fn); 54 55static void chud_null_kdebug(uint32_t debugid __unused, uintptr_t arg0 __unused, 56 uintptr_t arg1 __unused, uintptr_t arg2 __unused, uintptr_t arg3 __unused, 57 uintptr_t arg4 __unused) { 58 return; 59} 60 61static void 62chudxnu_private_kdebug_callback( 63 uint32_t debugid, 64 uintptr_t arg0, 65 uintptr_t arg1, 66 uintptr_t arg2, 67 uintptr_t arg3, 68 uintptr_t arg4) 69{ 70 chudxnu_kdebug_callback_func_t fn = kdebug_callback_fn; 71 72 if(fn) { 73 (fn)(debugid, arg0, arg1, arg2, arg3, arg4); 74 } 75} 76 77__private_extern__ kern_return_t 78chudxnu_kdebug_callback_enter(chudxnu_kdebug_callback_func_t func) 79{ 80 /* Atomically set the callback. */ 81 if(OSCompareAndSwapPtr(chud_null_kdebug, func, 82 (void * volatile *)&kdebug_callback_fn)) { 83 84 kdbg_control_chud(TRUE, (void *)chudxnu_private_kdebug_callback); 85 return KERN_SUCCESS; 86 } 87 return KERN_FAILURE; 88} 89 90__private_extern__ kern_return_t 91chudxnu_kdebug_callback_cancel(void) 92{ 93 kdbg_control_chud(FALSE, NULL); 94 95 chudxnu_kdebug_callback_func_t old = kdebug_callback_fn; 96 97 while(!OSCompareAndSwapPtr(old, chud_null_kdebug, 98 (void * volatile *)&kdebug_callback_fn)) { 99 old = kdebug_callback_fn; 100 } 101 102 return KERN_SUCCESS; 103} 104 105#pragma mark **** CHUD syscall **** 106typedef kern_return_t (*chudxnu_syscall_callback_func_t)(uint64_t code, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4); 107 108static kern_return_t chud_null_syscall(uint64_t code, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4); 109static chudxnu_syscall_callback_func_t syscall_callback_fn = chud_null_syscall; 110 111kern_return_t chudxnu_syscall_callback_enter(chudxnu_syscall_callback_func_t func); 112kern_return_t chudxnu_syscall_callback_cancel(void); 113 114static kern_return_t chud_null_syscall(uint64_t code __unused, 115 uint64_t arg0 __unused, uint64_t arg1 __unused, uint64_t arg2 __unused, 116 uint64_t arg3 __unused, uint64_t arg4 __unused) { 117 return (kern_return_t)EINVAL; 118} 119 120/* 121 * chud 122 * 123 * Performs performance-related tasks. A private interface registers a handler for this 124 * system call. The implementation is in the CHUDProf kernel extension. 125 * 126 * chud() is a callback style system call used by the CHUD Tools suite of performance tools. If the CHUD 127 * kexts are not loaded, this system call will always return EINVAL. The CHUD kexts contain the 128 * implementation of the system call. 129 * 130 * The current behavior of the chud() system call is as follows: 131 * 132 * Parameters: p (ignored) 133 * uap User argument descriptor (see below) 134 * retval return value of fn (the function returned by syscall_callback_fn) 135 * 136 * Indirect parameters: uap->code Selects the operation to do. This is broken down into a 137 * 16-bit facility and a 16-bit action. 138 * 139 * The rest of the indirect parameters depend on the facility and the action that is selected: 140 * 141 * Facility: 1 Amber instruction tracer 142 * Action: 1 Indicate that a new thread has been created. No arguments are used. 143 * 144 * Action: 2 Indicate that a thread is about to exit. No arguments are used. 145 * 146 * Facility: 2 Not Supported for this system call 147 * 148 * Facility: 3 CHUD Trace facility 149 * Action: 1 Record a backtrace of the calling process into the CHUD Trace facility sample 150 * buffer. 151 * 152 * uap->arg1 Number of frames to skip 153 * uap->arg2 Pointer to a uint64_t containing a timestamp for the 154 * beginning of the sample. NULL uses the current time. 155 * uap->arg3 Pointer to a uint64_t containing a timestamp for the end 156 * of the sample. NULL uses the current time. 157 * uap->arg4 Pointer to auxiliary data to be recorded with the sample 158 * uap->arg5 Size of the auxiliary data pointed to by arg4. 159 * 160 * Returns: EINVAL If syscall_callback_fn returns an invalid function 161 * KERN_SUCCESS Success 162 * KERN_FAILURE Generic failure 163 * KERN_NO_SPACE Auxiliary data is too large (only used by Facility: 3) 164 * 165 * Implicit returns: retval return value of fn (the function returned by syscall_callback_fn) 166 */ 167int 168chud(__unused proc_t p, struct chud_args *uap, int32_t *retval) 169{ 170#if CONFIG_MACF 171 int error = mac_system_check_chud(kauth_cred_get()); 172 if (error) 173 return error; 174#endif 175 176 chudxnu_syscall_callback_func_t fn = syscall_callback_fn; 177 178 if(!fn) { 179 return EINVAL; 180 } 181 182 *retval = fn(uap->code, uap->arg1, uap->arg2, uap->arg3, uap->arg4, uap->arg5); 183 184 return 0; 185} 186 187__private_extern__ kern_return_t 188chudxnu_syscall_callback_enter(chudxnu_syscall_callback_func_t func) 189{ 190 if(OSCompareAndSwapPtr(chud_null_syscall, func, 191 (void * volatile *)&syscall_callback_fn)) { 192 return KERN_SUCCESS; 193 } 194 return KERN_FAILURE; 195} 196 197__private_extern__ kern_return_t 198chudxnu_syscall_callback_cancel(void) 199{ 200 chudxnu_syscall_callback_func_t old = syscall_callback_fn; 201 202 while(!OSCompareAndSwapPtr(old, chud_null_syscall, 203 (void * volatile *)&syscall_callback_fn)) { 204 old = syscall_callback_fn; 205 } 206 207 return KERN_SUCCESS; 208} 209 210/* DTrace callback */ 211typedef kern_return_t (*chudxnu_dtrace_callback_t)(uint64_t selector, 212 uint64_t *args, uint32_t count); 213int chudxnu_dtrace_callback(uint64_t selector, uint64_t *args, uint32_t count); 214kern_return_t chudxnu_dtrace_callback_enter(chudxnu_dtrace_callback_t fn); 215void chudxnu_dtrace_callback_cancel(void); 216 217int 218chud_null_dtrace(uint64_t selector, uint64_t *args, uint32_t count); 219 220static chudxnu_dtrace_callback_t 221 dtrace_callback = (chudxnu_dtrace_callback_t) chud_null_dtrace; 222 223int 224chud_null_dtrace(uint64_t selector __unused, uint64_t *args __unused, 225 uint32_t count __unused) { 226 return ENXIO; 227} 228 229int 230chudxnu_dtrace_callback(uint64_t selector, uint64_t *args, uint32_t count) 231{ 232 /* If no callback is hooked up, let's return ENXIO */ 233 int ret = ENXIO; 234 235 /* Make a local stack copy of the function ptr */ 236 chudxnu_dtrace_callback_t fn = dtrace_callback; 237 238 if(fn) { 239 ret = fn(selector, args, count); 240 } 241 242 return ret; 243} 244 245__private_extern__ kern_return_t 246chudxnu_dtrace_callback_enter(chudxnu_dtrace_callback_t fn) 247{ 248 /* Atomically enter the call back */ 249 if(!OSCompareAndSwapPtr(chud_null_dtrace, fn, 250 (void * volatile *) &dtrace_callback)) { 251 return KERN_FAILURE; 252 } 253 254 return KERN_SUCCESS; 255} 256 257__private_extern__ void 258chudxnu_dtrace_callback_cancel(void) 259{ 260 chudxnu_dtrace_callback_t old_fn = dtrace_callback; 261 262 /* Atomically clear the call back */ 263 while(!OSCompareAndSwapPtr(old_fn, chud_null_dtrace, 264 (void * volatile *) &dtrace_callback)) { 265 old_fn = dtrace_callback; 266 } 267} 268 269