xref: /freebsd/sys/cddl/dev/kinst/kinst.c (revision f374ba41)
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 void
69 kinst_probe_create(struct kinst_probe *kp, linker_file_t lf)
70 {
71 	kp->kp_id = dtrace_probe_create(kinst_id, lf->filename,
72 	    kp->kp_func, kp->kp_name, 3, kp);
73 
74 	LIST_INSERT_HEAD(KINST_GETPROBE(kp->kp_patchpoint), kp, kp_hashnext);
75 }
76 
77 static int
78 kinst_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused,
79     struct thread *td __unused)
80 {
81 	return (0);
82 }
83 
84 static int
85 kinst_close(struct cdev *dev __unused, int fflag __unused, int devtype __unused,
86     struct thread *td __unused)
87 {
88 	dtrace_condense(kinst_id);
89 	return (0);
90 }
91 
92 static int
93 kinst_linker_file_cb(linker_file_t lf, void *arg)
94 {
95 	dtrace_kinst_probedesc_t *pd;
96 
97 	pd = arg;
98 	if (pd->kpd_mod[0] != '\0' && strcmp(pd->kpd_mod, lf->filename) != 0)
99 		return (0);
100 
101 	/*
102 	 * Invoke kinst_make_probe_function() once for each function symbol in
103 	 * the module "lf".
104 	 */
105 	return (linker_file_function_listall(lf, kinst_make_probe, arg));
106 }
107 
108 static int
109 kinst_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
110     int flags __unused, struct thread *td __unused)
111 {
112 	dtrace_kinst_probedesc_t *pd;
113 	int error = 0;
114 
115 	switch (cmd) {
116 	case KINSTIOC_MAKEPROBE:
117 		pd = (dtrace_kinst_probedesc_t *)addr;
118 		pd->kpd_func[sizeof(pd->kpd_func) - 1] = '\0';
119 		pd->kpd_mod[sizeof(pd->kpd_mod) - 1] = '\0';
120 
121 		/* Loop over all functions in the kernel and loaded modules. */
122 		error = linker_file_foreach(kinst_linker_file_cb, pd);
123 		break;
124 	default:
125 		error = ENOTTY;
126 		break;
127 	}
128 
129 	return (error);
130 }
131 
132 static void
133 kinst_provide_module(void *arg, modctl_t *lf)
134 {
135 }
136 
137 static void
138 kinst_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
139 {
140 	desc->dtargd_ndx = DTRACE_ARGNONE;
141 }
142 
143 static void
144 kinst_destroy(void *arg, dtrace_id_t id, void *parg)
145 {
146 	struct kinst_probe *kp = parg;
147 
148 	LIST_REMOVE(kp, kp_hashnext);
149 	free(kp, M_KINST);
150 }
151 
152 static void
153 kinst_enable(void *arg, dtrace_id_t id, void *parg)
154 {
155 	struct kinst_probe *kp = parg;
156 	static bool warned = false;
157 
158 	if (!warned) {
159 		KINST_LOG(
160 		    "kinst: This provider is experimental, exercise caution");
161 		warned = true;
162 	}
163 
164 	kinst_patch_tracepoint(kp, kp->kp_patchval);
165 }
166 
167 static void
168 kinst_disable(void *arg, dtrace_id_t id, void *parg)
169 {
170 	struct kinst_probe *kp = parg;
171 
172 	kinst_patch_tracepoint(kp, kp->kp_savedval);
173 }
174 
175 static int
176 kinst_load(void *dummy)
177 {
178 	int error;
179 
180 	error = kinst_trampoline_init();
181 	if (error != 0)
182 		return (error);
183 	error = kinst_md_init();
184 	if (error != 0) {
185 		kinst_trampoline_deinit();
186 		return (error);
187 	}
188 
189 	error = dtrace_register("kinst", &kinst_attr, DTRACE_PRIV_USER, NULL,
190 	    &kinst_pops, NULL, &kinst_id);
191 	if (error != 0) {
192 		kinst_md_deinit();
193 		kinst_trampoline_deinit();
194 		return (error);
195 	}
196 	kinst_probetab = malloc(KINST_PROBETAB_MAX *
197 	    sizeof(struct kinst_probe_list), M_KINST, M_WAITOK | M_ZERO);
198 	for (int i = 0; i < KINST_PROBETAB_MAX; i++)
199 		LIST_INIT(&kinst_probetab[i]);
200 	kinst_cdev = make_dev(&kinst_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
201 	    "dtrace/kinst");
202 	dtrace_invop_add(kinst_invop);
203 	return (0);
204 }
205 
206 static int
207 kinst_unload(void *dummy)
208 {
209 	free(kinst_probetab, M_KINST);
210 	kinst_md_deinit();
211 	kinst_trampoline_deinit();
212 	dtrace_invop_remove(kinst_invop);
213 	destroy_dev(kinst_cdev);
214 
215 	return (dtrace_unregister(kinst_id));
216 }
217 
218 static int
219 kinst_modevent(module_t mod __unused, int type, void *data __unused)
220 {
221 	int error = 0;
222 
223 	switch (type) {
224 	case MOD_LOAD:
225 		break;
226 	case MOD_UNLOAD:
227 		break;
228 	case MOD_SHUTDOWN:
229 		break;
230 	default:
231 		error = EOPNOTSUPP;
232 		break;
233 	}
234 
235 	return (error);
236 }
237 
238 SYSINIT(kinst_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, kinst_load, NULL);
239 SYSUNINIT(kinst_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, kinst_unload,
240     NULL);
241 
242 DEV_MODULE(kinst, kinst_modevent, NULL);
243 MODULE_VERSION(kinst, 1);
244 MODULE_DEPEND(kinst, dtrace, 1, 1, 1);
245 MODULE_DEPEND(kinst, opensolaris, 1, 1, 1);
246