4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 #include <kern/thread.h>
29 #include <mach/thread_status.h>
31 /* XXX All of these should really be derived from syscall_sw.h */
32 #if defined (__x86_64__)
33 #define SYSCALL_CLASS_SHIFT 24
34 #define SYSCALL_CLASS_MASK (0xFF << SYSCALL_CLASS_SHIFT)
35 #define SYSCALL_NUMBER_MASK (~SYSCALL_CLASS_MASK)
36 #define I386_SYSCALL_NUMBER_MASK (0xFFFF)
39 #include <sys/param.h>
40 #include <sys/systm.h>
42 #include <sys/errno.h>
43 #include <sys/ioctl.h>
45 #include <sys/fcntl.h>
46 #include <sys/syscall.h>
47 #include <miscfs/devfs/devfs.h>
49 #include <sys/dtrace.h>
50 #include <sys/dtrace_impl.h>
51 #include <sys/systrace_args.h>
54 #include <sys/systm.h>
58 #include <machine/pal_routines.h>
60 #if defined (__x86_64__)
61 #define SYSTRACE_ARTIFICIAL_FRAMES 2
62 #define MACHTRACE_ARTIFICIAL_FRAMES 3
63 #elif defined(__arm__) || defined(__arm64__)
64 #define SYSTRACE_ARTIFICIAL_FRAMES 2
65 #define MACHTRACE_ARTIFICIAL_FRAMES 3
67 #error Unknown Architecture
70 #define SYSTRACE_NARGS (int)(sizeof(((uthread_t)NULL)->uu_arg) / sizeof(((uthread_t)NULL)->uu_arg[0]))
72 #include <sys/sysent.h>
73 #define sy_callc sy_call /* Map Solaris slot name to Darwin's */
74 #define NSYSCALL nsysent /* and is less than 500 or so */
76 extern const char *syscallnames
[];
78 #include <sys/dtrace_glue.h>
79 #define casptr dtrace_casptr
80 #define membar_enter dtrace_membar_producer
82 #define LOADABLE_SYSCALL(a) 0 /* Not pertinent to Darwin. */
83 #define LOADED_SYSCALL(a) 1 /* Not pertinent to Darwin. */
85 static LCK_MTX_DECLARE_ATTR(dtrace_systrace_lock
,
86 &dtrace_lck_grp
, &dtrace_lck_attr
); /* probe state lock */
88 systrace_sysent_t
*systrace_sysent
= NULL
;
89 void (*systrace_probe
)(dtrace_id_t
, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
91 static uint64_t systrace_getargval(void *, dtrace_id_t
, void *, int, int);
92 static void systrace_getargdesc(void *, dtrace_id_t
, void *, dtrace_argdesc_t
*);
95 systrace_stub(dtrace_id_t id
, uint64_t arg0
, uint64_t arg1
,
96 uint64_t arg2
, uint64_t arg3
, uint64_t arg4
)
98 #pragma unused(id,arg0,arg1,arg2,arg3,arg4)
102 dtrace_systrace_syscall(struct proc
*pp
, void *uap
, int *rv
)
104 unsigned short code
; /* The system call number */
106 systrace_sysent_t
*sy
;
109 syscall_arg_t
*ip
= (syscall_arg_t
*)uap
;
110 uint64_t uargs
[SYSTRACE_NARGS
] = {0};
112 #if defined (__x86_64__)
114 pal_register_cache_state(current_thread(), VALID
);
115 x86_saved_state_t
*tagged_regs
= (x86_saved_state_t
*)find_user_regs(current_thread());
117 if (is_saved_state64(tagged_regs
)) {
118 x86_saved_state64_t
*regs
= saved_state64(tagged_regs
);
119 code
= regs
->rax
& SYSCALL_NUMBER_MASK
;
121 * Check for indirect system call... system call number
128 code
= saved_state32(tagged_regs
)->eax
& I386_SYSCALL_NUMBER_MASK
;
131 vm_offset_t params
= (vm_offset_t
) (saved_state32(tagged_regs
)->uesp
+ sizeof(int));
132 code
= fuword(params
);
136 #elif defined(__arm__)
139 * On arm, syscall numbers depend on a flavor (indirect or not)
140 * and can be in either r0 or r12 (always u32)
143 /* See bsd/dev/arm/systemcalls.c:arm_get_syscall_number */
144 arm_saved_state_t
*arm_regs
= (arm_saved_state_t
*) find_user_regs(current_thread());
146 /* Check for indirect system call */
147 if (arm_regs
->r
[12] != 0) {
148 code
= arm_regs
->r
[12];
150 code
= arm_regs
->r
[0];
153 #elif defined(__arm64__)
156 * On arm64, syscall numbers depend on a flavor (indirect or not)
157 * ... and for u32 can be in either r0 or r12
158 * ... and for u64 can be in either x0 or x16
161 /* see bsd/dev/arm/systemcalls.c:arm_get_syscall_number */
162 arm_saved_state_t
*arm_regs
= (arm_saved_state_t
*) find_user_regs(current_thread());
164 if (is_saved_state32(arm_regs
)) {
165 /* Check for indirect system call */
166 if (saved_state32(arm_regs
)->r
[12] != 0) {
167 code
= saved_state32(arm_regs
)->r
[12];
169 code
= saved_state32(arm_regs
)->r
[0];
172 /* Check for indirect system call */
173 if (saved_state64(arm_regs
)->x
[ARM64_SYSCALL_CODE_REG_NUM
] != 0) {
174 code
= saved_state64(arm_regs
)->x
[ARM64_SYSCALL_CODE_REG_NUM
];
176 code
= saved_state64(arm_regs
)->x
[0];
181 #error Unknown Architecture
184 // Bounds "check" the value of code a la unix_syscall
185 sy
= (code
>= nsysent
) ? &systrace_sysent
[SYS_invalid
] : &systrace_sysent
[code
];
187 systrace_args(code
, ip
, uargs
);
189 if ((id
= sy
->stsy_entry
) != DTRACE_IDNONE
) {
190 uthread_t uthread
= (uthread_t
)get_bsdthread_info(current_thread());
192 uthread
->t_dtrace_syscall_args
= uargs
;
195 static_assert(SYSTRACE_NARGS
>= 5, "not enough system call arguments");
196 (*systrace_probe
)(id
, uargs
[0], uargs
[1], uargs
[2], uargs
[3], uargs
[4]);
199 uthread
->t_dtrace_syscall_args
= NULL
;
207 * APPLE NOTE: Not implemented.
208 * We want to explicitly allow DTrace consumers to stop a process
209 * before it actually executes the meat of the syscall.
211 p
= ttoproc(curthread
);
212 mutex_enter(&p
->p_lock
);
213 if (curthread
->t_dtrace_stop
&& !curthread
->t_lwp
->lwp_nostop
) {
214 curthread
->t_dtrace_stop
= 0;
215 stop(PR_REQUESTED
, 0);
217 mutex_exit(&p
->p_lock
);
220 rval
= (*sy
->stsy_underlying
)(pp
, uap
, rv
);
222 if ((id
= sy
->stsy_return
) != DTRACE_IDNONE
) {
223 uint64_t munged_rv0
, munged_rv1
;
224 uthread_t uthread
= (uthread_t
)get_bsdthread_info(current_thread());
227 uthread
->t_dtrace_errno
= rval
; /* Establish t_dtrace_errno now in case this enabling refers to it. */
230 * "Decode" rv for use in the call to dtrace_probe()
232 if (rval
== ERESTART
) {
233 munged_rv0
= -1LL; /* System call will be reissued in user mode. Make DTrace report a -1 return. */
235 } else if (rval
!= EJUSTRETURN
) {
237 munged_rv0
= -1LL; /* Mimic what libc will do. */
240 switch (sy
->stsy_return_type
) {
241 case _SYSCALL_RET_INT_T
:
245 case _SYSCALL_RET_UINT_T
:
246 munged_rv0
= ((u_int
)rv
[0]);
247 munged_rv1
= ((u_int
)rv
[1]);
249 case _SYSCALL_RET_OFF_T
:
250 case _SYSCALL_RET_UINT64_T
:
251 munged_rv0
= *(u_int64_t
*)rv
;
254 case _SYSCALL_RET_ADDR_T
:
255 case _SYSCALL_RET_SIZE_T
:
256 case _SYSCALL_RET_SSIZE_T
:
257 munged_rv0
= *(user_addr_t
*)rv
;
260 case _SYSCALL_RET_NONE
:
276 * <http://mail.opensolaris.org/pipermail/dtrace-discuss/2007-January/003276.html> says:
278 * "This is a bit of an historical artifact. At first, the syscall provider just
279 * had its return value in arg0, and the fbt and pid providers had their return
280 * values in arg1 (so that we could use arg0 for the offset of the return site).
282 * We inevitably started writing scripts where we wanted to see the return
283 * values from probes in all three providers, and we made this script easier
284 * to write by replicating the syscall return values in arg1 to match fbt and
285 * pid. We debated briefly about removing the return value from arg0, but
286 * decided that it would be less confusing to have the same data in two places
287 * than to have some non-helpful, non-intuitive value in arg0.
289 * This change was made 4/23/2003 according to the DTrace project's putback log."
291 (*systrace_probe
)(id
, munged_rv0
, munged_rv0
, munged_rv1
, (uint64_t)rval
, 0);
298 dtrace_systrace_syscall_return(unsigned short code
, int rval
, int *rv
)
300 systrace_sysent_t
*sy
;
303 // Bounds "check" the value of code a la unix_syscall_return
304 sy
= (code
>= nsysent
) ? &systrace_sysent
[SYS_invalid
] : &systrace_sysent
[code
];
306 if ((id
= sy
->stsy_return
) != DTRACE_IDNONE
) {
307 uint64_t munged_rv0
, munged_rv1
;
308 uthread_t uthread
= (uthread_t
)get_bsdthread_info(current_thread());
311 uthread
->t_dtrace_errno
= rval
; /* Establish t_dtrace_errno now in case this enabling refers to it. */
314 * "Decode" rv for use in the call to dtrace_probe()
316 if (rval
== ERESTART
) {
317 munged_rv0
= -1LL; /* System call will be reissued in user mode. Make DTrace report a -1 return. */
319 } else if (rval
!= EJUSTRETURN
) {
321 munged_rv0
= -1LL; /* Mimic what libc will do. */
324 switch (sy
->stsy_return_type
) {
325 case _SYSCALL_RET_INT_T
:
329 case _SYSCALL_RET_UINT_T
:
330 munged_rv0
= ((u_int
)rv
[0]);
331 munged_rv1
= ((u_int
)rv
[1]);
333 case _SYSCALL_RET_OFF_T
:
334 case _SYSCALL_RET_UINT64_T
:
335 munged_rv0
= *(u_int64_t
*)rv
;
338 case _SYSCALL_RET_ADDR_T
:
339 case _SYSCALL_RET_SIZE_T
:
340 case _SYSCALL_RET_SSIZE_T
:
341 munged_rv0
= *(user_addr_t
*)rv
;
344 case _SYSCALL_RET_NONE
:
359 (*systrace_probe
)(id
, munged_rv0
, munged_rv0
, munged_rv1
, (uint64_t)rval
, 0);
363 #define SYSTRACE_SHIFT 16
364 #define SYSTRACE_ISENTRY(x) ((int)(x) >> SYSTRACE_SHIFT)
365 #define SYSTRACE_SYSNUM(x) ((int)(x) & ((1 << SYSTRACE_SHIFT) - 1))
366 #define SYSTRACE_ENTRY(id) ((1 << SYSTRACE_SHIFT) | (id))
367 #define SYSTRACE_RETURN(id) (id)
369 #if ((1 << SYSTRACE_SHIFT) <= NSYSCALL)
370 #error 1 << SYSTRACE_SHIFT must exceed number of system calls
373 static dtrace_provider_id_t systrace_id
;
376 * APPLE NOTE: Avoid name clash with Darwin automagic conf symbol.
377 * See balanced undef below.
379 #define systrace_init _systrace_init
382 systrace_init(const struct sysent
*actual
, systrace_sysent_t
**interposed
)
384 systrace_sysent_t
*ssysent
= *interposed
; /* Avoid sysent shadow warning
385 * from bsd/sys/sysent.h */
388 if (ssysent
== NULL
) {
389 *interposed
= ssysent
= kmem_zalloc(sizeof(systrace_sysent_t
) *
393 for (i
= 0; i
< NSYSCALL
; i
++) {
394 const struct sysent
*a
= &actual
[i
];
395 systrace_sysent_t
*s
= &ssysent
[i
];
397 if (LOADABLE_SYSCALL(a
) && !LOADED_SYSCALL(a
)) {
401 if (a
->sy_callc
== dtrace_systrace_syscall
) {
405 s
->stsy_underlying
= a
->sy_callc
;
406 s
->stsy_return_type
= a
->sy_return_type
;
413 systrace_provide(void *arg
, const dtrace_probedesc_t
*desc
)
415 #pragma unused(arg) /* __APPLE__ */
422 systrace_init(sysent
, &systrace_sysent
);
424 for (i
= 0; i
< NSYSCALL
; i
++) {
425 if (systrace_sysent
[i
].stsy_underlying
== NULL
) {
429 if (dtrace_probe_lookup(systrace_id
, NULL
,
430 syscallnames
[i
], "entry") != 0) {
434 (void) dtrace_probe_create(systrace_id
, NULL
, syscallnames
[i
],
435 "entry", SYSTRACE_ARTIFICIAL_FRAMES
,
436 (void *)((uintptr_t)SYSTRACE_ENTRY(i
)));
437 (void) dtrace_probe_create(systrace_id
, NULL
, syscallnames
[i
],
438 "return", SYSTRACE_ARTIFICIAL_FRAMES
,
439 (void *)((uintptr_t)SYSTRACE_RETURN(i
)));
441 systrace_sysent
[i
].stsy_entry
= DTRACE_IDNONE
;
442 systrace_sysent
[i
].stsy_return
= DTRACE_IDNONE
;
449 systrace_destroy(void *arg
, dtrace_id_t id
, void *parg
)
451 #pragma unused(arg,id) /* __APPLE__ */
453 int sysnum
= SYSTRACE_SYSNUM((uintptr_t)parg
);
455 #pragma unused(sysnum) /* __APPLE__ */
457 * There's nothing to do here but assert that we have actually been
460 if (SYSTRACE_ISENTRY((uintptr_t)parg
)) {
461 ASSERT(systrace_sysent
[sysnum
].stsy_entry
== DTRACE_IDNONE
);
463 ASSERT(systrace_sysent
[sysnum
].stsy_return
== DTRACE_IDNONE
);
469 systrace_enable(void *arg
, dtrace_id_t id
, void *parg
)
471 #pragma unused(arg) /* __APPLE__ */
473 int sysnum
= SYSTRACE_SYSNUM((uintptr_t)parg
);
474 int enabled
= (systrace_sysent
[sysnum
].stsy_entry
!= DTRACE_IDNONE
||
475 systrace_sysent
[sysnum
].stsy_return
!= DTRACE_IDNONE
);
477 if (SYSTRACE_ISENTRY((uintptr_t)parg
)) {
478 systrace_sysent
[sysnum
].stsy_entry
= id
;
480 systrace_sysent
[sysnum
].stsy_return
= id
;
484 ASSERT(sysent
[sysnum
].sy_callc
== dtrace_systrace_syscall
);
488 lck_mtx_lock(&dtrace_systrace_lock
);
489 if (sysent
[sysnum
].sy_callc
== systrace_sysent
[sysnum
].stsy_underlying
) {
490 /* It is not possible to write to sysent[] directly because it is const. */
491 vm_offset_t dss
= ptrauth_nop_cast(vm_offset_t
, &dtrace_systrace_syscall
);
492 ml_nofault_copy((vm_offset_t
)&dss
, (vm_offset_t
)&sysent
[sysnum
].sy_callc
, sizeof(vm_offset_t
));
494 lck_mtx_unlock(&dtrace_systrace_lock
);
501 systrace_disable(void *arg
, dtrace_id_t id
, void *parg
)
503 #pragma unused(arg,id) /* __APPLE__ */
505 int sysnum
= SYSTRACE_SYSNUM((uintptr_t)parg
);
506 int disable
= (systrace_sysent
[sysnum
].stsy_entry
== DTRACE_IDNONE
||
507 systrace_sysent
[sysnum
].stsy_return
== DTRACE_IDNONE
);
511 * Usage of volatile protects the if statement below from being optimized away.
513 * Compilers are clever and know that const array values can't change in time
514 * and the if below is always false. That is because it can't see that DTrace
515 * injects dtrace_systrace_syscall dynamically and violates constness of the
518 volatile const struct sysent
*syscallent
= &sysent
[sysnum
];
520 lck_mtx_lock(&dtrace_systrace_lock
);
521 if (syscallent
->sy_callc
== dtrace_systrace_syscall
) {
522 ml_nofault_copy((vm_offset_t
)&systrace_sysent
[sysnum
].stsy_underlying
,
523 (vm_offset_t
)&syscallent
->sy_callc
, sizeof(vm_offset_t
));
525 lck_mtx_unlock(&dtrace_systrace_lock
);
528 if (SYSTRACE_ISENTRY((uintptr_t)parg
)) {
529 systrace_sysent
[sysnum
].stsy_entry
= DTRACE_IDNONE
;
531 systrace_sysent
[sysnum
].stsy_return
= DTRACE_IDNONE
;
535 static dtrace_pattr_t systrace_attr
= {
536 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
537 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_UNKNOWN
},
538 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_ISA
},
539 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
540 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_ISA
},
543 static dtrace_pops_t systrace_pops
= {
544 .dtps_provide
= systrace_provide
,
545 .dtps_provide_module
= NULL
,
546 .dtps_enable
= systrace_enable
,
547 .dtps_disable
= systrace_disable
,
548 .dtps_suspend
= NULL
,
550 .dtps_getargdesc
= systrace_getargdesc
,
551 .dtps_getargval
= systrace_getargval
,
552 .dtps_usermode
= NULL
,
553 .dtps_destroy
= systrace_destroy
557 systrace_attach(dev_info_t
*devi
)
559 systrace_probe
= (void*)&dtrace_probe
;
562 if (ddi_create_minor_node(devi
, "systrace", S_IFCHR
, 0,
563 DDI_PSEUDO
, 0) == DDI_FAILURE
||
564 dtrace_register("syscall", &systrace_attr
, DTRACE_PRIV_USER
, NULL
,
565 &systrace_pops
, NULL
, &systrace_id
) != 0) {
566 systrace_probe
= systrace_stub
;
567 ddi_remove_minor_node(devi
, NULL
);
576 * APPLE NOTE: systrace_detach not implemented
578 #if !defined(__APPLE__)
580 systrace_detach(dev_info_t
*devi
, ddi_detach_cmd_t cmd
)
591 if (dtrace_unregister(systrace_id
) != 0) {
595 ddi_remove_minor_node(devi
, NULL
);
596 systrace_probe
= systrace_stub
;
599 #endif /* __APPLE__ */
602 typedef kern_return_t (*mach_call_t
)(void *);
604 /* APPLE NOTE: From #include <kern/syscall_sw.h> which may be changed for 64 bit! */
605 typedef void mach_munge_t(void *);
608 int mach_trap_arg_count
;
609 kern_return_t (*mach_trap_function
)(void *);
610 #if defined(__arm64__) || defined(__x86_64__)
611 mach_munge_t
*mach_trap_arg_munge32
; /* system call arguments for 32-bit */
613 int mach_trap_u32_words
;
615 const char* mach_trap_name
;
616 #endif /* MACH_ASSERT */
619 extern const mach_trap_t mach_trap_table
[]; /* syscall_sw.h now declares this as const */
620 extern const int mach_trap_count
;
622 extern const char *const mach_syscall_name_table
[];
624 /* XXX From osfmk/i386/bsd_i386.c */
625 struct mach_call_args
{
638 #define NSYSCALL mach_trap_count
640 #if ((1 << SYSTRACE_SHIFT) <= NSYSCALL)
641 #error 1 << SYSTRACE_SHIFT must exceed number of Mach traps
644 typedef struct machtrace_sysent
{
645 dtrace_id_t stsy_entry
;
646 dtrace_id_t stsy_return
;
647 kern_return_t (*stsy_underlying
)(void *);
648 int32_t stsy_return_type
;
649 } machtrace_sysent_t
;
651 static machtrace_sysent_t
*machtrace_sysent
= NULL
;
653 void (*machtrace_probe
)(dtrace_id_t
, uint64_t, uint64_t,
654 uint64_t, uint64_t, uint64_t);
656 static uint64_t machtrace_getarg(void *, dtrace_id_t
, void *, int, int);
658 static dtrace_provider_id_t machtrace_id
;
661 dtrace_machtrace_syscall(struct mach_call_args
*args
)
663 int code
; /* The mach call number */
665 machtrace_sysent_t
*sy
;
671 syscall_arg_t
*ip
= (syscall_arg_t
*)args
;
672 mach_call_t mach_call
;
674 #if defined (__x86_64__)
676 pal_register_cache_state(current_thread(), VALID
);
677 x86_saved_state_t
*tagged_regs
= (x86_saved_state_t
*)find_user_regs(current_thread());
679 if (is_saved_state64(tagged_regs
)) {
680 code
= saved_state64(tagged_regs
)->rax
& SYSCALL_NUMBER_MASK
;
682 code
= -saved_state32(tagged_regs
)->eax
;
685 #elif defined(__arm__)
687 /* r12 has the machcall number, but it is -ve */
688 arm_saved_state_t
*arm_regs
= (arm_saved_state_t
*) find_user_regs(current_thread());
689 code
= (int)arm_regs
->r
[12];
690 ASSERT(code
< 0); /* Otherwise it would be a Unix syscall */
693 #elif defined(__arm64__)
695 /* From arm/thread_status.h:get_saved_state_svc_number */
696 arm_saved_state_t
*arm_regs
= (arm_saved_state_t
*) find_user_regs(current_thread());
697 if (is_saved_state32(arm_regs
)) {
698 code
= (int)saved_state32(arm_regs
)->r
[12];
700 code
= (int)saved_state64(arm_regs
)->x
[ARM64_SYSCALL_CODE_REG_NUM
];
703 /* From bsd/arm64.c:mach_syscall */
704 ASSERT(code
< 0); /* Otherwise it would be a Unix syscall */
708 #error Unknown Architecture
711 sy
= &machtrace_sysent
[code
];
713 if ((id
= sy
->stsy_entry
) != DTRACE_IDNONE
) {
714 uthread_t uthread
= (uthread_t
)get_bsdthread_info(current_thread());
717 uthread
->t_dtrace_syscall_args
= (void *)ip
;
720 (*machtrace_probe
)(id
, *ip
, *(ip
+ 1), *(ip
+ 2), *(ip
+ 3), *(ip
+ 4));
723 uthread
->t_dtrace_syscall_args
= (void *)0;
729 * APPLE NOTE: Not implemented.
730 * We want to explicitly allow DTrace consumers to stop a process
731 * before it actually executes the meat of the syscall.
733 p
= ttoproc(curthread
);
734 mutex_enter(&p
->p_lock
);
735 if (curthread
->t_dtrace_stop
&& !curthread
->t_lwp
->lwp_nostop
) {
736 curthread
->t_dtrace_stop
= 0;
737 stop(PR_REQUESTED
, 0);
739 mutex_exit(&p
->p_lock
);
742 mach_call
= (mach_call_t
)(*sy
->stsy_underlying
);
743 rval
= mach_call(args
);
745 if ((id
= sy
->stsy_return
) != DTRACE_IDNONE
) {
746 (*machtrace_probe
)(id
, (uint64_t)rval
, 0, 0, 0, 0);
753 machtrace_init(const mach_trap_t
*actual
, machtrace_sysent_t
**interposed
)
755 machtrace_sysent_t
*msysent
= *interposed
;
758 if (msysent
== NULL
) {
759 *interposed
= msysent
= kmem_zalloc(sizeof(machtrace_sysent_t
) *
763 for (i
= 0; i
< NSYSCALL
; i
++) {
764 const mach_trap_t
*a
= &actual
[i
];
765 machtrace_sysent_t
*s
= &msysent
[i
];
767 if (LOADABLE_SYSCALL(a
) && !LOADED_SYSCALL(a
)) {
771 if (a
->mach_trap_function
== (mach_call_t
)(dtrace_machtrace_syscall
)) {
775 s
->stsy_underlying
= a
->mach_trap_function
;
781 machtrace_provide(void *arg
, const dtrace_probedesc_t
*desc
)
783 #pragma unused(arg) /* __APPLE__ */
791 machtrace_init(mach_trap_table
, &machtrace_sysent
);
793 for (i
= 0; i
< NSYSCALL
; i
++) {
794 if (machtrace_sysent
[i
].stsy_underlying
== NULL
) {
798 if (dtrace_probe_lookup(machtrace_id
, NULL
,
799 mach_syscall_name_table
[i
], "entry") != 0) {
803 (void) dtrace_probe_create(machtrace_id
, NULL
, mach_syscall_name_table
[i
],
804 "entry", MACHTRACE_ARTIFICIAL_FRAMES
,
805 (void *)((uintptr_t)SYSTRACE_ENTRY(i
)));
806 (void) dtrace_probe_create(machtrace_id
, NULL
, mach_syscall_name_table
[i
],
807 "return", MACHTRACE_ARTIFICIAL_FRAMES
,
808 (void *)((uintptr_t)SYSTRACE_RETURN(i
)));
810 machtrace_sysent
[i
].stsy_entry
= DTRACE_IDNONE
;
811 machtrace_sysent
[i
].stsy_return
= DTRACE_IDNONE
;
817 machtrace_destroy(void *arg
, dtrace_id_t id
, void *parg
)
819 #pragma unused(arg,id) /* __APPLE__ */
820 int sysnum
= SYSTRACE_SYSNUM((uintptr_t)parg
);
822 #pragma unused(sysnum) /* __APPLE__ */
825 * There's nothing to do here but assert that we have actually been
828 if (SYSTRACE_ISENTRY((uintptr_t)parg
)) {
829 ASSERT(machtrace_sysent
[sysnum
].stsy_entry
== DTRACE_IDNONE
);
831 ASSERT(machtrace_sysent
[sysnum
].stsy_return
== DTRACE_IDNONE
);
837 machtrace_enable(void *arg
, dtrace_id_t id
, void *parg
)
839 #pragma unused(arg) /* __APPLE__ */
841 int sysnum
= SYSTRACE_SYSNUM((uintptr_t)parg
);
842 int enabled
= (machtrace_sysent
[sysnum
].stsy_entry
!= DTRACE_IDNONE
||
843 machtrace_sysent
[sysnum
].stsy_return
!= DTRACE_IDNONE
);
845 if (SYSTRACE_ISENTRY((uintptr_t)parg
)) {
846 machtrace_sysent
[sysnum
].stsy_entry
= id
;
848 machtrace_sysent
[sysnum
].stsy_return
= id
;
852 ASSERT(mach_trap_table
[sysnum
].mach_trap_function
== (void *)dtrace_machtrace_syscall
);
856 lck_mtx_lock(&dtrace_systrace_lock
);
858 if (mach_trap_table
[sysnum
].mach_trap_function
== machtrace_sysent
[sysnum
].stsy_underlying
) {
859 /* It is not possible to write to mach_trap_table[] directly because it is const. */
860 vm_offset_t dss
= ptrauth_nop_cast(vm_offset_t
, &dtrace_machtrace_syscall
);
861 ml_nofault_copy((vm_offset_t
)&dss
, (vm_offset_t
)&mach_trap_table
[sysnum
].mach_trap_function
, sizeof(vm_offset_t
));
864 lck_mtx_unlock(&dtrace_systrace_lock
);
871 machtrace_disable(void *arg
, dtrace_id_t id
, void *parg
)
873 #pragma unused(arg,id) /* __APPLE__ */
875 int sysnum
= SYSTRACE_SYSNUM((uintptr_t)parg
);
876 int disable
= (machtrace_sysent
[sysnum
].stsy_entry
== DTRACE_IDNONE
||
877 machtrace_sysent
[sysnum
].stsy_return
== DTRACE_IDNONE
);
881 * Usage of volatile protects the if statement below from being optimized away.
883 * Compilers are clever and know that const array values can't change in time
884 * and the if below is always false. That is because it can't see that DTrace
885 * injects dtrace_machtrace_syscall dynamically and violates constness of the
888 volatile const mach_trap_t
*machtrap
= &mach_trap_table
[sysnum
];
890 lck_mtx_lock(&dtrace_systrace_lock
);
891 if (machtrap
->mach_trap_function
== (mach_call_t
)dtrace_machtrace_syscall
) {
892 ml_nofault_copy((vm_offset_t
)&machtrace_sysent
[sysnum
].stsy_underlying
,
893 (vm_offset_t
)&machtrap
->mach_trap_function
, sizeof(vm_offset_t
));
895 lck_mtx_unlock(&dtrace_systrace_lock
);
898 if (SYSTRACE_ISENTRY((uintptr_t)parg
)) {
899 machtrace_sysent
[sysnum
].stsy_entry
= DTRACE_IDNONE
;
901 machtrace_sysent
[sysnum
].stsy_return
= DTRACE_IDNONE
;
905 static dtrace_pattr_t machtrace_attr
= {
906 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
907 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_UNKNOWN
},
908 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_ISA
},
909 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
910 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_ISA
},
913 static dtrace_pops_t machtrace_pops
= {
914 .dtps_provide
= machtrace_provide
,
915 .dtps_provide_module
= NULL
,
916 .dtps_enable
= machtrace_enable
,
917 .dtps_disable
= machtrace_disable
,
918 .dtps_suspend
= NULL
,
920 .dtps_getargdesc
= NULL
,
921 .dtps_getargval
= machtrace_getarg
,
922 .dtps_usermode
= NULL
,
923 .dtps_destroy
= machtrace_destroy
927 machtrace_attach(dev_info_t
*devi
)
929 machtrace_probe
= dtrace_probe
;
932 if (ddi_create_minor_node(devi
, "machtrace", S_IFCHR
, 0,
933 DDI_PSEUDO
, 0) == DDI_FAILURE
||
934 dtrace_register("mach_trap", &machtrace_attr
, DTRACE_PRIV_USER
, NULL
,
935 &machtrace_pops
, NULL
, &machtrace_id
) != 0) {
936 machtrace_probe
= (void*)&systrace_stub
;
937 ddi_remove_minor_node(devi
, NULL
);
944 d_open_t _systrace_open
;
947 _systrace_open(dev_t dev
, int flags
, int devtype
, struct proc
*p
)
949 #pragma unused(dev,flags,devtype,p)
953 #define SYSTRACE_MAJOR -24 /* let the kernel pick the device number */
955 static struct cdevsw systrace_cdevsw
=
957 .d_open
= _systrace_open
,
960 .d_write
= eno_rdwrt
,
961 .d_ioctl
= eno_ioctl
,
962 .d_stop
= (stop_fcn_t
*)nulldev
,
963 .d_reset
= (reset_fcn_t
*)nulldev
,
964 .d_select
= eno_select
,
966 .d_strategy
= eno_strat
,
967 .d_reserved_1
= eno_getc
,
968 .d_reserved_2
= eno_putc
,
971 void systrace_init( void );
974 systrace_init( void )
976 if (dtrace_sdt_probes_restricted()) {
980 int majdevno
= cdevsw_add(SYSTRACE_MAJOR
, &systrace_cdevsw
);
983 printf("systrace_init: failed to allocate a major number!\n");
987 systrace_attach((dev_info_t
*)(uintptr_t)majdevno
);
988 machtrace_attach((dev_info_t
*)(uintptr_t)majdevno
);
990 #undef SYSTRACE_MAJOR
993 systrace_getargval(void *arg
, dtrace_id_t id
, void *parg
, int argno
, int aframes
)
995 #pragma unused(arg,id,parg,aframes) /* __APPLE__ */
997 uint64_t *uargs
= NULL
;
999 uthread_t uthread
= (uthread_t
)get_bsdthread_info(current_thread());
1002 uargs
= uthread
->t_dtrace_syscall_args
;
1007 if (argno
< 0 || argno
>= SYSTRACE_NARGS
) {
1011 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT
);
1013 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT
);
1018 systrace_getargdesc(void *arg
, dtrace_id_t id
, void *parg
,
1019 dtrace_argdesc_t
*desc
)
1021 #pragma unused(arg, id)
1022 int sysnum
= SYSTRACE_SYSNUM(parg
);
1023 uthread_t uthread
= (uthread_t
)get_bsdthread_info(current_thread());
1024 uint64_t *uargs
= NULL
;
1027 desc
->dtargd_ndx
= DTRACE_ARGNONE
;
1031 uargs
= uthread
->t_dtrace_syscall_args
;
1033 if (SYSTRACE_ISENTRY((uintptr_t)parg
)) {
1034 systrace_entry_setargdesc(sysnum
, desc
->dtargd_ndx
,
1035 desc
->dtargd_native
, sizeof(desc
->dtargd_native
));
1037 systrace_return_setargdesc(sysnum
, desc
->dtargd_ndx
,
1038 desc
->dtargd_native
, sizeof(desc
->dtargd_native
));
1041 if (desc
->dtargd_native
[0] == '\0') {
1042 desc
->dtargd_ndx
= DTRACE_ARGNONE
;
1047 machtrace_getarg(void *arg
, dtrace_id_t id
, void *parg
, int argno
, int aframes
)
1049 #pragma unused(arg,id,parg,aframes) /* __APPLE__ */
1051 syscall_arg_t
*stack
= (syscall_arg_t
*)NULL
;
1053 uthread_t uthread
= (uthread_t
)get_bsdthread_info(current_thread());
1056 stack
= (syscall_arg_t
*)uthread
->t_dtrace_syscall_args
;
1063 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT
);
1064 /* dtrace_probe arguments arg0 .. arg4 are 64bits wide */
1065 val
= (uint64_t)*(stack
+ argno
);
1066 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT
);