]> git.saurik.com Git - apple/xnu.git/blob - bsd/dev/dtrace/dtrace_glue.c
d33a8f03084c6b81ad2b130d91aa4ca5c42e9d13
[apple/xnu.git] / bsd / dev / dtrace / dtrace_glue.c
1 /*
2 * Copyright (c) 2005-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
30 /*
31 * APPLE NOTE: This file is compiled even if dtrace is unconfig'd. A symbol
32 * from this file (_dtrace_register_anon_DOF) always needs to be exported for
33 * an external kext to link against.
34 */
35
36 #if CONFIG_DTRACE
37
38 #define MACH__POSIX_C_SOURCE_PRIVATE 1 /* pulls in suitable savearea from mach/ppc/thread_status.h */
39 #include <kern/thread.h>
40 #include <mach/thread_status.h>
41
42 #include <stdarg.h>
43 #include <string.h>
44 #include <sys/malloc.h>
45 #include <sys/time.h>
46 #include <sys/proc.h>
47 #include <sys/proc_internal.h>
48 #include <sys/kauth.h>
49 #include <sys/user.h>
50 #include <sys/systm.h>
51 #include <sys/dtrace.h>
52 #include <sys/dtrace_impl.h>
53 #include <libkern/OSAtomic.h>
54 #include <libkern/OSKextLibPrivate.h>
55 #include <kern/kern_types.h>
56 #include <kern/timer_call.h>
57 #include <kern/thread_call.h>
58 #include <kern/task.h>
59 #include <kern/sched_prim.h>
60 #include <kern/queue.h>
61 #include <miscfs/devfs/devfs.h>
62 #include <kern/kalloc.h>
63
64 #include <mach/vm_param.h>
65 #include <mach/mach_vm.h>
66 #include <mach/task.h>
67 #include <vm/pmap.h>
68 #include <vm/vm_map.h> /* All the bits we care about are guarded by MACH_KERNEL_PRIVATE :-( */
69
70 /*
71 * pid/proc
72 */
73 /* Solaris proc_t is the struct. Darwin's proc_t is a pointer to it. */
74 #define proc_t struct proc /* Steer clear of the Darwin typedef for proc_t */
75
76 void
77 dtrace_sprlock(proc_t *p)
78 {
79 lck_mtx_assert(&p->p_mlock, LCK_MTX_ASSERT_NOTOWNED);
80 lck_mtx_lock(&p->p_dtrace_sprlock);
81 }
82
83 void
84 dtrace_sprunlock(proc_t *p)
85 {
86 lck_mtx_unlock(&p->p_dtrace_sprlock);
87 }
88
89 /* Not called from probe context */
90 proc_t *
91 sprlock(pid_t pid)
92 {
93 proc_t* p;
94
95 if ((p = proc_find(pid)) == PROC_NULL) {
96 return PROC_NULL;
97 }
98
99 task_suspend_internal(p->task);
100
101 dtrace_sprlock(p);
102
103 proc_lock(p);
104
105 return p;
106 }
107
108 /* Not called from probe context */
109 void
110 sprunlock(proc_t *p)
111 {
112 if (p != PROC_NULL) {
113 proc_unlock(p);
114
115 dtrace_sprunlock(p);
116
117 task_resume_internal(p->task);
118
119 proc_rele(p);
120 }
121 }
122
123 /*
124 * uread/uwrite
125 */
126
127 // These are not exported from vm_map.h.
128 extern kern_return_t vm_map_read_user(vm_map_t map, vm_map_address_t src_addr, void *dst_p, vm_size_t size);
129 extern kern_return_t vm_map_write_user(vm_map_t map, void *src_p, vm_map_address_t dst_addr, vm_size_t size);
130
131 /* Not called from probe context */
132 int
133 uread(proc_t *p, void *buf, user_size_t len, user_addr_t a)
134 {
135 kern_return_t ret;
136
137 ASSERT(p != PROC_NULL);
138 ASSERT(p->task != NULL);
139
140 task_t task = p->task;
141
142 /*
143 * Grab a reference to the task vm_map_t to make sure
144 * the map isn't pulled out from under us.
145 *
146 * Because the proc_lock is not held at all times on all code
147 * paths leading here, it is possible for the proc to have
148 * exited. If the map is null, fail.
149 */
150 vm_map_t map = get_task_map_reference(task);
151 if (map) {
152 ret = vm_map_read_user( map, (vm_map_address_t)a, buf, (vm_size_t)len);
153 vm_map_deallocate(map);
154 } else {
155 ret = KERN_TERMINATED;
156 }
157
158 return (int)ret;
159 }
160
161
162 /* Not called from probe context */
163 int
164 uwrite(proc_t *p, void *buf, user_size_t len, user_addr_t a)
165 {
166 kern_return_t ret;
167
168 ASSERT(p != NULL);
169 ASSERT(p->task != NULL);
170
171 task_t task = p->task;
172
173 /*
174 * Grab a reference to the task vm_map_t to make sure
175 * the map isn't pulled out from under us.
176 *
177 * Because the proc_lock is not held at all times on all code
178 * paths leading here, it is possible for the proc to have
179 * exited. If the map is null, fail.
180 */
181 vm_map_t map = get_task_map_reference(task);
182 if (map) {
183 /* Find the memory permissions. */
184 uint32_t nestingDepth = 999999;
185 vm_region_submap_short_info_data_64_t info;
186 mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
187 mach_vm_address_t address = (mach_vm_address_t)a;
188 mach_vm_size_t sizeOfRegion = (mach_vm_size_t)len;
189
190 ret = mach_vm_region_recurse(map, &address, &sizeOfRegion, &nestingDepth, (vm_region_recurse_info_t)&info, &count);
191 if (ret != KERN_SUCCESS) {
192 goto done;
193 }
194
195 vm_prot_t reprotect;
196
197 if (!(info.protection & VM_PROT_WRITE)) {
198 /* Save the original protection values for restoration later */
199 reprotect = info.protection;
200
201 if (info.max_protection & VM_PROT_WRITE) {
202 /* The memory is not currently writable, but can be made writable. */
203 ret = mach_vm_protect(map, (mach_vm_offset_t)a, (mach_vm_size_t)len, 0, (reprotect & ~VM_PROT_EXECUTE) | VM_PROT_WRITE);
204 } else {
205 /*
206 * The memory is not currently writable, and cannot be made writable. We need to COW this memory.
207 *
208 * Strange, we can't just say "reprotect | VM_PROT_COPY", that fails.
209 */
210 ret = mach_vm_protect(map, (mach_vm_offset_t)a, (mach_vm_size_t)len, 0, VM_PROT_COPY | VM_PROT_READ | VM_PROT_WRITE);
211 }
212
213 if (ret != KERN_SUCCESS) {
214 goto done;
215 }
216 } else {
217 /* The memory was already writable. */
218 reprotect = VM_PROT_NONE;
219 }
220
221 ret = vm_map_write_user( map,
222 buf,
223 (vm_map_address_t)a,
224 (vm_size_t)len);
225
226 dtrace_flush_caches();
227
228 if (ret != KERN_SUCCESS) {
229 goto done;
230 }
231
232 if (reprotect != VM_PROT_NONE) {
233 ASSERT(reprotect & VM_PROT_EXECUTE);
234 ret = mach_vm_protect(map, (mach_vm_offset_t)a, (mach_vm_size_t)len, 0, reprotect);
235 }
236
237 done:
238 vm_map_deallocate(map);
239 } else {
240 ret = KERN_TERMINATED;
241 }
242
243 return (int)ret;
244 }
245
246 /*
247 * cpuvar
248 */
249 lck_mtx_t cpu_lock;
250 lck_mtx_t cyc_lock;
251 lck_mtx_t mod_lock;
252
253 dtrace_cpu_t *cpu_list;
254 cpu_core_t *cpu_core; /* XXX TLB lockdown? */
255
256 /*
257 * cred_t
258 */
259
260 /*
261 * dtrace_CRED() can be called from probe context. We cannot simply call kauth_cred_get() since
262 * that function may try to resolve a lazy credential binding, which entails taking the proc_lock.
263 */
264 cred_t *
265 dtrace_CRED(void)
266 {
267 struct uthread *uthread = get_bsdthread_info(current_thread());
268
269 if (uthread == NULL) {
270 return NULL;
271 } else {
272 return uthread->uu_ucred; /* May return NOCRED which is defined to be 0 */
273 }
274 }
275
276 #define HAS_ALLPRIVS(cr) priv_isfullset(&CR_OEPRIV(cr))
277 #define HAS_PRIVILEGE(cr, pr) ((pr) == PRIV_ALL ? \
278 HAS_ALLPRIVS(cr) : \
279 PRIV_ISASSERT(&CR_OEPRIV(cr), pr))
280
281 int
282 PRIV_POLICY_CHOICE(void* cred, int priv, int all)
283 {
284 #pragma unused(priv, all)
285 return kauth_cred_issuser(cred); /* XXX TODO: How is this different from PRIV_POLICY_ONLY? */
286 }
287
288 int
289 PRIV_POLICY_ONLY(void *cr, int priv, int boolean)
290 {
291 #pragma unused(priv, boolean)
292 return kauth_cred_issuser(cr); /* XXX TODO: HAS_PRIVILEGE(cr, priv); */
293 }
294
295 uid_t
296 crgetuid(const cred_t *cr)
297 {
298 cred_t copy_cr = *cr; return kauth_cred_getuid(&copy_cr);
299 }
300
301 /*
302 * "cyclic"
303 */
304
305 typedef struct wrap_timer_call {
306 /* node attributes */
307 cyc_handler_t hdlr;
308 cyc_time_t when;
309 uint64_t deadline;
310 int cpuid;
311 boolean_t suspended;
312 struct timer_call call;
313
314 /* next item in the linked list */
315 LIST_ENTRY(wrap_timer_call) entries;
316 } wrap_timer_call_t;
317
318 #define WAKEUP_REAPER 0x7FFFFFFFFFFFFFFFLL
319 #define NEARLY_FOREVER 0x7FFFFFFFFFFFFFFELL
320
321
322 typedef struct cyc_list {
323 cyc_omni_handler_t cyl_omni;
324 wrap_timer_call_t cyl_wrap_by_cpus[];
325 #if __arm__ && (__BIGGEST_ALIGNMENT__ > 4)
326 } __attribute__ ((aligned(8))) cyc_list_t;
327 #else
328 } cyc_list_t;
329 #endif
330
331 /* CPU going online/offline notifications */
332 void (*dtrace_cpu_state_changed_hook)(int, boolean_t) = NULL;
333 void dtrace_cpu_state_changed(int, boolean_t);
334
335 void
336 dtrace_install_cpu_hooks(void)
337 {
338 dtrace_cpu_state_changed_hook = dtrace_cpu_state_changed;
339 }
340
341 void
342 dtrace_cpu_state_changed(int cpuid, boolean_t is_running)
343 {
344 #pragma unused(cpuid)
345 wrap_timer_call_t *wrapTC = NULL;
346 boolean_t suspend = (is_running ? FALSE : TRUE);
347 dtrace_icookie_t s;
348
349 /* Ensure that we're not going to leave the CPU */
350 s = dtrace_interrupt_disable();
351 assert(cpuid == cpu_number());
352
353 LIST_FOREACH(wrapTC, &(cpu_list[cpu_number()].cpu_cyc_list), entries) {
354 assert(wrapTC->cpuid == cpu_number());
355 if (suspend) {
356 assert(!wrapTC->suspended);
357 /* If this fails, we'll panic anyway, so let's do this now. */
358 if (!timer_call_cancel(&wrapTC->call)) {
359 panic("timer_call_set_suspend() failed to cancel a timer call");
360 }
361 wrapTC->suspended = TRUE;
362 } else {
363 /* Rearm the timer, but ensure it was suspended first. */
364 assert(wrapTC->suspended);
365 clock_deadline_for_periodic_event(wrapTC->when.cyt_interval, mach_absolute_time(),
366 &wrapTC->deadline);
367 timer_call_enter1(&wrapTC->call, (void*) wrapTC, wrapTC->deadline,
368 TIMER_CALL_SYS_CRITICAL | TIMER_CALL_LOCAL);
369 wrapTC->suspended = FALSE;
370 }
371 }
372
373 /* Restore the previous interrupt state. */
374 dtrace_interrupt_enable(s);
375 }
376
377 static void
378 _timer_call_apply_cyclic( void *ignore, void *vTChdl )
379 {
380 #pragma unused(ignore)
381 wrap_timer_call_t *wrapTC = (wrap_timer_call_t *)vTChdl;
382
383 (*(wrapTC->hdlr.cyh_func))( wrapTC->hdlr.cyh_arg );
384
385 clock_deadline_for_periodic_event( wrapTC->when.cyt_interval, mach_absolute_time(), &(wrapTC->deadline));
386 timer_call_enter1( &(wrapTC->call), (void *)wrapTC, wrapTC->deadline, TIMER_CALL_SYS_CRITICAL | TIMER_CALL_LOCAL );
387 }
388
389 static cyclic_id_t
390 timer_call_add_cyclic(wrap_timer_call_t *wrapTC, cyc_handler_t *handler, cyc_time_t *when)
391 {
392 uint64_t now;
393 dtrace_icookie_t s;
394
395 timer_call_setup( &(wrapTC->call), _timer_call_apply_cyclic, NULL );
396 wrapTC->hdlr = *handler;
397 wrapTC->when = *when;
398
399 nanoseconds_to_absolutetime( wrapTC->when.cyt_interval, (uint64_t *)&wrapTC->when.cyt_interval );
400
401 now = mach_absolute_time();
402 wrapTC->deadline = now;
403
404 clock_deadline_for_periodic_event( wrapTC->when.cyt_interval, now, &(wrapTC->deadline));
405
406 /* Insert the timer to the list of the running timers on this CPU, and start it. */
407 s = dtrace_interrupt_disable();
408 wrapTC->cpuid = cpu_number();
409 LIST_INSERT_HEAD(&cpu_list[wrapTC->cpuid].cpu_cyc_list, wrapTC, entries);
410 timer_call_enter1(&wrapTC->call, (void*) wrapTC, wrapTC->deadline,
411 TIMER_CALL_SYS_CRITICAL | TIMER_CALL_LOCAL);
412 wrapTC->suspended = FALSE;
413 dtrace_interrupt_enable(s);
414
415 return (cyclic_id_t)wrapTC;
416 }
417
418 /*
419 * Executed on the CPU the timer is running on.
420 */
421 static void
422 timer_call_remove_cyclic(wrap_timer_call_t *wrapTC)
423 {
424 assert(wrapTC);
425 assert(cpu_number() == wrapTC->cpuid);
426
427 if (!timer_call_cancel(&wrapTC->call)) {
428 panic("timer_call_remove_cyclic() failed to cancel a timer call");
429 }
430
431 LIST_REMOVE(wrapTC, entries);
432 }
433
434 static void *
435 timer_call_get_cyclic_arg(wrap_timer_call_t *wrapTC)
436 {
437 return wrapTC ? wrapTC->hdlr.cyh_arg : NULL;
438 }
439
440 cyclic_id_t
441 cyclic_timer_add(cyc_handler_t *handler, cyc_time_t *when)
442 {
443 wrap_timer_call_t *wrapTC = _MALLOC(sizeof(wrap_timer_call_t), M_TEMP, M_ZERO | M_WAITOK);
444 if (NULL == wrapTC) {
445 return CYCLIC_NONE;
446 } else {
447 return timer_call_add_cyclic( wrapTC, handler, when );
448 }
449 }
450
451 void
452 cyclic_timer_remove(cyclic_id_t cyclic)
453 {
454 ASSERT( cyclic != CYCLIC_NONE );
455
456 /* Removing a timer call must be done on the CPU the timer is running on. */
457 wrap_timer_call_t *wrapTC = (wrap_timer_call_t *) cyclic;
458 dtrace_xcall(wrapTC->cpuid, (dtrace_xcall_t) timer_call_remove_cyclic, (void*) cyclic);
459
460 _FREE((void *)cyclic, M_TEMP);
461 }
462
463 static void
464 _cyclic_add_omni(cyc_list_t *cyc_list)
465 {
466 cyc_time_t cT;
467 cyc_handler_t cH;
468 cyc_omni_handler_t *omni = &cyc_list->cyl_omni;
469
470 (omni->cyo_online)(omni->cyo_arg, CPU, &cH, &cT);
471
472 wrap_timer_call_t *wrapTC = &cyc_list->cyl_wrap_by_cpus[cpu_number()];
473 timer_call_add_cyclic(wrapTC, &cH, &cT);
474 }
475
476 cyclic_id_list_t
477 cyclic_add_omni(cyc_omni_handler_t *omni)
478 {
479 cyc_list_t *cyc_list =
480 _MALLOC(sizeof(cyc_list_t) + NCPU * sizeof(wrap_timer_call_t), M_TEMP, M_ZERO | M_WAITOK);
481
482 if (NULL == cyc_list) {
483 return NULL;
484 }
485
486 cyc_list->cyl_omni = *omni;
487
488 dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)_cyclic_add_omni, (void *)cyc_list);
489
490 return (cyclic_id_list_t)cyc_list;
491 }
492
493 static void
494 _cyclic_remove_omni(cyc_list_t *cyc_list)
495 {
496 cyc_omni_handler_t *omni = &cyc_list->cyl_omni;
497 void *oarg;
498 wrap_timer_call_t *wrapTC;
499
500 /*
501 * If the processor was offline when dtrace started, we did not allocate
502 * a cyclic timer for this CPU.
503 */
504 if ((wrapTC = &cyc_list->cyl_wrap_by_cpus[cpu_number()]) != NULL) {
505 oarg = timer_call_get_cyclic_arg(wrapTC);
506 timer_call_remove_cyclic(wrapTC);
507 (omni->cyo_offline)(omni->cyo_arg, CPU, oarg);
508 }
509 }
510
511 void
512 cyclic_remove_omni(cyclic_id_list_t cyc_list)
513 {
514 ASSERT(cyc_list != NULL);
515
516 dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)_cyclic_remove_omni, (void *)cyc_list);
517 _FREE(cyc_list, M_TEMP);
518 }
519
520 typedef struct wrap_thread_call {
521 thread_call_t TChdl;
522 cyc_handler_t hdlr;
523 cyc_time_t when;
524 uint64_t deadline;
525 } wrap_thread_call_t;
526
527 /*
528 * _cyclic_apply will run on some thread under kernel_task. That's OK for the
529 * cleaner and the deadman, but too distant in time and place for the profile provider.
530 */
531 static void
532 _cyclic_apply( void *ignore, void *vTChdl )
533 {
534 #pragma unused(ignore)
535 wrap_thread_call_t *wrapTC = (wrap_thread_call_t *)vTChdl;
536
537 (*(wrapTC->hdlr.cyh_func))( wrapTC->hdlr.cyh_arg );
538
539 clock_deadline_for_periodic_event( wrapTC->when.cyt_interval, mach_absolute_time(), &(wrapTC->deadline));
540 (void)thread_call_enter1_delayed( wrapTC->TChdl, (void *)wrapTC, wrapTC->deadline );
541
542 /* Did cyclic_remove request a wakeup call when this thread call was re-armed? */
543 if (wrapTC->when.cyt_interval == WAKEUP_REAPER) {
544 thread_wakeup((event_t)wrapTC);
545 }
546 }
547
548 cyclic_id_t
549 cyclic_add(cyc_handler_t *handler, cyc_time_t *when)
550 {
551 uint64_t now;
552
553 wrap_thread_call_t *wrapTC = _MALLOC(sizeof(wrap_thread_call_t), M_TEMP, M_ZERO | M_WAITOK);
554 if (NULL == wrapTC) {
555 return CYCLIC_NONE;
556 }
557
558 wrapTC->TChdl = thread_call_allocate( _cyclic_apply, NULL );
559 wrapTC->hdlr = *handler;
560 wrapTC->when = *when;
561
562 ASSERT(when->cyt_when == 0);
563 ASSERT(when->cyt_interval < WAKEUP_REAPER);
564
565 nanoseconds_to_absolutetime(wrapTC->when.cyt_interval, (uint64_t *)&wrapTC->when.cyt_interval);
566
567 now = mach_absolute_time();
568 wrapTC->deadline = now;
569
570 clock_deadline_for_periodic_event( wrapTC->when.cyt_interval, now, &(wrapTC->deadline));
571 (void)thread_call_enter1_delayed( wrapTC->TChdl, (void *)wrapTC, wrapTC->deadline );
572
573 return (cyclic_id_t)wrapTC;
574 }
575
576 static void
577 noop_cyh_func(void * ignore)
578 {
579 #pragma unused(ignore)
580 }
581
582 void
583 cyclic_remove(cyclic_id_t cyclic)
584 {
585 wrap_thread_call_t *wrapTC = (wrap_thread_call_t *)cyclic;
586
587 ASSERT(cyclic != CYCLIC_NONE);
588
589 while (!thread_call_cancel(wrapTC->TChdl)) {
590 int ret = assert_wait(wrapTC, THREAD_UNINT);
591 ASSERT(ret == THREAD_WAITING);
592
593 wrapTC->when.cyt_interval = WAKEUP_REAPER;
594
595 ret = thread_block(THREAD_CONTINUE_NULL);
596 ASSERT(ret == THREAD_AWAKENED);
597 }
598
599 if (thread_call_free(wrapTC->TChdl)) {
600 _FREE(wrapTC, M_TEMP);
601 } else {
602 /* Gut this cyclic and move on ... */
603 wrapTC->hdlr.cyh_func = noop_cyh_func;
604 wrapTC->when.cyt_interval = NEARLY_FOREVER;
605 }
606 }
607
608 kern_return_t _dtrace_register_anon_DOF(char *, uchar_t *, uint_t);
609
610 kern_return_t
611 _dtrace_register_anon_DOF(char *name, uchar_t *data, uint_t nelements)
612 {
613 #pragma unused(name, data, nelements)
614 return KERN_FAILURE;
615 }
616
617 int
618 ddi_driver_major(dev_info_t *devi)
619 {
620 return (int)major(CAST_DOWN_EXPLICIT(int, devi));
621 }
622
623 int
624 ddi_create_minor_node(dev_info_t *dip, const char *name, int spec_type,
625 minor_t minor_num, const char *node_type, int flag)
626 {
627 #pragma unused(spec_type,node_type,flag)
628 dev_t dev = makedev( ddi_driver_major(dip), minor_num );
629
630 if (NULL == devfs_make_node( dev, DEVFS_CHAR, UID_ROOT, GID_WHEEL, 0666, name, 0 )) {
631 return DDI_FAILURE;
632 } else {
633 return DDI_SUCCESS;
634 }
635 }
636
637 void
638 ddi_remove_minor_node(dev_info_t *dip, char *name)
639 {
640 #pragma unused(dip,name)
641 /* XXX called from dtrace_detach, so NOTREACHED for now. */
642 }
643
644 major_t
645 getemajor( dev_t d )
646 {
647 return (major_t) major(d);
648 }
649
650 minor_t
651 getminor( dev_t d )
652 {
653 return (minor_t) minor(d);
654 }
655
656 extern void Debugger(const char*);
657
658 void
659 debug_enter(char *c)
660 {
661 Debugger(c);
662 }
663
664 /*
665 * kmem
666 */
667
668 void *
669 dt_kmem_alloc_site(size_t size, int kmflag, vm_allocation_site_t *site)
670 {
671 #pragma unused(kmflag)
672
673 /*
674 * We ignore the M_NOWAIT bit in kmflag (all of kmflag, in fact).
675 * Requests larger than 8K with M_NOWAIT fail in kalloc_canblock.
676 */
677 vm_size_t vsize = size;
678 return kalloc_canblock(&vsize, TRUE, site);
679 }
680
681 void *
682 dt_kmem_zalloc_site(size_t size, int kmflag, vm_allocation_site_t *site)
683 {
684 #pragma unused(kmflag)
685
686 /*
687 * We ignore the M_NOWAIT bit in kmflag (all of kmflag, in fact).
688 * Requests larger than 8K with M_NOWAIT fail in kalloc_canblock.
689 */
690 vm_size_t vsize = size;
691 void* buf = kalloc_canblock(&vsize, TRUE, site);
692
693 if (!buf) {
694 return NULL;
695 }
696
697 bzero(buf, size);
698
699 return buf;
700 }
701
702 void
703 dt_kmem_free(void *buf, size_t size)
704 {
705 #pragma unused(size)
706 /*
707 * DTrace relies on this, its doing a lot of NULL frees.
708 * A null free causes the debug builds to panic.
709 */
710 if (buf == NULL) {
711 return;
712 }
713
714 ASSERT(size > 0);
715
716 kfree(buf, size);
717 }
718
719
720
721 /*
722 * aligned dt_kmem allocator
723 * align should be a power of two
724 */
725
726 void*
727 dt_kmem_alloc_aligned_site(size_t size, size_t align, int kmflag, vm_allocation_site_t *site)
728 {
729 void *mem, **addr_to_free;
730 intptr_t mem_aligned;
731 size_t *size_to_free, hdr_size;
732
733 /* Must be a power of two. */
734 assert(align != 0);
735 assert((align & (align - 1)) == 0);
736
737 /*
738 * We are going to add a header to the allocation. It contains
739 * the address to free and the total size of the buffer.
740 */
741 hdr_size = sizeof(size_t) + sizeof(void*);
742 mem = dt_kmem_alloc_site(size + align + hdr_size, kmflag, site);
743 if (mem == NULL) {
744 return NULL;
745 }
746
747 mem_aligned = (intptr_t) (((intptr_t) mem + align + hdr_size) & ~(align - 1));
748
749 /* Write the address to free in the header. */
750 addr_to_free = (void**) (mem_aligned - sizeof(void*));
751 *addr_to_free = mem;
752
753 /* Write the size to free in the header. */
754 size_to_free = (size_t*) (mem_aligned - hdr_size);
755 *size_to_free = size + align + hdr_size;
756
757 return (void*) mem_aligned;
758 }
759
760 void*
761 dt_kmem_zalloc_aligned_site(size_t size, size_t align, int kmflag, vm_allocation_site_t *s)
762 {
763 void* buf;
764
765 buf = dt_kmem_alloc_aligned_site(size, align, kmflag, s);
766
767 if (!buf) {
768 return NULL;
769 }
770
771 bzero(buf, size);
772
773 return buf;
774 }
775
776 void
777 dt_kmem_free_aligned(void* buf, size_t size)
778 {
779 #pragma unused(size)
780 intptr_t ptr = (intptr_t) buf;
781 void **addr_to_free = (void**) (ptr - sizeof(void*));
782 size_t *size_to_free = (size_t*) (ptr - (sizeof(size_t) + sizeof(void*)));
783
784 if (buf == NULL) {
785 return;
786 }
787
788 dt_kmem_free(*addr_to_free, *size_to_free);
789 }
790
791 /*
792 * dtrace wants to manage just a single block: dtrace_state_percpu_t * NCPU, and
793 * doesn't specify constructor, destructor, or reclaim methods.
794 * At present, it always zeroes the block it obtains from kmem_cache_alloc().
795 * We'll manage this constricted use of kmem_cache with ordinary _MALLOC and _FREE.
796 */
797 kmem_cache_t *
798 kmem_cache_create(
799 const char *name, /* descriptive name for this cache */
800 size_t bufsize, /* size of the objects it manages */
801 size_t align, /* required object alignment */
802 int (*constructor)(void *, void *, int), /* object constructor */
803 void (*destructor)(void *, void *), /* object destructor */
804 void (*reclaim)(void *), /* memory reclaim callback */
805 void *private, /* pass-thru arg for constr/destr/reclaim */
806 vmem_t *vmp, /* vmem source for slab allocation */
807 int cflags) /* cache creation flags */
808 {
809 #pragma unused(name,align,constructor,destructor,reclaim,private,vmp,cflags)
810 return (kmem_cache_t *)bufsize; /* A cookie that tracks the single object size. */
811 }
812
813 void *
814 kmem_cache_alloc(kmem_cache_t *cp, int kmflag)
815 {
816 #pragma unused(kmflag)
817 size_t bufsize = (size_t)cp;
818 return (void *)_MALLOC(bufsize, M_TEMP, M_WAITOK);
819 }
820
821 void
822 kmem_cache_free(kmem_cache_t *cp, void *buf)
823 {
824 #pragma unused(cp)
825 _FREE(buf, M_TEMP);
826 }
827
828 void
829 kmem_cache_destroy(kmem_cache_t *cp)
830 {
831 #pragma unused(cp)
832 }
833
834 /*
835 * vmem (Solaris "slab" allocator) used by DTrace solely to hand out resource ids
836 */
837 typedef unsigned int u_daddr_t;
838 #include "blist.h"
839
840 /* By passing around blist *handles*, the underlying blist can be resized as needed. */
841 struct blist_hdl {
842 blist_t blist;
843 };
844
845 vmem_t *
846 vmem_create(const char *name, void *base, size_t size, size_t quantum, void *ignore5,
847 void *ignore6, vmem_t *source, size_t qcache_max, int vmflag)
848 {
849 #pragma unused(name,quantum,ignore5,ignore6,source,qcache_max,vmflag)
850 blist_t bl;
851 struct blist_hdl *p = _MALLOC(sizeof(struct blist_hdl), M_TEMP, M_WAITOK);
852
853 ASSERT(quantum == 1);
854 ASSERT(NULL == ignore5);
855 ASSERT(NULL == ignore6);
856 ASSERT(NULL == source);
857 ASSERT(0 == qcache_max);
858 ASSERT(vmflag & VMC_IDENTIFIER);
859
860 size = MIN(128, size); /* Clamp to 128 initially, since the underlying data structure is pre-allocated */
861
862 p->blist = bl = blist_create( size );
863 blist_free(bl, 0, size);
864 if (base) {
865 blist_alloc( bl, (daddr_t)(uintptr_t)base ); /* Chomp off initial ID(s) */
866 }
867 return (vmem_t *)p;
868 }
869
870 void *
871 vmem_alloc(vmem_t *vmp, size_t size, int vmflag)
872 {
873 #pragma unused(vmflag)
874 struct blist_hdl *q = (struct blist_hdl *)vmp;
875 blist_t bl = q->blist;
876 daddr_t p;
877
878 p = blist_alloc(bl, (daddr_t)size);
879
880 if ((daddr_t)-1 == p) {
881 blist_resize(&bl, (bl->bl_blocks) << 1, 1);
882 q->blist = bl;
883 p = blist_alloc(bl, (daddr_t)size);
884 if ((daddr_t)-1 == p) {
885 panic("vmem_alloc: failure after blist_resize!");
886 }
887 }
888
889 return (void *)(uintptr_t)p;
890 }
891
892 void
893 vmem_free(vmem_t *vmp, void *vaddr, size_t size)
894 {
895 struct blist_hdl *p = (struct blist_hdl *)vmp;
896
897 blist_free( p->blist, (daddr_t)(uintptr_t)vaddr, (daddr_t)size );
898 }
899
900 void
901 vmem_destroy(vmem_t *vmp)
902 {
903 struct blist_hdl *p = (struct blist_hdl *)vmp;
904
905 blist_destroy( p->blist );
906 _FREE( p, sizeof(struct blist_hdl));
907 }
908
909 /*
910 * Timing
911 */
912
913 /*
914 * dtrace_gethrestime() provides the "walltimestamp", a value that is anchored at
915 * January 1, 1970. Because it can be called from probe context, it must take no locks.
916 */
917
918 hrtime_t
919 dtrace_gethrestime(void)
920 {
921 clock_sec_t secs;
922 clock_nsec_t nanosecs;
923 uint64_t secs64, ns64;
924
925 clock_get_calendar_nanotime_nowait(&secs, &nanosecs);
926 secs64 = (uint64_t)secs;
927 ns64 = (uint64_t)nanosecs;
928
929 ns64 = ns64 + (secs64 * 1000000000LL);
930 return ns64;
931 }
932
933 /*
934 * dtrace_gethrtime() provides high-resolution timestamps with machine-dependent origin.
935 * Hence its primary use is to specify intervals.
936 */
937
938 hrtime_t
939 dtrace_abs_to_nano(uint64_t elapsed)
940 {
941 static mach_timebase_info_data_t sTimebaseInfo = { 0, 0 };
942
943 /*
944 * If this is the first time we've run, get the timebase.
945 * We can use denom == 0 to indicate that sTimebaseInfo is
946 * uninitialised because it makes no sense to have a zero
947 * denominator in a fraction.
948 */
949
950 if (sTimebaseInfo.denom == 0) {
951 (void) clock_timebase_info(&sTimebaseInfo);
952 }
953
954 /*
955 * Convert to nanoseconds.
956 * return (elapsed * (uint64_t)sTimebaseInfo.numer)/(uint64_t)sTimebaseInfo.denom;
957 *
958 * Provided the final result is representable in 64 bits the following maneuver will
959 * deliver that result without intermediate overflow.
960 */
961 if (sTimebaseInfo.denom == sTimebaseInfo.numer) {
962 return elapsed;
963 } else if (sTimebaseInfo.denom == 1) {
964 return elapsed * (uint64_t)sTimebaseInfo.numer;
965 } else {
966 /* Decompose elapsed = eta32 * 2^32 + eps32: */
967 uint64_t eta32 = elapsed >> 32;
968 uint64_t eps32 = elapsed & 0x00000000ffffffffLL;
969
970 uint32_t numer = sTimebaseInfo.numer, denom = sTimebaseInfo.denom;
971
972 /* Form product of elapsed64 (decomposed) and numer: */
973 uint64_t mu64 = numer * eta32;
974 uint64_t lambda64 = numer * eps32;
975
976 /* Divide the constituents by denom: */
977 uint64_t q32 = mu64 / denom;
978 uint64_t r32 = mu64 - (q32 * denom); /* mu64 % denom */
979
980 return (q32 << 32) + ((r32 << 32) + lambda64) / denom;
981 }
982 }
983
984 hrtime_t
985 dtrace_gethrtime(void)
986 {
987 static uint64_t start = 0;
988
989 if (start == 0) {
990 start = mach_absolute_time();
991 }
992
993 return dtrace_abs_to_nano(mach_absolute_time() - start);
994 }
995
996 /*
997 * Atomicity and synchronization
998 */
999 uint32_t
1000 dtrace_cas32(uint32_t *target, uint32_t cmp, uint32_t new)
1001 {
1002 if (OSCompareAndSwap((UInt32)cmp, (UInt32)new, (volatile UInt32 *)target )) {
1003 return cmp;
1004 } else {
1005 return ~cmp; /* Must return something *other* than cmp */
1006 }
1007 }
1008
1009 void *
1010 dtrace_casptr(void *target, void *cmp, void *new)
1011 {
1012 if (OSCompareAndSwapPtr( cmp, new, (void**)target )) {
1013 return cmp;
1014 } else {
1015 return (void *)(~(uintptr_t)cmp); /* Must return something *other* than cmp */
1016 }
1017 }
1018
1019 /*
1020 * Interrupt manipulation
1021 */
1022 dtrace_icookie_t
1023 dtrace_interrupt_disable(void)
1024 {
1025 return (dtrace_icookie_t)ml_set_interrupts_enabled(FALSE);
1026 }
1027
1028 void
1029 dtrace_interrupt_enable(dtrace_icookie_t reenable)
1030 {
1031 (void)ml_set_interrupts_enabled((boolean_t)reenable);
1032 }
1033
1034 /*
1035 * MP coordination
1036 */
1037 static void
1038 dtrace_sync_func(void)
1039 {
1040 }
1041
1042 /*
1043 * dtrace_sync() is not called from probe context.
1044 */
1045 void
1046 dtrace_sync(void)
1047 {
1048 dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
1049 }
1050
1051 /*
1052 * The dtrace_copyin/out/instr and dtrace_fuword* routines can be called from probe context.
1053 */
1054
1055 extern kern_return_t dtrace_copyio_preflight(addr64_t);
1056 extern kern_return_t dtrace_copyio_postflight(addr64_t);
1057
1058 static int
1059 dtrace_copycheck(user_addr_t uaddr, uintptr_t kaddr, size_t size)
1060 {
1061 #pragma unused(kaddr)
1062
1063 vm_offset_t recover = dtrace_set_thread_recover( current_thread(), 0 ); /* Snare any extant recovery point. */
1064 dtrace_set_thread_recover( current_thread(), recover ); /* Put it back. We *must not* re-enter and overwrite. */
1065
1066 ASSERT(kaddr + size >= kaddr);
1067
1068 if (uaddr + size < uaddr || /* Avoid address wrap. */
1069 KERN_FAILURE == dtrace_copyio_preflight(uaddr)) { /* Machine specific setup/constraints. */
1070 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
1071 cpu_core[CPU->cpu_id].cpuc_dtrace_illval = uaddr;
1072 return 0;
1073 }
1074 return 1;
1075 }
1076
1077 void
1078 dtrace_copyin(user_addr_t src, uintptr_t dst, size_t len, volatile uint16_t *flags)
1079 {
1080 #pragma unused(flags)
1081
1082 if (dtrace_copycheck( src, dst, len )) {
1083 if (copyin((const user_addr_t)src, (char *)dst, (vm_size_t)len)) {
1084 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
1085 cpu_core[CPU->cpu_id].cpuc_dtrace_illval = src;
1086 }
1087 dtrace_copyio_postflight(src);
1088 }
1089 }
1090
1091 void
1092 dtrace_copyinstr(user_addr_t src, uintptr_t dst, size_t len, volatile uint16_t *flags)
1093 {
1094 #pragma unused(flags)
1095
1096 size_t actual;
1097
1098 if (dtrace_copycheck( src, dst, len )) {
1099 /* copyin as many as 'len' bytes. */
1100 int error = copyinstr((const user_addr_t)src, (char *)dst, (vm_size_t)len, &actual);
1101
1102 /*
1103 * ENAMETOOLONG is returned when 'len' bytes have been copied in but the NUL terminator was
1104 * not encountered. That does not require raising CPU_DTRACE_BADADDR, and we press on.
1105 * Note that we do *not* stuff a NUL terminator when returning ENAMETOOLONG, that's left
1106 * to the caller.
1107 */
1108 if (error && error != ENAMETOOLONG) {
1109 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
1110 cpu_core[CPU->cpu_id].cpuc_dtrace_illval = src;
1111 }
1112 dtrace_copyio_postflight(src);
1113 }
1114 }
1115
1116 void
1117 dtrace_copyout(uintptr_t src, user_addr_t dst, size_t len, volatile uint16_t *flags)
1118 {
1119 #pragma unused(flags)
1120
1121 if (dtrace_copycheck( dst, src, len )) {
1122 if (copyout((const void *)src, dst, (vm_size_t)len)) {
1123 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
1124 cpu_core[CPU->cpu_id].cpuc_dtrace_illval = dst;
1125 }
1126 dtrace_copyio_postflight(dst);
1127 }
1128 }
1129
1130 void
1131 dtrace_copyoutstr(uintptr_t src, user_addr_t dst, size_t len, volatile uint16_t *flags)
1132 {
1133 #pragma unused(flags)
1134
1135 size_t actual;
1136
1137 if (dtrace_copycheck( dst, src, len )) {
1138 /*
1139 * ENAMETOOLONG is returned when 'len' bytes have been copied out but the NUL terminator was
1140 * not encountered. We raise CPU_DTRACE_BADADDR in that case.
1141 * Note that we do *not* stuff a NUL terminator when returning ENAMETOOLONG, that's left
1142 * to the caller.
1143 */
1144 if (copyoutstr((const void *)src, dst, (size_t)len, &actual)) {
1145 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
1146 cpu_core[CPU->cpu_id].cpuc_dtrace_illval = dst;
1147 }
1148 dtrace_copyio_postflight(dst);
1149 }
1150 }
1151
1152 extern const int copysize_limit_panic;
1153
1154 int
1155 dtrace_copy_maxsize(void)
1156 {
1157 return copysize_limit_panic;
1158 }
1159
1160
1161 int
1162 dtrace_buffer_copyout(const void *kaddr, user_addr_t uaddr, vm_size_t nbytes)
1163 {
1164 int maxsize = dtrace_copy_maxsize();
1165 /*
1166 * Partition the copyout in copysize_limit_panic-sized chunks
1167 */
1168 while (nbytes >= (vm_size_t)maxsize) {
1169 if (copyout(kaddr, uaddr, maxsize) != 0) {
1170 return EFAULT;
1171 }
1172
1173 nbytes -= maxsize;
1174 uaddr += maxsize;
1175 kaddr += maxsize;
1176 }
1177 if (nbytes > 0) {
1178 if (copyout(kaddr, uaddr, nbytes) != 0) {
1179 return EFAULT;
1180 }
1181 }
1182
1183 return 0;
1184 }
1185
1186 uint8_t
1187 dtrace_fuword8(user_addr_t uaddr)
1188 {
1189 uint8_t ret = 0;
1190
1191 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
1192 if (dtrace_copycheck( uaddr, (uintptr_t)&ret, sizeof(ret))) {
1193 if (copyin((const user_addr_t)uaddr, (char *)&ret, sizeof(ret))) {
1194 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
1195 cpu_core[CPU->cpu_id].cpuc_dtrace_illval = uaddr;
1196 }
1197 dtrace_copyio_postflight(uaddr);
1198 }
1199 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
1200
1201 return ret;
1202 }
1203
1204 uint16_t
1205 dtrace_fuword16(user_addr_t uaddr)
1206 {
1207 uint16_t ret = 0;
1208
1209 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
1210 if (dtrace_copycheck( uaddr, (uintptr_t)&ret, sizeof(ret))) {
1211 if (copyin((const user_addr_t)uaddr, (char *)&ret, sizeof(ret))) {
1212 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
1213 cpu_core[CPU->cpu_id].cpuc_dtrace_illval = uaddr;
1214 }
1215 dtrace_copyio_postflight(uaddr);
1216 }
1217 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
1218
1219 return ret;
1220 }
1221
1222 uint32_t
1223 dtrace_fuword32(user_addr_t uaddr)
1224 {
1225 uint32_t ret = 0;
1226
1227 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
1228 if (dtrace_copycheck( uaddr, (uintptr_t)&ret, sizeof(ret))) {
1229 if (copyin((const user_addr_t)uaddr, (char *)&ret, sizeof(ret))) {
1230 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
1231 cpu_core[CPU->cpu_id].cpuc_dtrace_illval = uaddr;
1232 }
1233 dtrace_copyio_postflight(uaddr);
1234 }
1235 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
1236
1237 return ret;
1238 }
1239
1240 uint64_t
1241 dtrace_fuword64(user_addr_t uaddr)
1242 {
1243 uint64_t ret = 0;
1244
1245 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
1246 if (dtrace_copycheck( uaddr, (uintptr_t)&ret, sizeof(ret))) {
1247 if (copyin((const user_addr_t)uaddr, (char *)&ret, sizeof(ret))) {
1248 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
1249 cpu_core[CPU->cpu_id].cpuc_dtrace_illval = uaddr;
1250 }
1251 dtrace_copyio_postflight(uaddr);
1252 }
1253 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
1254
1255 return ret;
1256 }
1257
1258 /*
1259 * Emulation of Solaris fuword / suword
1260 * Called from the fasttrap provider, so the use of copyin/out requires fewer safegaurds.
1261 */
1262
1263 int
1264 fuword8(user_addr_t uaddr, uint8_t *value)
1265 {
1266 if (copyin((const user_addr_t)uaddr, (char *)value, sizeof(uint8_t)) != 0) {
1267 return -1;
1268 }
1269
1270 return 0;
1271 }
1272
1273 int
1274 fuword16(user_addr_t uaddr, uint16_t *value)
1275 {
1276 if (copyin((const user_addr_t)uaddr, (char *)value, sizeof(uint16_t)) != 0) {
1277 return -1;
1278 }
1279
1280 return 0;
1281 }
1282
1283 int
1284 fuword32(user_addr_t uaddr, uint32_t *value)
1285 {
1286 if (copyin((const user_addr_t)uaddr, (char *)value, sizeof(uint32_t)) != 0) {
1287 return -1;
1288 }
1289
1290 return 0;
1291 }
1292
1293 int
1294 fuword64(user_addr_t uaddr, uint64_t *value)
1295 {
1296 if (copyin((const user_addr_t)uaddr, (char *)value, sizeof(uint64_t)) != 0) {
1297 return -1;
1298 }
1299
1300 return 0;
1301 }
1302
1303 void
1304 fuword32_noerr(user_addr_t uaddr, uint32_t *value)
1305 {
1306 if (copyin((const user_addr_t)uaddr, (char *)value, sizeof(uint32_t))) {
1307 *value = 0;
1308 }
1309 }
1310
1311 void
1312 fuword64_noerr(user_addr_t uaddr, uint64_t *value)
1313 {
1314 if (copyin((const user_addr_t)uaddr, (char *)value, sizeof(uint64_t))) {
1315 *value = 0;
1316 }
1317 }
1318
1319 int
1320 suword64(user_addr_t addr, uint64_t value)
1321 {
1322 if (copyout((const void *)&value, addr, sizeof(value)) != 0) {
1323 return -1;
1324 }
1325
1326 return 0;
1327 }
1328
1329 int
1330 suword32(user_addr_t addr, uint32_t value)
1331 {
1332 if (copyout((const void *)&value, addr, sizeof(value)) != 0) {
1333 return -1;
1334 }
1335
1336 return 0;
1337 }
1338
1339 /*
1340 * Miscellaneous
1341 */
1342 extern boolean_t dtrace_tally_fault(user_addr_t);
1343
1344 boolean_t
1345 dtrace_tally_fault(user_addr_t uaddr)
1346 {
1347 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
1348 cpu_core[CPU->cpu_id].cpuc_dtrace_illval = uaddr;
1349 return DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT) ? TRUE : FALSE;
1350 }
1351
1352 #define TOTTY 0x02
1353 extern int prf(const char *, va_list, int, struct tty *); /* bsd/kern/subr_prf.h */
1354
1355 int
1356 vuprintf(const char *format, va_list ap)
1357 {
1358 return prf(format, ap, TOTTY, NULL);
1359 }
1360
1361 /* Not called from probe context */
1362 void
1363 cmn_err( int level, const char *format, ... )
1364 {
1365 #pragma unused(level)
1366 va_list alist;
1367
1368 va_start(alist, format);
1369 vuprintf(format, alist);
1370 va_end(alist);
1371 uprintf("\n");
1372 }
1373
1374 /*
1375 * History:
1376 * 2002-01-24 gvdl Initial implementation of strstr
1377 */
1378
1379 __private_extern__ const char *
1380 strstr(const char *in, const char *str)
1381 {
1382 char c;
1383 size_t len;
1384 if (!in || !str) {
1385 return in;
1386 }
1387
1388 c = *str++;
1389 if (!c) {
1390 return (const char *) in; // Trivial empty string case
1391 }
1392 len = strlen(str);
1393 do {
1394 char sc;
1395
1396 do {
1397 sc = *in++;
1398 if (!sc) {
1399 return (char *) 0;
1400 }
1401 } while (sc != c);
1402 } while (strncmp(in, str, len) != 0);
1403
1404 return (const char *) (in - 1);
1405 }
1406
1407 const void*
1408 bsearch(const void *key, const void *base0, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
1409 {
1410 const char *base = base0;
1411 size_t lim;
1412 int cmp;
1413 const void *p;
1414 for (lim = nmemb; lim != 0; lim >>= 1) {
1415 p = base + (lim >> 1) * size;
1416 cmp = (*compar)(key, p);
1417 if (cmp == 0) {
1418 return p;
1419 }
1420 if (cmp > 0) { /* key > p: move right */
1421 base = (const char *)p + size;
1422 lim--;
1423 } /* else move left */
1424 }
1425 return NULL;
1426 }
1427
1428 /*
1429 * Runtime and ABI
1430 */
1431 uintptr_t
1432 dtrace_caller(int ignore)
1433 {
1434 #pragma unused(ignore)
1435 return -1; /* Just as in Solaris dtrace_asm.s */
1436 }
1437
1438 int
1439 dtrace_getstackdepth(int aframes)
1440 {
1441 struct frame *fp = (struct frame *)__builtin_frame_address(0);
1442 struct frame *nextfp, *minfp, *stacktop;
1443 int depth = 0;
1444 int on_intr;
1445
1446 if ((on_intr = CPU_ON_INTR(CPU)) != 0) {
1447 stacktop = (struct frame *)dtrace_get_cpu_int_stack_top();
1448 } else {
1449 stacktop = (struct frame *)(dtrace_get_kernel_stack(current_thread()) + kernel_stack_size);
1450 }
1451
1452 minfp = fp;
1453
1454 aframes++;
1455
1456 for (;;) {
1457 depth++;
1458
1459 nextfp = *(struct frame **)fp;
1460
1461 if (nextfp <= minfp || nextfp >= stacktop) {
1462 if (on_intr) {
1463 /*
1464 * Hop from interrupt stack to thread stack.
1465 */
1466 vm_offset_t kstack_base = dtrace_get_kernel_stack(current_thread());
1467
1468 minfp = (struct frame *)kstack_base;
1469 stacktop = (struct frame *)(kstack_base + kernel_stack_size);
1470
1471 on_intr = 0;
1472 continue;
1473 }
1474 break;
1475 }
1476
1477 fp = nextfp;
1478 minfp = fp;
1479 }
1480
1481 if (depth <= aframes) {
1482 return 0;
1483 }
1484
1485 return depth - aframes;
1486 }
1487
1488 int
1489 dtrace_addr_in_module(void* addr, struct modctl *ctl)
1490 {
1491 return OSKextKextForAddress(addr) == (void*)ctl->mod_address;
1492 }
1493
1494 /*
1495 * Unconsidered
1496 */
1497 void
1498 dtrace_vtime_enable(void)
1499 {
1500 }
1501
1502 void
1503 dtrace_vtime_disable(void)
1504 {
1505 }
1506
1507 #else /* else ! CONFIG_DTRACE */
1508
1509 #include <sys/types.h>
1510 #include <mach/vm_types.h>
1511 #include <mach/kmod.h>
1512
1513 /*
1514 * This exists to prevent build errors when dtrace is unconfigured.
1515 */
1516
1517 kern_return_t _dtrace_register_anon_DOF(char *, unsigned char *, uint32_t);
1518
1519 kern_return_t
1520 _dtrace_register_anon_DOF(char *arg1, unsigned char *arg2, uint32_t arg3)
1521 {
1522 #pragma unused(arg1, arg2, arg3)
1523
1524 return KERN_FAILURE;
1525 }
1526
1527 #endif /* CONFIG_DTRACE */