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 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 184 error = dtrace_register("kinst", &kinst_attr, DTRACE_PRIV_USER, NULL, 185 &kinst_pops, NULL, &kinst_id); 186 if (error != 0) { 187 kinst_trampoline_deinit(); 188 return (error); 189 } 190 kinst_probetab = malloc(KINST_PROBETAB_MAX * 191 sizeof(struct kinst_probe_list), M_KINST, M_WAITOK | M_ZERO); 192 for (int i = 0; i < KINST_PROBETAB_MAX; i++) 193 LIST_INIT(&kinst_probetab[i]); 194 kinst_cdev = make_dev(&kinst_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 195 "dtrace/kinst"); 196 dtrace_invop_add(kinst_invop); 197 return (0); 198 } 199 200 static int 201 kinst_unload(void *dummy) 202 { 203 free(kinst_probetab, M_KINST); 204 kinst_trampoline_deinit(); 205 dtrace_invop_remove(kinst_invop); 206 destroy_dev(kinst_cdev); 207 208 return (dtrace_unregister(kinst_id)); 209 } 210 211 static int 212 kinst_modevent(module_t mod __unused, int type, void *data __unused) 213 { 214 int error = 0; 215 216 switch (type) { 217 case MOD_LOAD: 218 break; 219 case MOD_UNLOAD: 220 break; 221 case MOD_SHUTDOWN: 222 break; 223 default: 224 error = EOPNOTSUPP; 225 break; 226 } 227 228 return (error); 229 } 230 231 SYSINIT(kinst_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, kinst_load, NULL); 232 SYSUNINIT(kinst_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, kinst_unload, 233 NULL); 234 235 DEV_MODULE(kinst, kinst_modevent, NULL); 236 MODULE_VERSION(kinst, 1); 237 MODULE_DEPEND(kinst, dtrace, 1, 1, 1); 238 MODULE_DEPEND(kinst, opensolaris, 1, 1, 1); 239