xref: /freebsd/sys/cddl/dev/kinst/kinst.c (revision 9310bf54049422a9bd127100f697e6cbb189d889)
1 /*
2  * SPDX-License-Identifier: CDDL 1.0
3  *
4  * Copyright (c) 2022 Christos Margiolis <christos@FreeBSD.org>
5  * Copyright (c) 2023 The FreeBSD Foundation
6  *
7  * Portions of this software were developed by Christos Margiolis
8  * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
9  */
10 
11 #include <sys/param.h>
12 #include <sys/systm.h>
13 #include <sys/conf.h>
14 #include <sys/kernel.h>
15 #include <sys/linker.h>
16 #include <sys/module.h>
17 
18 #include <sys/dtrace.h>
19 
20 #include "kinst.h"
21 
22 MALLOC_DEFINE(M_KINST, "kinst", "Kernel Instruction Tracing");
23 
24 static d_open_t		kinst_open;
25 static d_close_t	kinst_close;
26 static d_ioctl_t	kinst_ioctl;
27 
28 static void	kinst_provide_module(void *, modctl_t *);
29 static void	kinst_getargdesc(void *, dtrace_id_t, void *,
30 		    dtrace_argdesc_t *);
31 static void	kinst_destroy(void *, dtrace_id_t, void *);
32 static void	kinst_enable(void *, dtrace_id_t, void *);
33 static void	kinst_disable(void *, dtrace_id_t, void *);
34 static int	kinst_load(void *);
35 static int	kinst_unload(void *);
36 static int	kinst_modevent(module_t, int, void *);
37 
38 static dtrace_pattr_t kinst_attr = {
39 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
40 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
41 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
42 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
43 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
44 };
45 
46 static const dtrace_pops_t kinst_pops = {
47 	.dtps_provide		= NULL,
48 	.dtps_provide_module	= kinst_provide_module,
49 	.dtps_enable		= kinst_enable,
50 	.dtps_disable		= kinst_disable,
51 	.dtps_suspend		= NULL,
52 	.dtps_resume		= NULL,
53 	.dtps_getargdesc	= kinst_getargdesc,
54 	.dtps_getargval		= NULL,
55 	.dtps_usermode		= NULL,
56 	.dtps_destroy		= kinst_destroy
57 };
58 
59 static struct cdevsw kinst_cdevsw = {
60 	.d_name			= "kinst",
61 	.d_version		= D_VERSION,
62 	.d_flags		= D_TRACKCLOSE,
63 	.d_open			= kinst_open,
64 	.d_close		= kinst_close,
65 	.d_ioctl		= kinst_ioctl,
66 };
67 
68 static dtrace_provider_id_t	kinst_id;
69 struct kinst_probe_list	*kinst_probetab;
70 static struct cdev	*kinst_cdev;
71 
72 /*
73  * Tracing memcpy() will crash the kernel when kinst tries to trace an instance
74  * of the memcpy() calls in kinst_invop(). To fix this, we can use
75  * kinst_memcpy() in those cases, with its arguments marked as 'volatile' to
76  * "outsmart" the compiler and avoid having it replaced by a regular memcpy().
77  */
78 volatile void *
79 kinst_memcpy(volatile void *dst, volatile const void *src, size_t len)
80 {
81 	volatile const unsigned char *src0;
82 	volatile unsigned char *dst0;
83 
84 	src0 = src;
85 	dst0 = dst;
86 
87 	while (len--)
88 		*dst0++ = *src0++;
89 
90 	return (dst);
91 }
92 
93 bool
94 kinst_excluded(const char *name)
95 {
96 	if (kinst_md_excluded(name))
97 		return (true);
98 
99 	/*
100 	 * Anything beginning with "dtrace_" may be called from probe context
101 	 * unless it explicitly indicates that it won't be called from probe
102 	 * context by using the prefix "dtrace_safe_".
103 	 */
104 	if (strncmp(name, "dtrace_", strlen("dtrace_")) == 0 &&
105 	    strncmp(name, "dtrace_safe_", strlen("dtrace_safe_")) != 0)
106 		return (true);
107 
108 	/*
109 	 * Omit instrumentation of functions that are probably in DDB.  It
110 	 * makes it too hard to debug broken kinst.
111 	 *
112 	 * NB: kdb_enter() can be excluded, but its call to printf() can't be.
113 	 * This is generally OK since we're not yet in debugging context.
114 	 */
115 	if (strncmp(name, "db_", strlen("db_")) == 0 ||
116 	    strncmp(name, "kdb_", strlen("kdb_")) == 0)
117 		return (true);
118 
119 	/*
120 	 * Lock owner methods may be called from probe context.
121 	 */
122 	if (strcmp(name, "owner_mtx") == 0 ||
123 	    strcmp(name, "owner_rm") == 0 ||
124 	    strcmp(name, "owner_rw") == 0 ||
125 	    strcmp(name, "owner_sx") == 0)
126 		return (true);
127 
128 	/*
129 	 * When DTrace is built into the kernel we need to exclude the kinst
130 	 * functions from instrumentation.
131 	 */
132 #ifndef _KLD_MODULE
133 	if (strncmp(name, "kinst_", strlen("kinst_")) == 0)
134 		return (true);
135 #endif
136 
137 	if (strcmp(name, "trap_check") == 0)
138 		return (true);
139 
140 	return (false);
141 }
142 
143 void
144 kinst_probe_create(struct kinst_probe *kp, linker_file_t lf)
145 {
146 	kp->kp_id = dtrace_probe_create(kinst_id, lf->filename,
147 	    kp->kp_func, kp->kp_name, 3, kp);
148 
149 	LIST_INSERT_HEAD(KINST_GETPROBE(kp->kp_patchpoint), kp, kp_hashnext);
150 }
151 
152 static int
153 kinst_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused,
154     struct thread *td __unused)
155 {
156 	return (0);
157 }
158 
159 static int
160 kinst_close(struct cdev *dev __unused, int fflag __unused, int devtype __unused,
161     struct thread *td __unused)
162 {
163 	dtrace_condense(kinst_id);
164 	return (0);
165 }
166 
167 static int
168 kinst_linker_file_cb(linker_file_t lf, void *arg)
169 {
170 	dtrace_kinst_probedesc_t *pd;
171 
172 	pd = arg;
173 	if (pd->kpd_mod[0] != '\0' && strcmp(pd->kpd_mod, lf->filename) != 0)
174 		return (0);
175 
176 	/*
177 	 * Invoke kinst_make_probe_function() once for each function symbol in
178 	 * the module "lf".
179 	 */
180 	return (linker_file_function_listall(lf, kinst_make_probe, arg));
181 }
182 
183 static int
184 kinst_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
185     int flags __unused, struct thread *td __unused)
186 {
187 	dtrace_kinst_probedesc_t *pd;
188 	int error = 0;
189 
190 	switch (cmd) {
191 	case KINSTIOC_MAKEPROBE:
192 		pd = (dtrace_kinst_probedesc_t *)addr;
193 		pd->kpd_func[sizeof(pd->kpd_func) - 1] = '\0';
194 		pd->kpd_mod[sizeof(pd->kpd_mod) - 1] = '\0';
195 
196 		/* Loop over all functions in the kernel and loaded modules. */
197 		error = linker_file_foreach(kinst_linker_file_cb, pd);
198 		break;
199 	default:
200 		error = ENOTTY;
201 		break;
202 	}
203 
204 	return (error);
205 }
206 
207 static void
208 kinst_provide_module(void *arg, modctl_t *lf)
209 {
210 }
211 
212 static void
213 kinst_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
214 {
215 	desc->dtargd_ndx = DTRACE_ARGNONE;
216 }
217 
218 static void
219 kinst_destroy(void *arg, dtrace_id_t id, void *parg)
220 {
221 	struct kinst_probe *kp = parg;
222 
223 	LIST_REMOVE(kp, kp_hashnext);
224 	free(kp, M_KINST);
225 }
226 
227 static void
228 kinst_enable(void *arg, dtrace_id_t id, void *parg)
229 {
230 	struct kinst_probe *kp = parg;
231 	static bool warned = false;
232 
233 	if (!warned) {
234 		KINST_LOG(
235 		    "kinst: This provider is experimental, exercise caution");
236 		warned = true;
237 	}
238 
239 	kinst_patch_tracepoint(kp, kp->kp_patchval);
240 }
241 
242 static void
243 kinst_disable(void *arg, dtrace_id_t id, void *parg)
244 {
245 	struct kinst_probe *kp = parg;
246 
247 	kinst_patch_tracepoint(kp, kp->kp_savedval);
248 }
249 
250 static int
251 kinst_load(void *dummy)
252 {
253 	int error;
254 
255 	error = kinst_trampoline_init();
256 	if (error != 0)
257 		return (error);
258 	error = kinst_md_init();
259 	if (error != 0) {
260 		kinst_trampoline_deinit();
261 		return (error);
262 	}
263 
264 	error = dtrace_register("kinst", &kinst_attr, DTRACE_PRIV_USER, NULL,
265 	    &kinst_pops, NULL, &kinst_id);
266 	if (error != 0) {
267 		kinst_md_deinit();
268 		kinst_trampoline_deinit();
269 		return (error);
270 	}
271 	kinst_probetab = malloc(KINST_PROBETAB_MAX *
272 	    sizeof(struct kinst_probe_list), M_KINST, M_WAITOK | M_ZERO);
273 	for (int i = 0; i < KINST_PROBETAB_MAX; i++)
274 		LIST_INIT(&kinst_probetab[i]);
275 	kinst_cdev = make_dev(&kinst_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
276 	    "dtrace/kinst");
277 	dtrace_invop_add(kinst_invop);
278 	return (0);
279 }
280 
281 static int
282 kinst_unload(void *dummy)
283 {
284 	free(kinst_probetab, M_KINST);
285 	kinst_md_deinit();
286 	kinst_trampoline_deinit();
287 	dtrace_invop_remove(kinst_invop);
288 	destroy_dev(kinst_cdev);
289 
290 	return (dtrace_unregister(kinst_id));
291 }
292 
293 static int
294 kinst_modevent(module_t mod __unused, int type, void *data __unused)
295 {
296 	int error = 0;
297 
298 	switch (type) {
299 	case MOD_LOAD:
300 		break;
301 	case MOD_UNLOAD:
302 		break;
303 	case MOD_SHUTDOWN:
304 		break;
305 	default:
306 		error = EOPNOTSUPP;
307 		break;
308 	}
309 
310 	return (error);
311 }
312 
313 SYSINIT(kinst_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, kinst_load, NULL);
314 SYSUNINIT(kinst_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, kinst_unload,
315     NULL);
316 
317 DEV_MODULE(kinst, kinst_modevent, NULL);
318 MODULE_VERSION(kinst, 1);
319 MODULE_DEPEND(kinst, dtrace, 1, 1, 1);
320 MODULE_DEPEND(kinst, opensolaris, 1, 1, 1);
321