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);
Cache object: 589d26ca32d0d54cc1f6d6bc9cb353b4
|