xref: /xnu-12377.41.6/bsd/pgo/profile_runtime.c (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
1 /*
2  * Copyright (c) 2014 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 #include <machine/machine_routines.h>
30 #include <sys/sysproto.h>
31 #include <sys/malloc.h>
32 #include <sys/systm.h>
33 #include <sys/pgo.h>
34 #include <sys/kauth.h>
35 #include <security/mac_framework.h>
36 #include <libkern/OSKextLib.h>
37 
38 #ifdef PROFILE
39 
40 static uint64_t
get_size_for_buffer(int flags)41 get_size_for_buffer(int flags)
42 {
43 	/* These __llvm functions are defined in InstrProfiling.h in compiler_rt.  That
44 	 * is a internal header, so we need to re-prototype them here.  */
45 	extern uint64_t __llvm_profile_get_size_for_buffer(void);
46 
47 	return __llvm_profile_get_size_for_buffer();
48 }
49 
50 
51 static int
write_buffer(int flags,char * buffer)52 write_buffer(int flags, char *buffer)
53 {
54 	extern int __llvm_profile_write_buffer(char *Buffer);
55 
56 	return __llvm_profile_write_buffer(buffer);
57 }
58 
59 #endif /* PROFILE */
60 
61 /* this variable is used to signal to the debugger that we'd like it to reset
62  * the counters */
63 int kdp_pgo_reset_counters = 0;
64 
65 /* called in debugger context */
66 kern_return_t
do_pgo_reset_counters(void)67 do_pgo_reset_counters(void)
68 {
69 	OSKextResetPgoCounters();
70 	kdp_pgo_reset_counters = 0;
71 	return KERN_SUCCESS;
72 }
73 
74 static kern_return_t
kextpgo_trap(void)75 kextpgo_trap(void)
76 {
77 	return DebuggerTrapWithState(DBOP_RESET_PGO_COUNTERS, NULL, NULL, NULL, 0, NULL, FALSE, 0, NULL);
78 }
79 
80 static kern_return_t
pgo_reset_counters(void)81 pgo_reset_counters(void)
82 {
83 	kern_return_t r;
84 	boolean_t istate;
85 
86 	OSKextResetPgoCountersLock();
87 
88 	istate = ml_set_interrupts_enabled(FALSE);
89 
90 	kdp_pgo_reset_counters = 1;
91 	r = kextpgo_trap();
92 
93 	ml_set_interrupts_enabled(istate);
94 
95 	OSKextResetPgoCountersUnlock();
96 	return r;
97 }
98 
99 /*
100  * returns:
101  *   EPERM  unless you are root
102  *   EINVAL for invalid args.
103  *   ENOSYS for not implemented
104  *   ERANGE for integer overflow
105  *   ENOENT if kext not found
106  *   ENOTSUP kext does not support PGO
107  *   EIO llvm returned an error.  shouldn't ever happen.
108  */
109 
110 int
grab_pgo_data(struct proc * p,struct grab_pgo_data_args * uap,register_t * retval)111 grab_pgo_data(struct proc *p,
112     struct grab_pgo_data_args *uap,
113     register_t *retval)
114 {
115 	char *buffer = NULL;
116 	uint64_t size64 = 0;
117 	int err = 0;
118 
119 	(void) p;
120 
121 	if (!kauth_cred_issuser(kauth_cred_get())) {
122 		err = EPERM;
123 		goto out;
124 	}
125 
126 #if CONFIG_MACF
127 	err = mac_system_check_info(kauth_cred_get(), "kern.profiling_data");
128 	if (err) {
129 		goto out;
130 	}
131 #endif /* CONFIG_MACF */
132 
133 	if (uap->flags & ~PGO_ALL_FLAGS ||
134 	    uap->size < 0 ||
135 	    (uap->size > 0 && uap->buffer == 0)) {
136 		err = EINVAL;
137 		goto out;
138 	}
139 
140 	if (uap->flags & PGO_HIB) {
141 		err = ENOTSUP;
142 		goto out;
143 	}
144 
145 	if (uap->flags & PGO_RESET_ALL) {
146 		if (uap->flags != PGO_RESET_ALL || uap->uuid || uap->buffer || uap->size) {
147 			err = EINVAL;
148 		} else {
149 			kern_return_t r = pgo_reset_counters();
150 			switch (r) {
151 			case KERN_SUCCESS:
152 				err = 0;
153 				break;
154 			case KERN_OPERATION_TIMED_OUT:
155 				err = ETIMEDOUT;
156 				break;
157 			default:
158 				err = EIO;
159 				break;
160 			}
161 		}
162 		goto out;
163 	}
164 
165 	*retval = 0;
166 
167 	if (uap->uuid) {
168 		uuid_t uuid;
169 		err = copyin(uap->uuid, &uuid, sizeof(uuid));
170 		if (err) {
171 			goto out;
172 		}
173 
174 		if (uap->buffer == 0 && uap->size == 0) {
175 			if (uap->flags & PGO_WAIT_FOR_UNLOAD) {
176 				err = EINVAL;
177 				goto out;
178 			}
179 
180 			err = OSKextGrabPgoData(uuid, &size64, NULL, 0, 0, !!(uap->flags & PGO_METADATA));
181 			if (size64 == 0 && err == 0) {
182 				err = EIO;
183 			}
184 			if (err) {
185 				goto out;
186 			}
187 
188 			ssize_t size = size64;
189 			if (((uint64_t) size) != size64 ||
190 			    size < 0) {
191 				err = ERANGE;
192 				goto out;
193 			}
194 
195 			*retval = size;
196 			err = 0;
197 			goto out;
198 		} else if (!uap->buffer || uap->size <= 0) {
199 			err = EINVAL;
200 			goto out;
201 		} else {
202 			err = OSKextGrabPgoData(uuid, &size64, NULL, 0,
203 			    false,
204 			    !!(uap->flags & PGO_METADATA));
205 
206 			if (size64 == 0 && err == 0) {
207 				err = EIO;
208 			}
209 			if (err) {
210 				goto out;
211 			}
212 
213 			if (uap->size < 0 || (uint64_t)uap->size < size64) {
214 				err = EINVAL;
215 				goto out;
216 			}
217 
218 			buffer = kalloc_data(size64, Z_WAITOK | Z_ZERO);
219 			if (!buffer) {
220 				err = ENOMEM;
221 				goto out;
222 			}
223 
224 			err = OSKextGrabPgoData(uuid, &size64, buffer, size64,
225 			    !!(uap->flags & PGO_WAIT_FOR_UNLOAD),
226 			    !!(uap->flags & PGO_METADATA));
227 			if (err) {
228 				goto out;
229 			}
230 
231 			ssize_t size = size64;
232 			if (((uint64_t) size) != size64 ||
233 			    size < 0) {
234 				err = ERANGE;
235 				goto out;
236 			}
237 
238 			err = copyout(buffer, uap->buffer, size);
239 			if (err) {
240 				goto out;
241 			}
242 
243 			*retval = size;
244 			goto out;
245 		}
246 	}
247 
248 
249 #ifdef PROFILE
250 
251 	size64 = get_size_for_buffer(uap->flags);
252 	ssize_t size = size64;
253 
254 	if (uap->flags & (PGO_WAIT_FOR_UNLOAD | PGO_METADATA)) {
255 		err = EINVAL;
256 		goto out;
257 	}
258 
259 	if (((uint64_t) size) != size64 ||
260 	    size < 0) {
261 		err = ERANGE;
262 		goto out;
263 	}
264 
265 
266 	if (uap->buffer == 0 && uap->size == 0) {
267 		*retval = size;
268 		err = 0;
269 		goto out;
270 	} else if (uap->size < size) {
271 		err = EINVAL;
272 		goto out;
273 	} else {
274 		buffer = kalloc_data(size, Z_WAITOK | Z_ZERO);
275 		if (!buffer) {
276 			err = ENOMEM;
277 			goto out;
278 		}
279 
280 		err = write_buffer(uap->flags, buffer);
281 		if (err) {
282 			err = EIO;
283 			goto out;
284 		}
285 
286 		err = copyout(buffer, uap->buffer, size);
287 		if (err) {
288 			goto out;
289 		}
290 
291 		*retval = size;
292 		goto out;
293 	}
294 
295 #else /* PROFILE */
296 
297 	*retval = -1;
298 	err = ENOSYS;
299 	goto out;
300 
301 #endif /* !PROFILE */
302 
303 out:
304 	if (buffer) {
305 		kfree_data(buffer, size64);
306 	}
307 	if (err) {
308 		*retval = -1;
309 	}
310 	return err;
311 }
312