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