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