1*1031c584SApple OSS Distributions /*
2*1031c584SApple OSS Distributions * Copyright (c) 2014 Apple Inc. All rights reserved.
3*1031c584SApple OSS Distributions *
4*1031c584SApple OSS Distributions * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5*1031c584SApple OSS Distributions *
6*1031c584SApple OSS Distributions * This file contains Original Code and/or Modifications of Original Code
7*1031c584SApple OSS Distributions * as defined in and that are subject to the Apple Public Source License
8*1031c584SApple OSS Distributions * Version 2.0 (the 'License'). You may not use this file except in
9*1031c584SApple OSS Distributions * compliance with the License. The rights granted to you under the License
10*1031c584SApple OSS Distributions * may not be used to create, or enable the creation or redistribution of,
11*1031c584SApple OSS Distributions * unlawful or unlicensed copies of an Apple operating system, or to
12*1031c584SApple OSS Distributions * circumvent, violate, or enable the circumvention or violation of, any
13*1031c584SApple OSS Distributions * terms of an Apple operating system software license agreement.
14*1031c584SApple OSS Distributions *
15*1031c584SApple OSS Distributions * Please obtain a copy of the License at
16*1031c584SApple OSS Distributions * http://www.opensource.apple.com/apsl/ and read it before using this file.
17*1031c584SApple OSS Distributions *
18*1031c584SApple OSS Distributions * The Original Code and all software distributed under the License are
19*1031c584SApple OSS Distributions * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20*1031c584SApple OSS Distributions * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21*1031c584SApple OSS Distributions * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22*1031c584SApple OSS Distributions * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23*1031c584SApple OSS Distributions * Please see the License for the specific language governing rights and
24*1031c584SApple OSS Distributions * limitations under the License.
25*1031c584SApple OSS Distributions *
26*1031c584SApple OSS Distributions * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27*1031c584SApple OSS Distributions */
28*1031c584SApple OSS Distributions
29*1031c584SApple OSS Distributions #include <machine/machine_routines.h>
30*1031c584SApple OSS Distributions #include <sys/sysproto.h>
31*1031c584SApple OSS Distributions #include <sys/malloc.h>
32*1031c584SApple OSS Distributions #include <sys/systm.h>
33*1031c584SApple OSS Distributions #include <sys/pgo.h>
34*1031c584SApple OSS Distributions #include <sys/kauth.h>
35*1031c584SApple OSS Distributions #include <security/mac_framework.h>
36*1031c584SApple OSS Distributions #include <libkern/OSKextLib.h>
37*1031c584SApple OSS Distributions
38*1031c584SApple OSS Distributions
39*1031c584SApple OSS Distributions #ifdef PROFILE
40*1031c584SApple OSS Distributions
41*1031c584SApple OSS Distributions /* These __llvm functions are defined in InstrProfiling.h in compiler_rt. That
42*1031c584SApple OSS Distributions * is a internal header, so we need to re-prototype them here. */
43*1031c584SApple OSS Distributions
44*1031c584SApple OSS Distributions uint64_t __llvm_profile_get_size_for_buffer(void);
45*1031c584SApple OSS Distributions int __llvm_profile_write_buffer(char *Buffer);
46*1031c584SApple OSS Distributions uint64_t __llvm_profile_get_size_for_buffer_internal(const char *DataBegin,
47*1031c584SApple OSS Distributions const char *DataEnd,
48*1031c584SApple OSS Distributions const char *CountersBegin,
49*1031c584SApple OSS Distributions const char *CountersEnd,
50*1031c584SApple OSS Distributions const char *NamesBegin,
51*1031c584SApple OSS Distributions const char *NamesEnd);
52*1031c584SApple OSS Distributions int __llvm_profile_write_buffer_internal(char *Buffer,
53*1031c584SApple OSS Distributions const char *DataBegin,
54*1031c584SApple OSS Distributions const char *DataEnd,
55*1031c584SApple OSS Distributions const char *CountersBegin,
56*1031c584SApple OSS Distributions const char *CountersEnd,
57*1031c584SApple OSS Distributions const char *NamesBegin,
58*1031c584SApple OSS Distributions const char *NamesEnd);
59*1031c584SApple OSS Distributions
60*1031c584SApple OSS Distributions extern char __pgo_hib_DataStart __asm("section$start$__HIB$__llvm_prf_data");
61*1031c584SApple OSS Distributions extern char __pgo_hib_DataEnd __asm("section$end$__HIB$__llvm_prf_data");
62*1031c584SApple OSS Distributions extern char __pgo_hib_NamesStart __asm("section$start$__HIB$__llvm_prf_names");
63*1031c584SApple OSS Distributions extern char __pgo_hib_NamesEnd __asm("section$end$__HIB$__llvm_prf_names");
64*1031c584SApple OSS Distributions extern char __pgo_hib_CountersStart __asm("section$start$__HIB$__llvm_prf_cnts");
65*1031c584SApple OSS Distributions extern char __pgo_hib_CountersEnd __asm("section$end$__HIB$__llvm_prf_cnts");
66*1031c584SApple OSS Distributions
67*1031c584SApple OSS Distributions
68*1031c584SApple OSS Distributions static uint64_t
get_size_for_buffer(int flags)69*1031c584SApple OSS Distributions get_size_for_buffer(int flags)
70*1031c584SApple OSS Distributions {
71*1031c584SApple OSS Distributions if (flags & PGO_HIB) {
72*1031c584SApple OSS Distributions return __llvm_profile_get_size_for_buffer_internal(
73*1031c584SApple OSS Distributions &__pgo_hib_DataStart, &__pgo_hib_DataEnd,
74*1031c584SApple OSS Distributions &__pgo_hib_CountersStart, &__pgo_hib_CountersEnd,
75*1031c584SApple OSS Distributions &__pgo_hib_NamesStart, &__pgo_hib_NamesEnd);
76*1031c584SApple OSS Distributions } else {
77*1031c584SApple OSS Distributions return __llvm_profile_get_size_for_buffer();
78*1031c584SApple OSS Distributions }
79*1031c584SApple OSS Distributions }
80*1031c584SApple OSS Distributions
81*1031c584SApple OSS Distributions
82*1031c584SApple OSS Distributions static int
write_buffer(int flags,char * buffer)83*1031c584SApple OSS Distributions write_buffer(int flags, char *buffer)
84*1031c584SApple OSS Distributions {
85*1031c584SApple OSS Distributions if (flags & PGO_HIB) {
86*1031c584SApple OSS Distributions return __llvm_profile_write_buffer_internal(
87*1031c584SApple OSS Distributions buffer,
88*1031c584SApple OSS Distributions &__pgo_hib_DataStart, &__pgo_hib_DataEnd,
89*1031c584SApple OSS Distributions &__pgo_hib_CountersStart, &__pgo_hib_CountersEnd,
90*1031c584SApple OSS Distributions &__pgo_hib_NamesStart, &__pgo_hib_NamesEnd);
91*1031c584SApple OSS Distributions } else {
92*1031c584SApple OSS Distributions return __llvm_profile_write_buffer(buffer);
93*1031c584SApple OSS Distributions }
94*1031c584SApple OSS Distributions }
95*1031c584SApple OSS Distributions
96*1031c584SApple OSS Distributions
97*1031c584SApple OSS Distributions #endif
98*1031c584SApple OSS Distributions
99*1031c584SApple OSS Distributions /* this variable is used to signal to the debugger that we'd like it to reset
100*1031c584SApple OSS Distributions * the counters */
101*1031c584SApple OSS Distributions int kdp_pgo_reset_counters = 0;
102*1031c584SApple OSS Distributions
103*1031c584SApple OSS Distributions /* called in debugger context */
104*1031c584SApple OSS Distributions kern_return_t
do_pgo_reset_counters()105*1031c584SApple OSS Distributions do_pgo_reset_counters()
106*1031c584SApple OSS Distributions {
107*1031c584SApple OSS Distributions #ifdef PROFILE
108*1031c584SApple OSS Distributions memset(&__pgo_hib_CountersStart, 0,
109*1031c584SApple OSS Distributions ((uintptr_t)(&__pgo_hib_CountersEnd)) - ((uintptr_t)(&__pgo_hib_CountersStart)));
110*1031c584SApple OSS Distributions #endif
111*1031c584SApple OSS Distributions OSKextResetPgoCounters();
112*1031c584SApple OSS Distributions kdp_pgo_reset_counters = 0;
113*1031c584SApple OSS Distributions return KERN_SUCCESS;
114*1031c584SApple OSS Distributions }
115*1031c584SApple OSS Distributions
116*1031c584SApple OSS Distributions static kern_return_t
kextpgo_trap()117*1031c584SApple OSS Distributions kextpgo_trap()
118*1031c584SApple OSS Distributions {
119*1031c584SApple OSS Distributions return DebuggerTrapWithState(DBOP_RESET_PGO_COUNTERS, NULL, NULL, NULL, 0, NULL, FALSE, 0);
120*1031c584SApple OSS Distributions }
121*1031c584SApple OSS Distributions
122*1031c584SApple OSS Distributions static kern_return_t
pgo_reset_counters()123*1031c584SApple OSS Distributions pgo_reset_counters()
124*1031c584SApple OSS Distributions {
125*1031c584SApple OSS Distributions kern_return_t r;
126*1031c584SApple OSS Distributions boolean_t istate;
127*1031c584SApple OSS Distributions
128*1031c584SApple OSS Distributions OSKextResetPgoCountersLock();
129*1031c584SApple OSS Distributions
130*1031c584SApple OSS Distributions istate = ml_set_interrupts_enabled(FALSE);
131*1031c584SApple OSS Distributions
132*1031c584SApple OSS Distributions kdp_pgo_reset_counters = 1;
133*1031c584SApple OSS Distributions r = kextpgo_trap();
134*1031c584SApple OSS Distributions
135*1031c584SApple OSS Distributions ml_set_interrupts_enabled(istate);
136*1031c584SApple OSS Distributions
137*1031c584SApple OSS Distributions OSKextResetPgoCountersUnlock();
138*1031c584SApple OSS Distributions return r;
139*1031c584SApple OSS Distributions }
140*1031c584SApple OSS Distributions
141*1031c584SApple OSS Distributions
142*1031c584SApple OSS Distributions /*
143*1031c584SApple OSS Distributions * returns:
144*1031c584SApple OSS Distributions * EPERM unless you are root
145*1031c584SApple OSS Distributions * EINVAL for invalid args.
146*1031c584SApple OSS Distributions * ENOSYS for not implemented
147*1031c584SApple OSS Distributions * ERANGE for integer overflow
148*1031c584SApple OSS Distributions * ENOENT if kext not found
149*1031c584SApple OSS Distributions * ENOTSUP kext does not support PGO
150*1031c584SApple OSS Distributions * EIO llvm returned an error. shouldn't ever happen.
151*1031c584SApple OSS Distributions */
152*1031c584SApple OSS Distributions
153*1031c584SApple OSS Distributions int
grab_pgo_data(struct proc * p,struct grab_pgo_data_args * uap,register_t * retval)154*1031c584SApple OSS Distributions grab_pgo_data(struct proc *p,
155*1031c584SApple OSS Distributions struct grab_pgo_data_args *uap,
156*1031c584SApple OSS Distributions register_t *retval)
157*1031c584SApple OSS Distributions {
158*1031c584SApple OSS Distributions char *buffer = NULL;
159*1031c584SApple OSS Distributions uint64_t size64 = 0;
160*1031c584SApple OSS Distributions int err = 0;
161*1031c584SApple OSS Distributions
162*1031c584SApple OSS Distributions (void) p;
163*1031c584SApple OSS Distributions
164*1031c584SApple OSS Distributions if (!kauth_cred_issuser(kauth_cred_get())) {
165*1031c584SApple OSS Distributions err = EPERM;
166*1031c584SApple OSS Distributions goto out;
167*1031c584SApple OSS Distributions }
168*1031c584SApple OSS Distributions
169*1031c584SApple OSS Distributions #if CONFIG_MACF
170*1031c584SApple OSS Distributions err = mac_system_check_info(kauth_cred_get(), "kern.profiling_data");
171*1031c584SApple OSS Distributions if (err) {
172*1031c584SApple OSS Distributions goto out;
173*1031c584SApple OSS Distributions }
174*1031c584SApple OSS Distributions #endif
175*1031c584SApple OSS Distributions
176*1031c584SApple OSS Distributions if (uap->flags & ~PGO_ALL_FLAGS ||
177*1031c584SApple OSS Distributions uap->size < 0 ||
178*1031c584SApple OSS Distributions (uap->size > 0 && uap->buffer == 0)) {
179*1031c584SApple OSS Distributions err = EINVAL;
180*1031c584SApple OSS Distributions goto out;
181*1031c584SApple OSS Distributions }
182*1031c584SApple OSS Distributions
183*1031c584SApple OSS Distributions if (uap->flags & PGO_RESET_ALL) {
184*1031c584SApple OSS Distributions if (uap->flags != PGO_RESET_ALL || uap->uuid || uap->buffer || uap->size) {
185*1031c584SApple OSS Distributions err = EINVAL;
186*1031c584SApple OSS Distributions } else {
187*1031c584SApple OSS Distributions kern_return_t r = pgo_reset_counters();
188*1031c584SApple OSS Distributions switch (r) {
189*1031c584SApple OSS Distributions case KERN_SUCCESS:
190*1031c584SApple OSS Distributions err = 0;
191*1031c584SApple OSS Distributions break;
192*1031c584SApple OSS Distributions case KERN_OPERATION_TIMED_OUT:
193*1031c584SApple OSS Distributions err = ETIMEDOUT;
194*1031c584SApple OSS Distributions break;
195*1031c584SApple OSS Distributions default:
196*1031c584SApple OSS Distributions err = EIO;
197*1031c584SApple OSS Distributions break;
198*1031c584SApple OSS Distributions }
199*1031c584SApple OSS Distributions }
200*1031c584SApple OSS Distributions goto out;
201*1031c584SApple OSS Distributions }
202*1031c584SApple OSS Distributions
203*1031c584SApple OSS Distributions *retval = 0;
204*1031c584SApple OSS Distributions
205*1031c584SApple OSS Distributions if (uap->uuid) {
206*1031c584SApple OSS Distributions uuid_t uuid;
207*1031c584SApple OSS Distributions err = copyin(uap->uuid, &uuid, sizeof(uuid));
208*1031c584SApple OSS Distributions if (err) {
209*1031c584SApple OSS Distributions goto out;
210*1031c584SApple OSS Distributions }
211*1031c584SApple OSS Distributions
212*1031c584SApple OSS Distributions if (uap->buffer == 0 && uap->size == 0) {
213*1031c584SApple OSS Distributions if (uap->flags & PGO_WAIT_FOR_UNLOAD) {
214*1031c584SApple OSS Distributions err = EINVAL;
215*1031c584SApple OSS Distributions goto out;
216*1031c584SApple OSS Distributions }
217*1031c584SApple OSS Distributions
218*1031c584SApple OSS Distributions err = OSKextGrabPgoData(uuid, &size64, NULL, 0, 0, !!(uap->flags & PGO_METADATA));
219*1031c584SApple OSS Distributions if (size64 == 0 && err == 0) {
220*1031c584SApple OSS Distributions err = EIO;
221*1031c584SApple OSS Distributions }
222*1031c584SApple OSS Distributions if (err) {
223*1031c584SApple OSS Distributions goto out;
224*1031c584SApple OSS Distributions }
225*1031c584SApple OSS Distributions
226*1031c584SApple OSS Distributions ssize_t size = size64;
227*1031c584SApple OSS Distributions if (((uint64_t) size) != size64 ||
228*1031c584SApple OSS Distributions size < 0) {
229*1031c584SApple OSS Distributions err = ERANGE;
230*1031c584SApple OSS Distributions goto out;
231*1031c584SApple OSS Distributions }
232*1031c584SApple OSS Distributions
233*1031c584SApple OSS Distributions *retval = size;
234*1031c584SApple OSS Distributions err = 0;
235*1031c584SApple OSS Distributions goto out;
236*1031c584SApple OSS Distributions } else if (!uap->buffer || uap->size <= 0) {
237*1031c584SApple OSS Distributions err = EINVAL;
238*1031c584SApple OSS Distributions goto out;
239*1031c584SApple OSS Distributions } else {
240*1031c584SApple OSS Distributions err = OSKextGrabPgoData(uuid, &size64, NULL, 0,
241*1031c584SApple OSS Distributions false,
242*1031c584SApple OSS Distributions !!(uap->flags & PGO_METADATA));
243*1031c584SApple OSS Distributions
244*1031c584SApple OSS Distributions if (size64 == 0 && err == 0) {
245*1031c584SApple OSS Distributions err = EIO;
246*1031c584SApple OSS Distributions }
247*1031c584SApple OSS Distributions if (err) {
248*1031c584SApple OSS Distributions goto out;
249*1031c584SApple OSS Distributions }
250*1031c584SApple OSS Distributions
251*1031c584SApple OSS Distributions if (uap->size < 0 || (uint64_t)uap->size < size64) {
252*1031c584SApple OSS Distributions err = EINVAL;
253*1031c584SApple OSS Distributions goto out;
254*1031c584SApple OSS Distributions }
255*1031c584SApple OSS Distributions
256*1031c584SApple OSS Distributions buffer = kalloc_data(size64, Z_WAITOK | Z_ZERO);
257*1031c584SApple OSS Distributions if (!buffer) {
258*1031c584SApple OSS Distributions err = ENOMEM;
259*1031c584SApple OSS Distributions goto out;
260*1031c584SApple OSS Distributions }
261*1031c584SApple OSS Distributions
262*1031c584SApple OSS Distributions err = OSKextGrabPgoData(uuid, &size64, buffer, size64,
263*1031c584SApple OSS Distributions !!(uap->flags & PGO_WAIT_FOR_UNLOAD),
264*1031c584SApple OSS Distributions !!(uap->flags & PGO_METADATA));
265*1031c584SApple OSS Distributions if (err) {
266*1031c584SApple OSS Distributions goto out;
267*1031c584SApple OSS Distributions }
268*1031c584SApple OSS Distributions
269*1031c584SApple OSS Distributions ssize_t size = size64;
270*1031c584SApple OSS Distributions if (((uint64_t) size) != size64 ||
271*1031c584SApple OSS Distributions size < 0) {
272*1031c584SApple OSS Distributions err = ERANGE;
273*1031c584SApple OSS Distributions goto out;
274*1031c584SApple OSS Distributions }
275*1031c584SApple OSS Distributions
276*1031c584SApple OSS Distributions err = copyout(buffer, uap->buffer, size);
277*1031c584SApple OSS Distributions if (err) {
278*1031c584SApple OSS Distributions goto out;
279*1031c584SApple OSS Distributions }
280*1031c584SApple OSS Distributions
281*1031c584SApple OSS Distributions *retval = size;
282*1031c584SApple OSS Distributions goto out;
283*1031c584SApple OSS Distributions }
284*1031c584SApple OSS Distributions }
285*1031c584SApple OSS Distributions
286*1031c584SApple OSS Distributions
287*1031c584SApple OSS Distributions #ifdef PROFILE
288*1031c584SApple OSS Distributions
289*1031c584SApple OSS Distributions size64 = get_size_for_buffer(uap->flags);
290*1031c584SApple OSS Distributions ssize_t size = size64;
291*1031c584SApple OSS Distributions
292*1031c584SApple OSS Distributions if (uap->flags & (PGO_WAIT_FOR_UNLOAD | PGO_METADATA)) {
293*1031c584SApple OSS Distributions err = EINVAL;
294*1031c584SApple OSS Distributions goto out;
295*1031c584SApple OSS Distributions }
296*1031c584SApple OSS Distributions
297*1031c584SApple OSS Distributions if (((uint64_t) size) != size64 ||
298*1031c584SApple OSS Distributions size < 0) {
299*1031c584SApple OSS Distributions err = ERANGE;
300*1031c584SApple OSS Distributions goto out;
301*1031c584SApple OSS Distributions }
302*1031c584SApple OSS Distributions
303*1031c584SApple OSS Distributions
304*1031c584SApple OSS Distributions if (uap->buffer == 0 && uap->size == 0) {
305*1031c584SApple OSS Distributions *retval = size;
306*1031c584SApple OSS Distributions err = 0;
307*1031c584SApple OSS Distributions goto out;
308*1031c584SApple OSS Distributions } else if (uap->size < size) {
309*1031c584SApple OSS Distributions err = EINVAL;
310*1031c584SApple OSS Distributions goto out;
311*1031c584SApple OSS Distributions } else {
312*1031c584SApple OSS Distributions buffer = kalloc_data(size, Z_WAITOK | Z_ZERO);
313*1031c584SApple OSS Distributions if (!buffer) {
314*1031c584SApple OSS Distributions err = ENOMEM;
315*1031c584SApple OSS Distributions goto out;
316*1031c584SApple OSS Distributions }
317*1031c584SApple OSS Distributions
318*1031c584SApple OSS Distributions err = write_buffer(uap->flags, buffer);
319*1031c584SApple OSS Distributions if (err) {
320*1031c584SApple OSS Distributions err = EIO;
321*1031c584SApple OSS Distributions goto out;
322*1031c584SApple OSS Distributions }
323*1031c584SApple OSS Distributions
324*1031c584SApple OSS Distributions err = copyout(buffer, uap->buffer, size);
325*1031c584SApple OSS Distributions if (err) {
326*1031c584SApple OSS Distributions goto out;
327*1031c584SApple OSS Distributions }
328*1031c584SApple OSS Distributions
329*1031c584SApple OSS Distributions *retval = size;
330*1031c584SApple OSS Distributions goto out;
331*1031c584SApple OSS Distributions }
332*1031c584SApple OSS Distributions
333*1031c584SApple OSS Distributions #else
334*1031c584SApple OSS Distributions
335*1031c584SApple OSS Distributions *retval = -1;
336*1031c584SApple OSS Distributions err = ENOSYS;
337*1031c584SApple OSS Distributions goto out;
338*1031c584SApple OSS Distributions
339*1031c584SApple OSS Distributions #endif
340*1031c584SApple OSS Distributions
341*1031c584SApple OSS Distributions out:
342*1031c584SApple OSS Distributions if (buffer) {
343*1031c584SApple OSS Distributions kfree_data(buffer, size64);
344*1031c584SApple OSS Distributions }
345*1031c584SApple OSS Distributions if (err) {
346*1031c584SApple OSS Distributions *retval = -1;
347*1031c584SApple OSS Distributions }
348*1031c584SApple OSS Distributions return err;
349*1031c584SApple OSS Distributions }
350