1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2016-2018, Matthew Macy <mmacy@freebsd.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/queue.h> 35 #include <sys/blist.h> 36 #include <sys/conf.h> 37 #include <sys/exec.h> 38 #include <sys/filedesc.h> 39 #include <sys/kernel.h> 40 #include <sys/linker.h> 41 #include <sys/malloc.h> 42 #include <sys/mount.h> 43 #include <sys/mutex.h> 44 #include <sys/proc.h> 45 #include <sys/resourcevar.h> 46 #include <sys/sbuf.h> 47 #include <sys/smp.h> 48 #include <sys/socket.h> 49 #include <sys/vnode.h> 50 #include <sys/bus.h> 51 #include <sys/pciio.h> 52 53 #include <dev/pci/pcivar.h> 54 #include <dev/pci/pcireg.h> 55 56 #include <net/if.h> 57 58 #include <vm/vm.h> 59 #include <vm/pmap.h> 60 #include <vm/vm_map.h> 61 #include <vm/vm_param.h> 62 #include <vm/vm_object.h> 63 #include <vm/swap_pager.h> 64 65 #include <machine/bus.h> 66 67 #include <compat/linux/linux_ioctl.h> 68 #include <compat/linux/linux_mib.h> 69 #include <compat/linux/linux_util.h> 70 #include <fs/pseudofs/pseudofs.h> 71 72 #include <linux/compat.h> 73 #include <linux/debugfs.h> 74 #include <linux/fs.h> 75 76 MALLOC_DEFINE(M_DFSINT, "debugfsint", "Linux debugfs internal"); 77 78 static struct pfs_node *debugfs_root; 79 80 #define DM_SYMLINK 0x1 81 #define DM_DIR 0x2 82 #define DM_FILE 0x3 83 84 struct dentry_meta { 85 struct dentry dm_dnode; 86 const struct file_operations *dm_fops; 87 void *dm_data; 88 umode_t dm_mode; 89 int dm_type; 90 }; 91 92 static int 93 debugfs_attr(PFS_ATTR_ARGS) 94 { 95 struct dentry_meta *dm; 96 97 dm = pn->pn_data; 98 99 vap->va_mode = dm->dm_mode; 100 return (0); 101 } 102 103 static int 104 debugfs_destroy(PFS_DESTROY_ARGS) 105 { 106 struct dentry_meta *dm; 107 108 dm = pn->pn_data; 109 if (dm->dm_type == DM_SYMLINK) 110 free(dm->dm_data, M_DFSINT); 111 112 free(dm, M_DFSINT); 113 return (0); 114 } 115 116 static int 117 debugfs_fill(PFS_FILL_ARGS) 118 { 119 struct dentry_meta *d; 120 struct linux_file lf = {}; 121 struct vnode vn; 122 char *buf; 123 int rc; 124 off_t off = 0; 125 126 if ((rc = linux_set_current_flags(curthread, M_NOWAIT))) 127 return (rc); 128 129 d = pn->pn_data; 130 vn.v_data = d->dm_data; 131 132 rc = d->dm_fops->open(&vn, &lf); 133 if (rc < 0) { 134 #ifdef INVARIANTS 135 printf("%s:%d open failed with %d\n", __FUNCTION__, __LINE__, rc); 136 #endif 137 return (-rc); 138 } 139 140 rc = -ENODEV; 141 if (uio->uio_rw == UIO_READ && d->dm_fops->read) { 142 rc = -ENOMEM; 143 buf = (char *) malloc(sb->s_size, M_DFSINT, M_ZERO | M_NOWAIT); 144 if (buf != NULL) { 145 rc = d->dm_fops->read(&lf, buf, sb->s_size, &off); 146 if (rc > 0) 147 sbuf_bcpy(sb, buf, strlen(buf)); 148 149 free(buf, M_DFSINT); 150 } 151 } else if (uio->uio_rw == UIO_WRITE && d->dm_fops->write) { 152 sbuf_finish(sb); 153 rc = d->dm_fops->write(&lf, sbuf_data(sb), sbuf_len(sb), &off); 154 } 155 156 if (d->dm_fops->release) 157 d->dm_fops->release(&vn, &lf); 158 else 159 single_release(&vn, &lf); 160 161 if (rc < 0) { 162 #ifdef INVARIANTS 163 printf("%s:%d read/write failed with %d\n", __FUNCTION__, __LINE__, rc); 164 #endif 165 return (-rc); 166 } 167 return (0); 168 } 169 170 static int 171 debugfs_fill_data(PFS_FILL_ARGS) 172 { 173 struct dentry_meta *dm; 174 175 dm = pn->pn_data; 176 sbuf_printf(sb, "%s", (char *)dm->dm_data); 177 return (0); 178 } 179 180 struct dentry * 181 debugfs_create_file(const char *name, umode_t mode, 182 struct dentry *parent, void *data, 183 const struct file_operations *fops) 184 { 185 struct dentry_meta *dm; 186 struct dentry *dnode; 187 struct pfs_node *pnode; 188 int flags; 189 190 dm = malloc(sizeof(*dm), M_DFSINT, M_NOWAIT | M_ZERO); 191 if (dm == NULL) 192 return (NULL); 193 dnode = &dm->dm_dnode; 194 dm->dm_fops = fops; 195 dm->dm_data = data; 196 dm->dm_mode = mode; 197 dm->dm_type = DM_FILE; 198 if (parent != NULL) 199 pnode = parent->d_pfs_node; 200 else 201 pnode = debugfs_root; 202 203 flags = fops->write ? PFS_RDWR : PFS_RD; 204 dnode->d_pfs_node = pfs_create_file(pnode, name, debugfs_fill, 205 debugfs_attr, NULL, debugfs_destroy, flags | PFS_NOWAIT); 206 if (dnode->d_pfs_node == NULL) { 207 free(dm, M_DFSINT); 208 return (NULL); 209 } 210 dnode->d_pfs_node->pn_data = dm; 211 212 return (dnode); 213 } 214 215 /* 216 * NOTE: Files created with the _unsafe moniker will not be protected from 217 * debugfs core file removals. It is the responsibility of @fops to protect 218 * its file using debugfs_file_get() and debugfs_file_put(). 219 * 220 * FreeBSD's LinuxKPI lindebugfs does not perform file removals at the time 221 * of writing. Therefore there is no difference between functions with _unsafe 222 * and functions without _unsafe when using lindebugfs. Functions with _unsafe 223 * exist only for Linux compatibility. 224 */ 225 struct dentry * 226 debugfs_create_file_unsafe(const char *name, umode_t mode, 227 struct dentry *parent, void *data, 228 const struct file_operations *fops) 229 { 230 return (debugfs_create_file(name, mode, parent, data, fops)); 231 } 232 233 struct dentry * 234 debugfs_create_mode_unsafe(const char *name, umode_t mode, 235 struct dentry *parent, void *data, 236 const struct file_operations *fops, 237 const struct file_operations *fops_ro, 238 const struct file_operations *fops_wo) 239 { 240 umode_t read = mode & S_IRUGO; 241 umode_t write = mode & S_IWUGO; 242 243 if (read && !write) 244 return (debugfs_create_file_unsafe(name, mode, parent, data, fops_ro)); 245 246 if (write && !read) 247 return (debugfs_create_file_unsafe(name, mode, parent, data, fops_wo)); 248 249 return (debugfs_create_file_unsafe(name, mode, parent, data, fops)); 250 } 251 252 struct dentry * 253 debugfs_create_dir(const char *name, struct dentry *parent) 254 { 255 struct dentry_meta *dm; 256 struct dentry *dnode; 257 struct pfs_node *pnode; 258 259 dm = malloc(sizeof(*dm), M_DFSINT, M_NOWAIT | M_ZERO); 260 if (dm == NULL) 261 return (NULL); 262 dnode = &dm->dm_dnode; 263 dm->dm_mode = 0700; 264 dm->dm_type = DM_DIR; 265 if (parent != NULL) 266 pnode = parent->d_pfs_node; 267 else 268 pnode = debugfs_root; 269 270 dnode->d_pfs_node = pfs_create_dir(pnode, name, debugfs_attr, NULL, debugfs_destroy, PFS_RD | PFS_NOWAIT); 271 if (dnode->d_pfs_node == NULL) { 272 free(dm, M_DFSINT); 273 return (NULL); 274 } 275 dnode->d_pfs_node->pn_data = dm; 276 return (dnode); 277 } 278 279 struct dentry * 280 debugfs_create_symlink(const char *name, struct dentry *parent, 281 const char *dest) 282 { 283 struct dentry_meta *dm; 284 struct dentry *dnode; 285 struct pfs_node *pnode; 286 void *data; 287 288 data = strdup_flags(dest, M_DFSINT, M_NOWAIT); 289 if (data == NULL) 290 return (NULL); 291 dm = malloc(sizeof(*dm), M_DFSINT, M_NOWAIT | M_ZERO); 292 if (dm == NULL) 293 goto fail1; 294 dnode = &dm->dm_dnode; 295 dm->dm_mode = 0700; 296 dm->dm_type = DM_SYMLINK; 297 dm->dm_data = data; 298 if (parent != NULL) 299 pnode = parent->d_pfs_node; 300 else 301 pnode = debugfs_root; 302 303 dnode->d_pfs_node = pfs_create_link(pnode, name, &debugfs_fill_data, NULL, NULL, NULL, PFS_NOWAIT); 304 if (dnode->d_pfs_node == NULL) 305 goto fail; 306 dnode->d_pfs_node->pn_data = dm; 307 return (dnode); 308 fail: 309 free(dm, M_DFSINT); 310 fail1: 311 free(data, M_DFSINT); 312 return (NULL); 313 } 314 315 void 316 debugfs_remove(struct dentry *dnode) 317 { 318 if (dnode == NULL) 319 return; 320 321 pfs_destroy(dnode->d_pfs_node); 322 } 323 324 void 325 debugfs_remove_recursive(struct dentry *dnode) 326 { 327 if (dnode == NULL) 328 return; 329 330 pfs_destroy(dnode->d_pfs_node); 331 } 332 333 static int 334 debugfs_bool_get(void *data, uint64_t *ullval) 335 { 336 bool *bval = data; 337 338 if (*bval) 339 *ullval = 1; 340 else 341 *ullval = 0; 342 343 return (0); 344 } 345 346 static int 347 debugfs_bool_set(void *data, uint64_t ullval) 348 { 349 bool *bval = data; 350 351 if (ullval) 352 *bval = 1; 353 else 354 *bval = 0; 355 356 return (0); 357 } 358 359 DEFINE_DEBUGFS_ATTRIBUTE(fops_bool, debugfs_bool_get, debugfs_bool_set, "%llu\n"); 360 DEFINE_DEBUGFS_ATTRIBUTE(fops_bool_ro, debugfs_bool_get, NULL, "%llu\n"); 361 DEFINE_DEBUGFS_ATTRIBUTE(fops_bool_wo, NULL, debugfs_bool_set, "%llu\n"); 362 363 void 364 debugfs_create_bool(const char *name, umode_t mode, struct dentry *parent, bool *value) 365 { 366 debugfs_create_mode_unsafe(name, mode, parent, value, &fops_bool, 367 &fops_bool_ro, &fops_bool_wo); 368 } 369 370 static int 371 debugfs_ulong_get(void *data, uint64_t *value) 372 { 373 uint64_t *uldata = data; 374 *value = *uldata; 375 return (0); 376 } 377 378 static int 379 debugfs_ulong_set(void *data, uint64_t value) 380 { 381 uint64_t *uldata = data; 382 *uldata = value; 383 return (0); 384 } 385 386 DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong, debugfs_ulong_get, debugfs_ulong_set, "%llu\n"); 387 DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong_ro, debugfs_ulong_get, NULL, "%llu\n"); 388 DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong_wo, NULL, debugfs_ulong_set, "%llu\n"); 389 390 void 391 debugfs_create_ulong(const char *name, umode_t mode, struct dentry *parent, unsigned long *value) 392 { 393 debugfs_create_mode_unsafe(name, mode, parent, value, &fops_ulong, 394 &fops_ulong_ro, &fops_ulong_wo); 395 } 396 397 static int 398 lindebugfs_init(PFS_INIT_ARGS) 399 { 400 401 debugfs_root = pi->pi_root; 402 403 (void)debugfs_create_symlink("kcov", NULL, "/dev/kcov"); 404 405 return (0); 406 } 407 408 static int 409 lindebugfs_uninit(PFS_INIT_ARGS) 410 { 411 return (0); 412 } 413 414 PSEUDOFS(lindebugfs, 1, VFCF_JAIL); 415 MODULE_DEPEND(lindebugfs, linuxkpi, 1, 1, 1); 416