Path: blob/main/sys/compat/lindebugfs/lindebugfs.c
39483 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2016-2018, Matthew Macy <[email protected]>4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*26*/2728#include <sys/param.h>29#include <sys/systm.h>30#include <sys/queue.h>31#include <sys/blist.h>32#include <sys/conf.h>33#include <sys/exec.h>34#include <sys/filedesc.h>35#include <sys/kernel.h>36#include <sys/linker.h>37#include <sys/malloc.h>38#include <sys/mount.h>39#include <sys/mutex.h>40#include <sys/proc.h>41#include <sys/resourcevar.h>42#include <sys/sbuf.h>43#include <sys/smp.h>44#include <sys/socket.h>45#include <sys/vnode.h>46#include <sys/bus.h>47#include <sys/pciio.h>4849#include <dev/pci/pcivar.h>50#include <dev/pci/pcireg.h>5152#include <net/if.h>5354#include <vm/vm.h>55#include <vm/pmap.h>56#include <vm/vm_map.h>57#include <vm/vm_param.h>58#include <vm/vm_object.h>59#include <vm/swap_pager.h>6061#include <machine/bus.h>6263#include <compat/linux/linux_ioctl.h>64#include <compat/linux/linux_mib.h>65#include <compat/linux/linux_util.h>66#include <fs/pseudofs/pseudofs.h>6768#include <asm/atomic.h>69#include <linux/compat.h>70#include <linux/debugfs.h>71#include <linux/fs.h>7273MALLOC_DEFINE(M_DFSINT, "debugfsint", "Linux debugfs internal");7475static struct pfs_node *debugfs_root;7677#define DM_SYMLINK 0x178#define DM_DIR 0x279#define DM_FILE 0x38081struct dentry_meta {82struct dentry dm_dnode;83const struct file_operations *dm_fops;84void *dm_data;85umode_t dm_mode;86int dm_type;87};8889static int90debugfs_attr(PFS_ATTR_ARGS)91{92struct dentry_meta *dm;9394dm = pn->pn_data;9596vap->va_mode = dm->dm_mode;97return (0);98}99100static int101debugfs_destroy(PFS_DESTROY_ARGS)102{103struct dentry_meta *dm;104105dm = pn->pn_data;106if (dm != NULL && dm->dm_type == DM_SYMLINK)107free(dm->dm_data, M_DFSINT);108109free(dm, M_DFSINT);110return (0);111}112113static int114debugfs_fill(PFS_FILL_ARGS)115{116struct dentry_meta *d;117struct linux_file lf = {};118struct vnode vn;119char *buf;120int rc;121off_t off = 0;122123if ((rc = linux_set_current_flags(curthread, M_NOWAIT)))124return (rc);125126d = pn->pn_data;127vn.v_data = d->dm_data;128129rc = d->dm_fops->open(&vn, &lf);130if (rc < 0) {131#ifdef INVARIANTS132printf("%s:%d open failed with %d\n", __func__, __LINE__, rc);133#endif134return (-rc);135}136137rc = -ENODEV;138switch (uio->uio_rw) {139case UIO_READ:140if (d->dm_fops->read != NULL) {141rc = -ENOMEM;142buf = malloc(sb->s_size, M_DFSINT, M_ZERO | M_NOWAIT);143if (buf != NULL) {144rc = d->dm_fops->read(&lf, buf, sb->s_size,145&off);146if (rc > 0)147sbuf_bcpy(sb, buf, strlen(buf));148149free(buf, M_DFSINT);150}151}152break;153case UIO_WRITE:154if (d->dm_fops->write != NULL) {155sbuf_finish(sb);156rc = d->dm_fops->write(&lf, sbuf_data(sb), sbuf_len(sb),157&off);158}159break;160}161162if (d->dm_fops->release)163d->dm_fops->release(&vn, &lf);164165if (rc < 0) {166#ifdef INVARIANTS167printf("%s:%d read/write failed with %d\n", __func__, __LINE__, rc);168#endif169return (-rc);170}171return (0);172}173174static int175debugfs_fill_data(PFS_FILL_ARGS)176{177struct dentry_meta *dm;178179dm = pn->pn_data;180sbuf_printf(sb, "%s", (char *)dm->dm_data);181return (0);182}183184struct dentry *185debugfs_create_file(const char *name, umode_t mode,186struct dentry *parent, void *data,187const struct file_operations *fops)188{189struct dentry_meta *dm;190struct dentry *dnode;191struct pfs_node *pnode;192int flags;193194dm = malloc(sizeof(*dm), M_DFSINT, M_NOWAIT | M_ZERO);195if (dm == NULL)196return (NULL);197dnode = &dm->dm_dnode;198dm->dm_fops = fops;199dm->dm_data = data;200dm->dm_mode = mode;201dm->dm_type = DM_FILE;202if (parent != NULL)203pnode = parent->d_pfs_node;204else205pnode = debugfs_root;206207flags = fops->write ? PFS_RDWR : PFS_RD;208pfs_create_file(pnode, &dnode->d_pfs_node, name, debugfs_fill,209debugfs_attr, NULL, debugfs_destroy, flags | PFS_NOWAIT);210if (dnode->d_pfs_node == NULL) {211free(dm, M_DFSINT);212return (NULL);213}214dnode->d_pfs_node->pn_data = dm;215216return (dnode);217}218219struct dentry *220debugfs_create_file_size(const char *name, umode_t mode,221struct dentry *parent, void *data,222const struct file_operations *fops,223loff_t file_size __unused)224{225226return debugfs_create_file(name, mode, parent, data, fops);227}228229/*230* NOTE: Files created with the _unsafe moniker will not be protected from231* debugfs core file removals. It is the responsibility of @fops to protect232* its file using debugfs_file_get() and debugfs_file_put().233*234* FreeBSD's LinuxKPI lindebugfs does not perform file removals at the time235* of writing. Therefore there is no difference between functions with _unsafe236* and functions without _unsafe when using lindebugfs. Functions with _unsafe237* exist only for Linux compatibility.238*/239struct dentry *240debugfs_create_file_unsafe(const char *name, umode_t mode,241struct dentry *parent, void *data,242const struct file_operations *fops)243{244245return (debugfs_create_file(name, mode, parent, data, fops));246}247248struct dentry *249debugfs_create_mode_unsafe(const char *name, umode_t mode,250struct dentry *parent, void *data,251const struct file_operations *fops,252const struct file_operations *fops_ro,253const struct file_operations *fops_wo)254{255umode_t read = mode & S_IRUGO;256umode_t write = mode & S_IWUGO;257258if (read && !write)259return (debugfs_create_file_unsafe(name, mode, parent, data, fops_ro));260261if (write && !read)262return (debugfs_create_file_unsafe(name, mode, parent, data, fops_wo));263264return (debugfs_create_file_unsafe(name, mode, parent, data, fops));265}266267struct dentry *268debugfs_create_dir(const char *name, struct dentry *parent)269{270struct dentry_meta *dm;271struct dentry *dnode;272struct pfs_node *pnode;273274dm = malloc(sizeof(*dm), M_DFSINT, M_NOWAIT | M_ZERO);275if (dm == NULL)276return (NULL);277dnode = &dm->dm_dnode;278dm->dm_mode = 0700;279dm->dm_type = DM_DIR;280if (parent != NULL)281pnode = parent->d_pfs_node;282else283pnode = debugfs_root;284285pfs_create_dir(pnode, &dnode->d_pfs_node, name, debugfs_attr, NULL,286debugfs_destroy, PFS_RD | PFS_NOWAIT);287if (dnode->d_pfs_node == NULL) {288free(dm, M_DFSINT);289return (NULL);290}291dnode->d_pfs_node->pn_data = dm;292return (dnode);293}294295struct dentry *296debugfs_create_symlink(const char *name, struct dentry *parent,297const char *dest)298{299struct dentry_meta *dm;300struct dentry *dnode;301struct pfs_node *pnode;302void *data;303304data = strdup_flags(dest, M_DFSINT, M_NOWAIT);305if (data == NULL)306return (NULL);307dm = malloc(sizeof(*dm), M_DFSINT, M_NOWAIT | M_ZERO);308if (dm == NULL)309goto fail1;310dnode = &dm->dm_dnode;311dm->dm_mode = 0700;312dm->dm_type = DM_SYMLINK;313dm->dm_data = data;314if (parent != NULL)315pnode = parent->d_pfs_node;316else317pnode = debugfs_root;318319pfs_create_link(pnode, &dnode->d_pfs_node, name, &debugfs_fill_data,320NULL, NULL, NULL, PFS_NOWAIT);321if (dnode->d_pfs_node == NULL)322goto fail;323dnode->d_pfs_node->pn_data = dm;324return (dnode);325fail:326free(dm, M_DFSINT);327fail1:328free(data, M_DFSINT);329return (NULL);330}331332struct dentry *333debugfs_lookup(const char *name, struct dentry *parent)334{335struct dentry_meta *dm;336struct dentry *dnode;337struct pfs_node *pnode;338339pnode = pfs_find_node(parent->d_pfs_node, name);340if (pnode == NULL)341return (NULL);342343dm = (struct dentry_meta *)pnode->pn_data;344dnode = &dm->dm_dnode;345346return (dnode);347}348349void350debugfs_remove(struct dentry *dnode)351{352if (dnode == NULL)353return;354355pfs_destroy(dnode->d_pfs_node);356}357358void359debugfs_remove_recursive(struct dentry *dnode)360{361if (dnode == NULL)362return;363364pfs_destroy(dnode->d_pfs_node);365}366367static int368debugfs_bool_get(void *data, uint64_t *ullval)369{370bool *bval = data;371372if (*bval)373*ullval = 1;374else375*ullval = 0;376377return (0);378}379380static int381debugfs_bool_set(void *data, uint64_t ullval)382{383bool *bval = data;384385if (ullval)386*bval = 1;387else388*bval = 0;389390return (0);391}392393DEFINE_DEBUGFS_ATTRIBUTE(fops_bool, debugfs_bool_get, debugfs_bool_set, "%llu\n");394DEFINE_DEBUGFS_ATTRIBUTE(fops_bool_ro, debugfs_bool_get, NULL, "%llu\n");395DEFINE_DEBUGFS_ATTRIBUTE(fops_bool_wo, NULL, debugfs_bool_set, "%llu\n");396397void398debugfs_create_bool(const char *name, umode_t mode, struct dentry *parent, bool *value)399{400401debugfs_create_mode_unsafe(name, mode, parent, value, &fops_bool,402&fops_bool_ro, &fops_bool_wo);403}404405406static int407debugfs_u8_get(void *data, uint64_t *value)408{409uint8_t *u8data = data;410*value = *u8data;411return (0);412}413414static int415debugfs_u8_set(void *data, uint64_t value)416{417uint8_t *u8data = data;418*u8data = (uint8_t)value;419return (0);420}421422DEFINE_DEBUGFS_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%u\n");423DEFINE_DEBUGFS_ATTRIBUTE(fops_u8_ro, debugfs_u8_get, NULL, "%u\n");424DEFINE_DEBUGFS_ATTRIBUTE(fops_u8_wo, NULL, debugfs_u8_set, "%u\n");425426void427debugfs_create_u8(const char *name, umode_t mode, struct dentry *parent, uint8_t *value)428{429430debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u8,431&fops_u8_ro, &fops_u8_wo);432}433434DEFINE_DEBUGFS_ATTRIBUTE(fops_x8, debugfs_u8_get, debugfs_u8_set, "0x%016llx\n");435DEFINE_DEBUGFS_ATTRIBUTE(fops_x8_ro, debugfs_u8_get, NULL, "0x%016llx\n");436DEFINE_DEBUGFS_ATTRIBUTE(fops_x8_wo, NULL, debugfs_u8_set, "0x%016llx\n");437438void439debugfs_create_x8(const char *name, umode_t mode, struct dentry *parent, uint8_t *value)440{441442debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x8,443&fops_x8_ro, &fops_x8_wo);444}445446447static int448debugfs_u16_get(void *data, uint64_t *value)449{450uint16_t *u16data = data;451*value = *u16data;452return (0);453}454455static int456debugfs_u16_set(void *data, uint64_t value)457{458uint16_t *u16data = data;459*u16data = (uint16_t)value;460return (0);461}462463DEFINE_DEBUGFS_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%u\n");464DEFINE_DEBUGFS_ATTRIBUTE(fops_u16_ro, debugfs_u16_get, NULL, "%u\n");465DEFINE_DEBUGFS_ATTRIBUTE(fops_u16_wo, NULL, debugfs_u16_set, "%u\n");466467void468debugfs_create_u16(const char *name, umode_t mode, struct dentry *parent, uint16_t *value)469{470471debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u16,472&fops_u16_ro, &fops_u16_wo);473}474475DEFINE_DEBUGFS_ATTRIBUTE(fops_x16, debugfs_u16_get, debugfs_u16_set, "0x%016llx\n");476DEFINE_DEBUGFS_ATTRIBUTE(fops_x16_ro, debugfs_u16_get, NULL, "0x%016llx\n");477DEFINE_DEBUGFS_ATTRIBUTE(fops_x16_wo, NULL, debugfs_u16_set, "0x%016llx\n");478479void480debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent, uint16_t *value)481{482483debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x16,484&fops_x16_ro, &fops_x16_wo);485}486487488static int489debugfs_u32_get(void *data, uint64_t *value)490{491uint32_t *u32data = data;492*value = *u32data;493return (0);494}495496static int497debugfs_u32_set(void *data, uint64_t value)498{499uint32_t *u32data = data;500*u32data = (uint32_t)value;501return (0);502}503504DEFINE_DEBUGFS_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%u\n");505DEFINE_DEBUGFS_ATTRIBUTE(fops_u32_ro, debugfs_u32_get, NULL, "%u\n");506DEFINE_DEBUGFS_ATTRIBUTE(fops_u32_wo, NULL, debugfs_u32_set, "%u\n");507508void509debugfs_create_u32(const char *name, umode_t mode, struct dentry *parent, uint32_t *value)510{511512debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u32,513&fops_u32_ro, &fops_u32_wo);514}515516DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, debugfs_u32_get, debugfs_u32_set, "0x%016llx\n");517DEFINE_DEBUGFS_ATTRIBUTE(fops_x32_ro, debugfs_u32_get, NULL, "0x%016llx\n");518DEFINE_DEBUGFS_ATTRIBUTE(fops_x32_wo, NULL, debugfs_u32_set, "0x%016llx\n");519520void521debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent, uint32_t *value)522{523524debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x32,525&fops_x32_ro, &fops_x32_wo);526}527528529static int530debugfs_u64_get(void *data, uint64_t *value)531{532uint64_t *u64data = data;533*value = *u64data;534return (0);535}536537static int538debugfs_u64_set(void *data, uint64_t value)539{540uint64_t *u64data = data;541*u64data = (uint64_t)value;542return (0);543}544545DEFINE_DEBUGFS_ATTRIBUTE(fops_u64, debugfs_u64_get, debugfs_u64_set, "%u\n");546DEFINE_DEBUGFS_ATTRIBUTE(fops_u64_ro, debugfs_u64_get, NULL, "%u\n");547DEFINE_DEBUGFS_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%u\n");548549void550debugfs_create_u64(const char *name, umode_t mode, struct dentry *parent, uint64_t *value)551{552553debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u64,554&fops_u64_ro, &fops_u64_wo);555}556557DEFINE_DEBUGFS_ATTRIBUTE(fops_x64, debugfs_u64_get, debugfs_u64_set, "0x%016llx\n");558DEFINE_DEBUGFS_ATTRIBUTE(fops_x64_ro, debugfs_u64_get, NULL, "0x%016llx\n");559DEFINE_DEBUGFS_ATTRIBUTE(fops_x64_wo, NULL, debugfs_u64_set, "0x%016llx\n");560561void562debugfs_create_x64(const char *name, umode_t mode, struct dentry *parent, uint64_t *value)563{564565debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x64,566&fops_x64_ro, &fops_x64_wo);567}568569570static int571debugfs_ulong_get(void *data, uint64_t *value)572{573uint64_t *uldata = data;574*value = *uldata;575return (0);576}577578static int579debugfs_ulong_set(void *data, uint64_t value)580{581uint64_t *uldata = data;582*uldata = value;583return (0);584}585586DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong, debugfs_ulong_get, debugfs_ulong_set, "%llu\n");587DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong_ro, debugfs_ulong_get, NULL, "%llu\n");588DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong_wo, NULL, debugfs_ulong_set, "%llu\n");589590void591debugfs_create_ulong(const char *name, umode_t mode, struct dentry *parent, unsigned long *value)592{593594debugfs_create_mode_unsafe(name, mode, parent, value, &fops_ulong,595&fops_ulong_ro, &fops_ulong_wo);596}597598599static int600debugfs_atomic_t_get(void *data, uint64_t *value)601{602atomic_t *atomic_data = data;603*value = atomic_read(atomic_data);604return (0);605}606607static int608debugfs_atomic_t_set(void *data, uint64_t value)609{610atomic_t *atomic_data = data;611atomic_set(atomic_data, (int)value);612return (0);613}614615DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t, debugfs_atomic_t_get, debugfs_atomic_t_set, "%d\n");616DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t_ro, debugfs_atomic_t_get, NULL, "%d\n");617DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t_wo, NULL, debugfs_atomic_t_set, "%d\n");618619void620debugfs_create_atomic_t(const char *name, umode_t mode, struct dentry *parent, atomic_t *value)621{622623debugfs_create_mode_unsafe(name, mode, parent, value, &fops_atomic_t,624&fops_atomic_t_ro, &fops_atomic_t_wo);625}626627628static int629fops_str_open(struct inode *inode, struct file *filp)630{631632return (simple_open(inode, filp));633}634635static ssize_t636fops_str_read(struct file *filp, char __user *ubuf, size_t read_size,637loff_t *ppos)638{639ssize_t ret;640char *str, *str_with_newline;641size_t str_len, str_with_newline_len;642643if (filp->private_data == NULL)644return (-EINVAL);645646str = *(char **)filp->private_data;647str_len = strlen(str);648649/*650* `str_with_newline` is terminated with a newline, but is not651* NUL-terminated.652*/653str_with_newline_len = str_len + 1;654str_with_newline = kmalloc(str_with_newline_len, GFP_KERNEL);655if (str_with_newline == NULL)656return (-ENOMEM);657658strncpy(str_with_newline, str, str_len);659str_with_newline[str_len] = '\n';660661ret = simple_read_from_buffer(ubuf, read_size, ppos,662str_with_newline, str_with_newline_len);663664kfree(str_with_newline);665666return (ret);667}668669static ssize_t670fops_str_write(struct file *filp, const char *buf, size_t write_size,671loff_t *ppos)672{673char *old, *new;674size_t old_len, new_len;675676if (filp->private_data == NULL)677return (-EINVAL);678679old = *(char **)filp->private_data;680new = NULL;681682/*683* We enforce concatenation of the newly written value to the existing684* value.685*/686old_len = strlen(old);687if (*ppos && *ppos != old_len)688return (-EINVAL);689690new_len = old_len + write_size;691if (new_len + 1 > PAGE_SIZE)692return (-E2BIG);693694new = kmalloc(new_len + 1, GFP_KERNEL);695if (new == NULL)696return (-ENOMEM);697698memcpy(new, old, old_len);699if (copy_from_user(new + old_len, buf, write_size) != 0) {700kfree(new);701return (-EFAULT);702}703704new[new_len] = '\0';705strim(new);706707filp->private_data = &new;708709kfree(old);710711return (write_size);712}713714static const struct file_operations fops_str = {715.owner = THIS_MODULE,716.open = fops_str_open,717.read = fops_str_read,718.write = fops_str_write,719.llseek = no_llseek720};721static const struct file_operations fops_str_ro = {722.owner = THIS_MODULE,723.open = fops_str_open,724.read = fops_str_read,725.llseek = no_llseek726};727static const struct file_operations fops_str_wo = {728.owner = THIS_MODULE,729.open = fops_str_open,730.write = fops_str_write,731.llseek = no_llseek732};733734void735debugfs_create_str(const char *name, umode_t mode, struct dentry *parent,736char **value)737{738debugfs_create_mode_unsafe(name, mode, parent, value,739&fops_str, &fops_str_ro, &fops_str_wo);740}741742743static ssize_t744fops_blob_read(struct file *filp, char __user *ubuf, size_t read_size, loff_t *ppos)745{746struct debugfs_blob_wrapper *blob;747748blob = filp->private_data;749if (blob == NULL)750return (-EINVAL);751if (blob->size == 0 || blob->data == NULL)752return (-EINVAL);753754return (simple_read_from_buffer(ubuf, read_size, ppos, blob->data, blob->size));755}756757static int758fops_blob_open(struct inode *inode, struct file *filp)759{760761return (simple_open(inode, filp));762}763764static const struct file_operations __fops_blob_ro = {765.owner = THIS_MODULE,766.open = fops_blob_open,767.read = fops_blob_read,768.llseek = no_llseek769};770771struct dentry *772debugfs_create_blob(const char *name, umode_t mode, struct dentry *parent,773struct debugfs_blob_wrapper *value)774{775/* Blobs are read-only. */776return (debugfs_create_file(name, mode & 0444, parent, value, &__fops_blob_ro));777}778779780static int781lindebugfs_init(PFS_INIT_ARGS)782{783784debugfs_root = pi->pi_root;785786(void)debugfs_create_symlink("kcov", NULL, "/dev/kcov");787788return (0);789}790791static int792lindebugfs_uninit(PFS_INIT_ARGS)793{794795return (0);796}797798PSEUDOFS(lindebugfs, 1, VFCF_JAIL);799MODULE_DEPEND(lindebugfs, linuxkpi, 1, 1, 1);800801802