18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  file.c - part of debugfs, a tiny little debug file system
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
68c2ecf20Sopenharmony_ci *  Copyright (C) 2004 IBM Inc.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  debugfs is for people to use instead of /proc or /sys.
98c2ecf20Sopenharmony_ci *  See Documentation/filesystems/ for more details.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/fs.h>
148c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
158c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
168c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
178c2ecf20Sopenharmony_ci#include <linux/io.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/atomic.h>
208c2ecf20Sopenharmony_ci#include <linux/device.h>
218c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
228c2ecf20Sopenharmony_ci#include <linux/poll.h>
238c2ecf20Sopenharmony_ci#include <linux/security.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "internal.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct poll_table_struct;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic ssize_t default_read_file(struct file *file, char __user *buf,
308c2ecf20Sopenharmony_ci				 size_t count, loff_t *ppos)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	return 0;
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic ssize_t default_write_file(struct file *file, const char __user *buf,
368c2ecf20Sopenharmony_ci				   size_t count, loff_t *ppos)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	return count;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ciconst struct file_operations debugfs_noop_file_operations = {
428c2ecf20Sopenharmony_ci	.read =		default_read_file,
438c2ecf20Sopenharmony_ci	.write =	default_write_file,
448c2ecf20Sopenharmony_ci	.open =		simple_open,
458c2ecf20Sopenharmony_ci	.llseek =	noop_llseek,
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define F_DENTRY(filp) ((filp)->f_path.dentry)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ciconst struct file_operations *debugfs_real_fops(const struct file *filp)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct debugfs_fsdata *fsd = F_DENTRY(filp)->d_fsdata;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT) {
558c2ecf20Sopenharmony_ci		/*
568c2ecf20Sopenharmony_ci		 * Urgh, we've been called w/o a protecting
578c2ecf20Sopenharmony_ci		 * debugfs_file_get().
588c2ecf20Sopenharmony_ci		 */
598c2ecf20Sopenharmony_ci		WARN_ON(1);
608c2ecf20Sopenharmony_ci		return NULL;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return fsd->real_fops;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_real_fops);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/**
688c2ecf20Sopenharmony_ci * debugfs_file_get - mark the beginning of file data access
698c2ecf20Sopenharmony_ci * @dentry: the dentry object whose data is being accessed.
708c2ecf20Sopenharmony_ci *
718c2ecf20Sopenharmony_ci * Up to a matching call to debugfs_file_put(), any successive call
728c2ecf20Sopenharmony_ci * into the file removing functions debugfs_remove() and
738c2ecf20Sopenharmony_ci * debugfs_remove_recursive() will block. Since associated private
748c2ecf20Sopenharmony_ci * file data may only get freed after a successful return of any of
758c2ecf20Sopenharmony_ci * the removal functions, you may safely access it after a successful
768c2ecf20Sopenharmony_ci * call to debugfs_file_get() without worrying about lifetime issues.
778c2ecf20Sopenharmony_ci *
788c2ecf20Sopenharmony_ci * If -%EIO is returned, the file has already been removed and thus,
798c2ecf20Sopenharmony_ci * it is not safe to access any of its data. If, on the other hand,
808c2ecf20Sopenharmony_ci * it is allowed to access the file data, zero is returned.
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_ciint debugfs_file_get(struct dentry *dentry)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct debugfs_fsdata *fsd;
858c2ecf20Sopenharmony_ci	void *d_fsd;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/*
888c2ecf20Sopenharmony_ci	 * This could only happen if some debugfs user erroneously calls
898c2ecf20Sopenharmony_ci	 * debugfs_file_get() on a dentry that isn't even a file, let
908c2ecf20Sopenharmony_ci	 * them know about it.
918c2ecf20Sopenharmony_ci	 */
928c2ecf20Sopenharmony_ci	if (WARN_ON(!d_is_reg(dentry)))
938c2ecf20Sopenharmony_ci		return -EINVAL;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	d_fsd = READ_ONCE(dentry->d_fsdata);
968c2ecf20Sopenharmony_ci	if (!((unsigned long)d_fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)) {
978c2ecf20Sopenharmony_ci		fsd = d_fsd;
988c2ecf20Sopenharmony_ci	} else {
998c2ecf20Sopenharmony_ci		fsd = kmalloc(sizeof(*fsd), GFP_KERNEL);
1008c2ecf20Sopenharmony_ci		if (!fsd)
1018c2ecf20Sopenharmony_ci			return -ENOMEM;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		fsd->real_fops = (void *)((unsigned long)d_fsd &
1048c2ecf20Sopenharmony_ci					~DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
1058c2ecf20Sopenharmony_ci		refcount_set(&fsd->active_users, 1);
1068c2ecf20Sopenharmony_ci		init_completion(&fsd->active_users_drained);
1078c2ecf20Sopenharmony_ci		if (cmpxchg(&dentry->d_fsdata, d_fsd, fsd) != d_fsd) {
1088c2ecf20Sopenharmony_ci			kfree(fsd);
1098c2ecf20Sopenharmony_ci			fsd = READ_ONCE(dentry->d_fsdata);
1108c2ecf20Sopenharmony_ci		}
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/*
1148c2ecf20Sopenharmony_ci	 * In case of a successful cmpxchg() above, this check is
1158c2ecf20Sopenharmony_ci	 * strictly necessary and must follow it, see the comment in
1168c2ecf20Sopenharmony_ci	 * __debugfs_remove_file().
1178c2ecf20Sopenharmony_ci	 * OTOH, if the cmpxchg() hasn't been executed or wasn't
1188c2ecf20Sopenharmony_ci	 * successful, this serves the purpose of not starving
1198c2ecf20Sopenharmony_ci	 * removers.
1208c2ecf20Sopenharmony_ci	 */
1218c2ecf20Sopenharmony_ci	if (d_unlinked(dentry))
1228c2ecf20Sopenharmony_ci		return -EIO;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (!refcount_inc_not_zero(&fsd->active_users))
1258c2ecf20Sopenharmony_ci		return -EIO;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_file_get);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci/**
1328c2ecf20Sopenharmony_ci * debugfs_file_put - mark the end of file data access
1338c2ecf20Sopenharmony_ci * @dentry: the dentry object formerly passed to
1348c2ecf20Sopenharmony_ci *          debugfs_file_get().
1358c2ecf20Sopenharmony_ci *
1368c2ecf20Sopenharmony_ci * Allow any ongoing concurrent call into debugfs_remove() or
1378c2ecf20Sopenharmony_ci * debugfs_remove_recursive() blocked by a former call to
1388c2ecf20Sopenharmony_ci * debugfs_file_get() to proceed and return to its caller.
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_civoid debugfs_file_put(struct dentry *dentry)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct debugfs_fsdata *fsd = READ_ONCE(dentry->d_fsdata);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&fsd->active_users))
1458c2ecf20Sopenharmony_ci		complete(&fsd->active_users_drained);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_file_put);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/*
1508c2ecf20Sopenharmony_ci * Only permit access to world-readable files when the kernel is locked down.
1518c2ecf20Sopenharmony_ci * We also need to exclude any file that has ways to write or alter it as root
1528c2ecf20Sopenharmony_ci * can bypass the permissions check.
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_cistatic int debugfs_locked_down(struct inode *inode,
1558c2ecf20Sopenharmony_ci			       struct file *filp,
1568c2ecf20Sopenharmony_ci			       const struct file_operations *real_fops)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	if ((inode->i_mode & 07777 & ~0444) == 0 &&
1598c2ecf20Sopenharmony_ci	    !(filp->f_mode & FMODE_WRITE) &&
1608c2ecf20Sopenharmony_ci	    !real_fops->unlocked_ioctl &&
1618c2ecf20Sopenharmony_ci	    !real_fops->compat_ioctl &&
1628c2ecf20Sopenharmony_ci	    !real_fops->mmap)
1638c2ecf20Sopenharmony_ci		return 0;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if (security_locked_down(LOCKDOWN_DEBUGFS))
1668c2ecf20Sopenharmony_ci		return -EPERM;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int open_proxy_open(struct inode *inode, struct file *filp)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct dentry *dentry = F_DENTRY(filp);
1748c2ecf20Sopenharmony_ci	const struct file_operations *real_fops = NULL;
1758c2ecf20Sopenharmony_ci	int r;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	r = debugfs_file_get(dentry);
1788c2ecf20Sopenharmony_ci	if (r)
1798c2ecf20Sopenharmony_ci		return r == -EIO ? -ENOENT : r;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	real_fops = debugfs_real_fops(filp);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	r = debugfs_locked_down(inode, filp, real_fops);
1848c2ecf20Sopenharmony_ci	if (r)
1858c2ecf20Sopenharmony_ci		goto out;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (!fops_get(real_fops)) {
1888c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES
1898c2ecf20Sopenharmony_ci		if (real_fops->owner &&
1908c2ecf20Sopenharmony_ci		    real_fops->owner->state == MODULE_STATE_GOING) {
1918c2ecf20Sopenharmony_ci			r = -ENXIO;
1928c2ecf20Sopenharmony_ci			goto out;
1938c2ecf20Sopenharmony_ci		}
1948c2ecf20Sopenharmony_ci#endif
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci		/* Huh? Module did not clean up after itself at exit? */
1978c2ecf20Sopenharmony_ci		WARN(1, "debugfs file owner did not clean up at exit: %pd",
1988c2ecf20Sopenharmony_ci			dentry);
1998c2ecf20Sopenharmony_ci		r = -ENXIO;
2008c2ecf20Sopenharmony_ci		goto out;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	replace_fops(filp, real_fops);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (real_fops->open)
2058c2ecf20Sopenharmony_ci		r = real_fops->open(inode, filp);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ciout:
2088c2ecf20Sopenharmony_ci	debugfs_file_put(dentry);
2098c2ecf20Sopenharmony_ci	return r;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ciconst struct file_operations debugfs_open_proxy_file_operations = {
2138c2ecf20Sopenharmony_ci	.open = open_proxy_open,
2148c2ecf20Sopenharmony_ci};
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci#define PROTO(args...) args
2178c2ecf20Sopenharmony_ci#define ARGS(args...) args
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci#define FULL_PROXY_FUNC(name, ret_type, filp, proto, args)		\
2208c2ecf20Sopenharmony_cistatic ret_type full_proxy_ ## name(proto)				\
2218c2ecf20Sopenharmony_ci{									\
2228c2ecf20Sopenharmony_ci	struct dentry *dentry = F_DENTRY(filp);			\
2238c2ecf20Sopenharmony_ci	const struct file_operations *real_fops;			\
2248c2ecf20Sopenharmony_ci	ret_type r;							\
2258c2ecf20Sopenharmony_ci									\
2268c2ecf20Sopenharmony_ci	r = debugfs_file_get(dentry);					\
2278c2ecf20Sopenharmony_ci	if (unlikely(r))						\
2288c2ecf20Sopenharmony_ci		return r;						\
2298c2ecf20Sopenharmony_ci	real_fops = debugfs_real_fops(filp);				\
2308c2ecf20Sopenharmony_ci	r = real_fops->name(args);					\
2318c2ecf20Sopenharmony_ci	debugfs_file_put(dentry);					\
2328c2ecf20Sopenharmony_ci	return r;							\
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ciFULL_PROXY_FUNC(llseek, loff_t, filp,
2368c2ecf20Sopenharmony_ci		PROTO(struct file *filp, loff_t offset, int whence),
2378c2ecf20Sopenharmony_ci		ARGS(filp, offset, whence));
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ciFULL_PROXY_FUNC(read, ssize_t, filp,
2408c2ecf20Sopenharmony_ci		PROTO(struct file *filp, char __user *buf, size_t size,
2418c2ecf20Sopenharmony_ci			loff_t *ppos),
2428c2ecf20Sopenharmony_ci		ARGS(filp, buf, size, ppos));
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ciFULL_PROXY_FUNC(write, ssize_t, filp,
2458c2ecf20Sopenharmony_ci		PROTO(struct file *filp, const char __user *buf, size_t size,
2468c2ecf20Sopenharmony_ci			loff_t *ppos),
2478c2ecf20Sopenharmony_ci		ARGS(filp, buf, size, ppos));
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ciFULL_PROXY_FUNC(unlocked_ioctl, long, filp,
2508c2ecf20Sopenharmony_ci		PROTO(struct file *filp, unsigned int cmd, unsigned long arg),
2518c2ecf20Sopenharmony_ci		ARGS(filp, cmd, arg));
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic __poll_t full_proxy_poll(struct file *filp,
2548c2ecf20Sopenharmony_ci				struct poll_table_struct *wait)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct dentry *dentry = F_DENTRY(filp);
2578c2ecf20Sopenharmony_ci	__poll_t r = 0;
2588c2ecf20Sopenharmony_ci	const struct file_operations *real_fops;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (debugfs_file_get(dentry))
2618c2ecf20Sopenharmony_ci		return EPOLLHUP;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	real_fops = debugfs_real_fops(filp);
2648c2ecf20Sopenharmony_ci	r = real_fops->poll(filp, wait);
2658c2ecf20Sopenharmony_ci	debugfs_file_put(dentry);
2668c2ecf20Sopenharmony_ci	return r;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int full_proxy_release(struct inode *inode, struct file *filp)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	const struct dentry *dentry = F_DENTRY(filp);
2728c2ecf20Sopenharmony_ci	const struct file_operations *real_fops = debugfs_real_fops(filp);
2738c2ecf20Sopenharmony_ci	const struct file_operations *proxy_fops = filp->f_op;
2748c2ecf20Sopenharmony_ci	int r = 0;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/*
2778c2ecf20Sopenharmony_ci	 * We must not protect this against removal races here: the
2788c2ecf20Sopenharmony_ci	 * original releaser should be called unconditionally in order
2798c2ecf20Sopenharmony_ci	 * not to leak any resources. Releasers must not assume that
2808c2ecf20Sopenharmony_ci	 * ->i_private is still being meaningful here.
2818c2ecf20Sopenharmony_ci	 */
2828c2ecf20Sopenharmony_ci	if (real_fops->release)
2838c2ecf20Sopenharmony_ci		r = real_fops->release(inode, filp);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	replace_fops(filp, d_inode(dentry)->i_fop);
2868c2ecf20Sopenharmony_ci	kfree(proxy_fops);
2878c2ecf20Sopenharmony_ci	fops_put(real_fops);
2888c2ecf20Sopenharmony_ci	return r;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic void __full_proxy_fops_init(struct file_operations *proxy_fops,
2928c2ecf20Sopenharmony_ci				const struct file_operations *real_fops)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	proxy_fops->release = full_proxy_release;
2958c2ecf20Sopenharmony_ci	if (real_fops->llseek)
2968c2ecf20Sopenharmony_ci		proxy_fops->llseek = full_proxy_llseek;
2978c2ecf20Sopenharmony_ci	if (real_fops->read)
2988c2ecf20Sopenharmony_ci		proxy_fops->read = full_proxy_read;
2998c2ecf20Sopenharmony_ci	if (real_fops->write)
3008c2ecf20Sopenharmony_ci		proxy_fops->write = full_proxy_write;
3018c2ecf20Sopenharmony_ci	if (real_fops->poll)
3028c2ecf20Sopenharmony_ci		proxy_fops->poll = full_proxy_poll;
3038c2ecf20Sopenharmony_ci	if (real_fops->unlocked_ioctl)
3048c2ecf20Sopenharmony_ci		proxy_fops->unlocked_ioctl = full_proxy_unlocked_ioctl;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int full_proxy_open(struct inode *inode, struct file *filp)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct dentry *dentry = F_DENTRY(filp);
3108c2ecf20Sopenharmony_ci	const struct file_operations *real_fops = NULL;
3118c2ecf20Sopenharmony_ci	struct file_operations *proxy_fops = NULL;
3128c2ecf20Sopenharmony_ci	int r;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	r = debugfs_file_get(dentry);
3158c2ecf20Sopenharmony_ci	if (r)
3168c2ecf20Sopenharmony_ci		return r == -EIO ? -ENOENT : r;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	real_fops = debugfs_real_fops(filp);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	r = debugfs_locked_down(inode, filp, real_fops);
3218c2ecf20Sopenharmony_ci	if (r)
3228c2ecf20Sopenharmony_ci		goto out;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (!fops_get(real_fops)) {
3258c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES
3268c2ecf20Sopenharmony_ci		if (real_fops->owner &&
3278c2ecf20Sopenharmony_ci		    real_fops->owner->state == MODULE_STATE_GOING) {
3288c2ecf20Sopenharmony_ci			r = -ENXIO;
3298c2ecf20Sopenharmony_ci			goto out;
3308c2ecf20Sopenharmony_ci		}
3318c2ecf20Sopenharmony_ci#endif
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		/* Huh? Module did not cleanup after itself at exit? */
3348c2ecf20Sopenharmony_ci		WARN(1, "debugfs file owner did not clean up at exit: %pd",
3358c2ecf20Sopenharmony_ci			dentry);
3368c2ecf20Sopenharmony_ci		r = -ENXIO;
3378c2ecf20Sopenharmony_ci		goto out;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	proxy_fops = kzalloc(sizeof(*proxy_fops), GFP_KERNEL);
3418c2ecf20Sopenharmony_ci	if (!proxy_fops) {
3428c2ecf20Sopenharmony_ci		r = -ENOMEM;
3438c2ecf20Sopenharmony_ci		goto free_proxy;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci	__full_proxy_fops_init(proxy_fops, real_fops);
3468c2ecf20Sopenharmony_ci	replace_fops(filp, proxy_fops);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	if (real_fops->open) {
3498c2ecf20Sopenharmony_ci		r = real_fops->open(inode, filp);
3508c2ecf20Sopenharmony_ci		if (r) {
3518c2ecf20Sopenharmony_ci			replace_fops(filp, d_inode(dentry)->i_fop);
3528c2ecf20Sopenharmony_ci			goto free_proxy;
3538c2ecf20Sopenharmony_ci		} else if (filp->f_op != proxy_fops) {
3548c2ecf20Sopenharmony_ci			/* No protection against file removal anymore. */
3558c2ecf20Sopenharmony_ci			WARN(1, "debugfs file owner replaced proxy fops: %pd",
3568c2ecf20Sopenharmony_ci				dentry);
3578c2ecf20Sopenharmony_ci			goto free_proxy;
3588c2ecf20Sopenharmony_ci		}
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	goto out;
3628c2ecf20Sopenharmony_cifree_proxy:
3638c2ecf20Sopenharmony_ci	kfree(proxy_fops);
3648c2ecf20Sopenharmony_ci	fops_put(real_fops);
3658c2ecf20Sopenharmony_ciout:
3668c2ecf20Sopenharmony_ci	debugfs_file_put(dentry);
3678c2ecf20Sopenharmony_ci	return r;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ciconst struct file_operations debugfs_full_proxy_file_operations = {
3718c2ecf20Sopenharmony_ci	.open = full_proxy_open,
3728c2ecf20Sopenharmony_ci};
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cissize_t debugfs_attr_read(struct file *file, char __user *buf,
3758c2ecf20Sopenharmony_ci			size_t len, loff_t *ppos)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	struct dentry *dentry = F_DENTRY(file);
3788c2ecf20Sopenharmony_ci	ssize_t ret;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	ret = debugfs_file_get(dentry);
3818c2ecf20Sopenharmony_ci	if (unlikely(ret))
3828c2ecf20Sopenharmony_ci		return ret;
3838c2ecf20Sopenharmony_ci	ret = simple_attr_read(file, buf, len, ppos);
3848c2ecf20Sopenharmony_ci	debugfs_file_put(dentry);
3858c2ecf20Sopenharmony_ci	return ret;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_attr_read);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic ssize_t debugfs_attr_write_xsigned(struct file *file, const char __user *buf,
3908c2ecf20Sopenharmony_ci			 size_t len, loff_t *ppos, bool is_signed)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	struct dentry *dentry = F_DENTRY(file);
3938c2ecf20Sopenharmony_ci	ssize_t ret;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ret = debugfs_file_get(dentry);
3968c2ecf20Sopenharmony_ci	if (unlikely(ret))
3978c2ecf20Sopenharmony_ci		return ret;
3988c2ecf20Sopenharmony_ci	if (is_signed)
3998c2ecf20Sopenharmony_ci		ret = simple_attr_write_signed(file, buf, len, ppos);
4008c2ecf20Sopenharmony_ci	else
4018c2ecf20Sopenharmony_ci		ret = simple_attr_write(file, buf, len, ppos);
4028c2ecf20Sopenharmony_ci	debugfs_file_put(dentry);
4038c2ecf20Sopenharmony_ci	return ret;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cissize_t debugfs_attr_write(struct file *file, const char __user *buf,
4078c2ecf20Sopenharmony_ci			 size_t len, loff_t *ppos)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	return debugfs_attr_write_xsigned(file, buf, len, ppos, false);
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_attr_write);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cissize_t debugfs_attr_write_signed(struct file *file, const char __user *buf,
4148c2ecf20Sopenharmony_ci			 size_t len, loff_t *ppos)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	return debugfs_attr_write_xsigned(file, buf, len, ppos, true);
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_attr_write_signed);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic struct dentry *debugfs_create_mode_unsafe(const char *name, umode_t mode,
4218c2ecf20Sopenharmony_ci					struct dentry *parent, void *value,
4228c2ecf20Sopenharmony_ci					const struct file_operations *fops,
4238c2ecf20Sopenharmony_ci					const struct file_operations *fops_ro,
4248c2ecf20Sopenharmony_ci					const struct file_operations *fops_wo)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	/* if there are no write bits set, make read only */
4278c2ecf20Sopenharmony_ci	if (!(mode & S_IWUGO))
4288c2ecf20Sopenharmony_ci		return debugfs_create_file_unsafe(name, mode, parent, value,
4298c2ecf20Sopenharmony_ci						fops_ro);
4308c2ecf20Sopenharmony_ci	/* if there are no read bits set, make write only */
4318c2ecf20Sopenharmony_ci	if (!(mode & S_IRUGO))
4328c2ecf20Sopenharmony_ci		return debugfs_create_file_unsafe(name, mode, parent, value,
4338c2ecf20Sopenharmony_ci						fops_wo);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	return debugfs_create_file_unsafe(name, mode, parent, value, fops);
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic int debugfs_u8_set(void *data, u64 val)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	*(u8 *)data = val;
4418c2ecf20Sopenharmony_ci	return 0;
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_cistatic int debugfs_u8_get(void *data, u64 *val)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	*val = *(u8 *)data;
4468c2ecf20Sopenharmony_ci	return 0;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n");
4498c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_u8_ro, debugfs_u8_get, NULL, "%llu\n");
4508c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_u8_wo, NULL, debugfs_u8_set, "%llu\n");
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci/**
4538c2ecf20Sopenharmony_ci * debugfs_create_u8 - create a debugfs file that is used to read and write an unsigned 8-bit value
4548c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
4558c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
4568c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
4578c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
4588c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
4598c2ecf20Sopenharmony_ci * @value: a pointer to the variable that the file should read to and write
4608c2ecf20Sopenharmony_ci *         from.
4618c2ecf20Sopenharmony_ci *
4628c2ecf20Sopenharmony_ci * This function creates a file in debugfs with the given name that
4638c2ecf20Sopenharmony_ci * contains the value of the variable @value.  If the @mode variable is so
4648c2ecf20Sopenharmony_ci * set, it can be read from, and written to.
4658c2ecf20Sopenharmony_ci */
4668c2ecf20Sopenharmony_civoid debugfs_create_u8(const char *name, umode_t mode, struct dentry *parent,
4678c2ecf20Sopenharmony_ci		       u8 *value)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u8,
4708c2ecf20Sopenharmony_ci				   &fops_u8_ro, &fops_u8_wo);
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_u8);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic int debugfs_u16_set(void *data, u64 val)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	*(u16 *)data = val;
4778c2ecf20Sopenharmony_ci	return 0;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_cistatic int debugfs_u16_get(void *data, u64 *val)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	*val = *(u16 *)data;
4828c2ecf20Sopenharmony_ci	return 0;
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n");
4858c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_u16_ro, debugfs_u16_get, NULL, "%llu\n");
4868c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_u16_wo, NULL, debugfs_u16_set, "%llu\n");
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci/**
4898c2ecf20Sopenharmony_ci * debugfs_create_u16 - create a debugfs file that is used to read and write an unsigned 16-bit value
4908c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
4918c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
4928c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
4938c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
4948c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
4958c2ecf20Sopenharmony_ci * @value: a pointer to the variable that the file should read to and write
4968c2ecf20Sopenharmony_ci *         from.
4978c2ecf20Sopenharmony_ci *
4988c2ecf20Sopenharmony_ci * This function creates a file in debugfs with the given name that
4998c2ecf20Sopenharmony_ci * contains the value of the variable @value.  If the @mode variable is so
5008c2ecf20Sopenharmony_ci * set, it can be read from, and written to.
5018c2ecf20Sopenharmony_ci */
5028c2ecf20Sopenharmony_civoid debugfs_create_u16(const char *name, umode_t mode, struct dentry *parent,
5038c2ecf20Sopenharmony_ci			u16 *value)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u16,
5068c2ecf20Sopenharmony_ci				   &fops_u16_ro, &fops_u16_wo);
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_u16);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic int debugfs_u32_set(void *data, u64 val)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	*(u32 *)data = val;
5138c2ecf20Sopenharmony_ci	return 0;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_cistatic int debugfs_u32_get(void *data, u64 *val)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	*val = *(u32 *)data;
5188c2ecf20Sopenharmony_ci	return 0;
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n");
5218c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_u32_ro, debugfs_u32_get, NULL, "%llu\n");
5228c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_u32_wo, NULL, debugfs_u32_set, "%llu\n");
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci/**
5258c2ecf20Sopenharmony_ci * debugfs_create_u32 - create a debugfs file that is used to read and write an unsigned 32-bit value
5268c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
5278c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
5288c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
5298c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
5308c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
5318c2ecf20Sopenharmony_ci * @value: a pointer to the variable that the file should read to and write
5328c2ecf20Sopenharmony_ci *         from.
5338c2ecf20Sopenharmony_ci *
5348c2ecf20Sopenharmony_ci * This function creates a file in debugfs with the given name that
5358c2ecf20Sopenharmony_ci * contains the value of the variable @value.  If the @mode variable is so
5368c2ecf20Sopenharmony_ci * set, it can be read from, and written to.
5378c2ecf20Sopenharmony_ci */
5388c2ecf20Sopenharmony_civoid debugfs_create_u32(const char *name, umode_t mode, struct dentry *parent,
5398c2ecf20Sopenharmony_ci			u32 *value)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u32,
5428c2ecf20Sopenharmony_ci				   &fops_u32_ro, &fops_u32_wo);
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_u32);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic int debugfs_u64_set(void *data, u64 val)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	*(u64 *)data = val;
5498c2ecf20Sopenharmony_ci	return 0;
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_cistatic int debugfs_u64_get(void *data, u64 *val)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	*val = *(u64 *)data;
5558c2ecf20Sopenharmony_ci	return 0;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_u64, debugfs_u64_get, debugfs_u64_set, "%llu\n");
5588c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_u64_ro, debugfs_u64_get, NULL, "%llu\n");
5598c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci/**
5628c2ecf20Sopenharmony_ci * debugfs_create_u64 - create a debugfs file that is used to read and write an unsigned 64-bit value
5638c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
5648c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
5658c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
5668c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
5678c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
5688c2ecf20Sopenharmony_ci * @value: a pointer to the variable that the file should read to and write
5698c2ecf20Sopenharmony_ci *         from.
5708c2ecf20Sopenharmony_ci *
5718c2ecf20Sopenharmony_ci * This function creates a file in debugfs with the given name that
5728c2ecf20Sopenharmony_ci * contains the value of the variable @value.  If the @mode variable is so
5738c2ecf20Sopenharmony_ci * set, it can be read from, and written to.
5748c2ecf20Sopenharmony_ci */
5758c2ecf20Sopenharmony_civoid debugfs_create_u64(const char *name, umode_t mode, struct dentry *parent,
5768c2ecf20Sopenharmony_ci			u64 *value)
5778c2ecf20Sopenharmony_ci{
5788c2ecf20Sopenharmony_ci	debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u64,
5798c2ecf20Sopenharmony_ci				   &fops_u64_ro, &fops_u64_wo);
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_u64);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_cistatic int debugfs_ulong_set(void *data, u64 val)
5848c2ecf20Sopenharmony_ci{
5858c2ecf20Sopenharmony_ci	*(unsigned long *)data = val;
5868c2ecf20Sopenharmony_ci	return 0;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic int debugfs_ulong_get(void *data, u64 *val)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	*val = *(unsigned long *)data;
5928c2ecf20Sopenharmony_ci	return 0;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_ulong, debugfs_ulong_get, debugfs_ulong_set,
5958c2ecf20Sopenharmony_ci			"%llu\n");
5968c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_ulong_ro, debugfs_ulong_get, NULL, "%llu\n");
5978c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_ulong_wo, NULL, debugfs_ulong_set, "%llu\n");
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci/**
6008c2ecf20Sopenharmony_ci * debugfs_create_ulong - create a debugfs file that is used to read and write
6018c2ecf20Sopenharmony_ci * an unsigned long value.
6028c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
6038c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
6048c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
6058c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
6068c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
6078c2ecf20Sopenharmony_ci * @value: a pointer to the variable that the file should read to and write
6088c2ecf20Sopenharmony_ci *         from.
6098c2ecf20Sopenharmony_ci *
6108c2ecf20Sopenharmony_ci * This function creates a file in debugfs with the given name that
6118c2ecf20Sopenharmony_ci * contains the value of the variable @value.  If the @mode variable is so
6128c2ecf20Sopenharmony_ci * set, it can be read from, and written to.
6138c2ecf20Sopenharmony_ci *
6148c2ecf20Sopenharmony_ci * This function will return a pointer to a dentry if it succeeds.  This
6158c2ecf20Sopenharmony_ci * pointer must be passed to the debugfs_remove() function when the file is
6168c2ecf20Sopenharmony_ci * to be removed (no automatic cleanup happens if your module is unloaded,
6178c2ecf20Sopenharmony_ci * you are responsible here.)  If an error occurs, ERR_PTR(-ERROR) will be
6188c2ecf20Sopenharmony_ci * returned.
6198c2ecf20Sopenharmony_ci *
6208c2ecf20Sopenharmony_ci * If debugfs is not enabled in the kernel, the value ERR_PTR(-ENODEV) will
6218c2ecf20Sopenharmony_ci * be returned.
6228c2ecf20Sopenharmony_ci */
6238c2ecf20Sopenharmony_cistruct dentry *debugfs_create_ulong(const char *name, umode_t mode,
6248c2ecf20Sopenharmony_ci				    struct dentry *parent, unsigned long *value)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	return debugfs_create_mode_unsafe(name, mode, parent, value,
6278c2ecf20Sopenharmony_ci					&fops_ulong, &fops_ulong_ro,
6288c2ecf20Sopenharmony_ci					&fops_ulong_wo);
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_ulong);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_x8, debugfs_u8_get, debugfs_u8_set, "0x%02llx\n");
6338c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_x8_ro, debugfs_u8_get, NULL, "0x%02llx\n");
6348c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_x8_wo, NULL, debugfs_u8_set, "0x%02llx\n");
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_x16, debugfs_u16_get, debugfs_u16_set,
6378c2ecf20Sopenharmony_ci			"0x%04llx\n");
6388c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_x16_ro, debugfs_u16_get, NULL, "0x%04llx\n");
6398c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_x16_wo, NULL, debugfs_u16_set, "0x%04llx\n");
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_x32, debugfs_u32_get, debugfs_u32_set,
6428c2ecf20Sopenharmony_ci			"0x%08llx\n");
6438c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_x32_ro, debugfs_u32_get, NULL, "0x%08llx\n");
6448c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_x32_wo, NULL, debugfs_u32_set, "0x%08llx\n");
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_x64, debugfs_u64_get, debugfs_u64_set,
6478c2ecf20Sopenharmony_ci			"0x%016llx\n");
6488c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_x64_ro, debugfs_u64_get, NULL, "0x%016llx\n");
6498c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_x64_wo, NULL, debugfs_u64_set, "0x%016llx\n");
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci/*
6528c2ecf20Sopenharmony_ci * debugfs_create_x{8,16,32,64} - create a debugfs file that is used to read and write an unsigned {8,16,32,64}-bit value
6538c2ecf20Sopenharmony_ci *
6548c2ecf20Sopenharmony_ci * These functions are exactly the same as the above functions (but use a hex
6558c2ecf20Sopenharmony_ci * output for the decimal challenged). For details look at the above unsigned
6568c2ecf20Sopenharmony_ci * decimal functions.
6578c2ecf20Sopenharmony_ci */
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci/**
6608c2ecf20Sopenharmony_ci * debugfs_create_x8 - create a debugfs file that is used to read and write an unsigned 8-bit value
6618c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
6628c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
6638c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
6648c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
6658c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
6668c2ecf20Sopenharmony_ci * @value: a pointer to the variable that the file should read to and write
6678c2ecf20Sopenharmony_ci *         from.
6688c2ecf20Sopenharmony_ci */
6698c2ecf20Sopenharmony_civoid debugfs_create_x8(const char *name, umode_t mode, struct dentry *parent,
6708c2ecf20Sopenharmony_ci		       u8 *value)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x8,
6738c2ecf20Sopenharmony_ci				   &fops_x8_ro, &fops_x8_wo);
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_x8);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci/**
6788c2ecf20Sopenharmony_ci * debugfs_create_x16 - create a debugfs file that is used to read and write an unsigned 16-bit value
6798c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
6808c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
6818c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
6828c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
6838c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
6848c2ecf20Sopenharmony_ci * @value: a pointer to the variable that the file should read to and write
6858c2ecf20Sopenharmony_ci *         from.
6868c2ecf20Sopenharmony_ci */
6878c2ecf20Sopenharmony_civoid debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent,
6888c2ecf20Sopenharmony_ci			u16 *value)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x16,
6918c2ecf20Sopenharmony_ci				   &fops_x16_ro, &fops_x16_wo);
6928c2ecf20Sopenharmony_ci}
6938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_x16);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci/**
6968c2ecf20Sopenharmony_ci * debugfs_create_x32 - create a debugfs file that is used to read and write an unsigned 32-bit value
6978c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
6988c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
6998c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
7008c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
7018c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
7028c2ecf20Sopenharmony_ci * @value: a pointer to the variable that the file should read to and write
7038c2ecf20Sopenharmony_ci *         from.
7048c2ecf20Sopenharmony_ci */
7058c2ecf20Sopenharmony_civoid debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent,
7068c2ecf20Sopenharmony_ci			u32 *value)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x32,
7098c2ecf20Sopenharmony_ci				   &fops_x32_ro, &fops_x32_wo);
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_x32);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci/**
7148c2ecf20Sopenharmony_ci * debugfs_create_x64 - create a debugfs file that is used to read and write an unsigned 64-bit value
7158c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
7168c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
7178c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
7188c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
7198c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
7208c2ecf20Sopenharmony_ci * @value: a pointer to the variable that the file should read to and write
7218c2ecf20Sopenharmony_ci *         from.
7228c2ecf20Sopenharmony_ci */
7238c2ecf20Sopenharmony_civoid debugfs_create_x64(const char *name, umode_t mode, struct dentry *parent,
7248c2ecf20Sopenharmony_ci			u64 *value)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x64,
7278c2ecf20Sopenharmony_ci				   &fops_x64_ro, &fops_x64_wo);
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_x64);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_cistatic int debugfs_size_t_set(void *data, u64 val)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	*(size_t *)data = val;
7358c2ecf20Sopenharmony_ci	return 0;
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_cistatic int debugfs_size_t_get(void *data, u64 *val)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	*val = *(size_t *)data;
7408c2ecf20Sopenharmony_ci	return 0;
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_size_t, debugfs_size_t_get, debugfs_size_t_set,
7438c2ecf20Sopenharmony_ci			"%llu\n"); /* %llu and %zu are more or less the same */
7448c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_size_t_ro, debugfs_size_t_get, NULL, "%llu\n");
7458c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_size_t_wo, NULL, debugfs_size_t_set, "%llu\n");
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci/**
7488c2ecf20Sopenharmony_ci * debugfs_create_size_t - create a debugfs file that is used to read and write an size_t value
7498c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
7508c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
7518c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
7528c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
7538c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
7548c2ecf20Sopenharmony_ci * @value: a pointer to the variable that the file should read to and write
7558c2ecf20Sopenharmony_ci *         from.
7568c2ecf20Sopenharmony_ci */
7578c2ecf20Sopenharmony_civoid debugfs_create_size_t(const char *name, umode_t mode,
7588c2ecf20Sopenharmony_ci			   struct dentry *parent, size_t *value)
7598c2ecf20Sopenharmony_ci{
7608c2ecf20Sopenharmony_ci	debugfs_create_mode_unsafe(name, mode, parent, value, &fops_size_t,
7618c2ecf20Sopenharmony_ci				   &fops_size_t_ro, &fops_size_t_wo);
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_size_t);
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistatic int debugfs_atomic_t_set(void *data, u64 val)
7668c2ecf20Sopenharmony_ci{
7678c2ecf20Sopenharmony_ci	atomic_set((atomic_t *)data, val);
7688c2ecf20Sopenharmony_ci	return 0;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_cistatic int debugfs_atomic_t_get(void *data, u64 *val)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci	*val = atomic_read((atomic_t *)data);
7738c2ecf20Sopenharmony_ci	return 0;
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE_SIGNED(fops_atomic_t, debugfs_atomic_t_get,
7768c2ecf20Sopenharmony_ci			debugfs_atomic_t_set, "%lld\n");
7778c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE_SIGNED(fops_atomic_t_ro, debugfs_atomic_t_get, NULL,
7788c2ecf20Sopenharmony_ci			"%lld\n");
7798c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE_SIGNED(fops_atomic_t_wo, NULL, debugfs_atomic_t_set,
7808c2ecf20Sopenharmony_ci			"%lld\n");
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci/**
7838c2ecf20Sopenharmony_ci * debugfs_create_atomic_t - create a debugfs file that is used to read and
7848c2ecf20Sopenharmony_ci * write an atomic_t value
7858c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
7868c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
7878c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
7888c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
7898c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
7908c2ecf20Sopenharmony_ci * @value: a pointer to the variable that the file should read to and write
7918c2ecf20Sopenharmony_ci *         from.
7928c2ecf20Sopenharmony_ci */
7938c2ecf20Sopenharmony_civoid debugfs_create_atomic_t(const char *name, umode_t mode,
7948c2ecf20Sopenharmony_ci			     struct dentry *parent, atomic_t *value)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	debugfs_create_mode_unsafe(name, mode, parent, value, &fops_atomic_t,
7978c2ecf20Sopenharmony_ci				   &fops_atomic_t_ro, &fops_atomic_t_wo);
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_atomic_t);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_cissize_t debugfs_read_file_bool(struct file *file, char __user *user_buf,
8028c2ecf20Sopenharmony_ci			       size_t count, loff_t *ppos)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	char buf[3];
8058c2ecf20Sopenharmony_ci	bool val;
8068c2ecf20Sopenharmony_ci	int r;
8078c2ecf20Sopenharmony_ci	struct dentry *dentry = F_DENTRY(file);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	r = debugfs_file_get(dentry);
8108c2ecf20Sopenharmony_ci	if (unlikely(r))
8118c2ecf20Sopenharmony_ci		return r;
8128c2ecf20Sopenharmony_ci	val = *(bool *)file->private_data;
8138c2ecf20Sopenharmony_ci	debugfs_file_put(dentry);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	if (val)
8168c2ecf20Sopenharmony_ci		buf[0] = 'Y';
8178c2ecf20Sopenharmony_ci	else
8188c2ecf20Sopenharmony_ci		buf[0] = 'N';
8198c2ecf20Sopenharmony_ci	buf[1] = '\n';
8208c2ecf20Sopenharmony_ci	buf[2] = 0x00;
8218c2ecf20Sopenharmony_ci	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_read_file_bool);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_cissize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf,
8268c2ecf20Sopenharmony_ci				size_t count, loff_t *ppos)
8278c2ecf20Sopenharmony_ci{
8288c2ecf20Sopenharmony_ci	bool bv;
8298c2ecf20Sopenharmony_ci	int r;
8308c2ecf20Sopenharmony_ci	bool *val = file->private_data;
8318c2ecf20Sopenharmony_ci	struct dentry *dentry = F_DENTRY(file);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	r = kstrtobool_from_user(user_buf, count, &bv);
8348c2ecf20Sopenharmony_ci	if (!r) {
8358c2ecf20Sopenharmony_ci		r = debugfs_file_get(dentry);
8368c2ecf20Sopenharmony_ci		if (unlikely(r))
8378c2ecf20Sopenharmony_ci			return r;
8388c2ecf20Sopenharmony_ci		*val = bv;
8398c2ecf20Sopenharmony_ci		debugfs_file_put(dentry);
8408c2ecf20Sopenharmony_ci	}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	return count;
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_write_file_bool);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_cistatic const struct file_operations fops_bool = {
8478c2ecf20Sopenharmony_ci	.read =		debugfs_read_file_bool,
8488c2ecf20Sopenharmony_ci	.write =	debugfs_write_file_bool,
8498c2ecf20Sopenharmony_ci	.open =		simple_open,
8508c2ecf20Sopenharmony_ci	.llseek =	default_llseek,
8518c2ecf20Sopenharmony_ci};
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_cistatic const struct file_operations fops_bool_ro = {
8548c2ecf20Sopenharmony_ci	.read =		debugfs_read_file_bool,
8558c2ecf20Sopenharmony_ci	.open =		simple_open,
8568c2ecf20Sopenharmony_ci	.llseek =	default_llseek,
8578c2ecf20Sopenharmony_ci};
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic const struct file_operations fops_bool_wo = {
8608c2ecf20Sopenharmony_ci	.write =	debugfs_write_file_bool,
8618c2ecf20Sopenharmony_ci	.open =		simple_open,
8628c2ecf20Sopenharmony_ci	.llseek =	default_llseek,
8638c2ecf20Sopenharmony_ci};
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci/**
8668c2ecf20Sopenharmony_ci * debugfs_create_bool - create a debugfs file that is used to read and write a boolean value
8678c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
8688c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
8698c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
8708c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
8718c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
8728c2ecf20Sopenharmony_ci * @value: a pointer to the variable that the file should read to and write
8738c2ecf20Sopenharmony_ci *         from.
8748c2ecf20Sopenharmony_ci *
8758c2ecf20Sopenharmony_ci * This function creates a file in debugfs with the given name that
8768c2ecf20Sopenharmony_ci * contains the value of the variable @value.  If the @mode variable is so
8778c2ecf20Sopenharmony_ci * set, it can be read from, and written to.
8788c2ecf20Sopenharmony_ci *
8798c2ecf20Sopenharmony_ci * This function will return a pointer to a dentry if it succeeds.  This
8808c2ecf20Sopenharmony_ci * pointer must be passed to the debugfs_remove() function when the file is
8818c2ecf20Sopenharmony_ci * to be removed (no automatic cleanup happens if your module is unloaded,
8828c2ecf20Sopenharmony_ci * you are responsible here.)  If an error occurs, ERR_PTR(-ERROR) will be
8838c2ecf20Sopenharmony_ci * returned.
8848c2ecf20Sopenharmony_ci *
8858c2ecf20Sopenharmony_ci * If debugfs is not enabled in the kernel, the value ERR_PTR(-ENODEV) will
8868c2ecf20Sopenharmony_ci * be returned.
8878c2ecf20Sopenharmony_ci */
8888c2ecf20Sopenharmony_cistruct dentry *debugfs_create_bool(const char *name, umode_t mode,
8898c2ecf20Sopenharmony_ci				   struct dentry *parent, bool *value)
8908c2ecf20Sopenharmony_ci{
8918c2ecf20Sopenharmony_ci	return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_bool,
8928c2ecf20Sopenharmony_ci				   &fops_bool_ro, &fops_bool_wo);
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_bool);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_cistatic ssize_t read_file_blob(struct file *file, char __user *user_buf,
8978c2ecf20Sopenharmony_ci			      size_t count, loff_t *ppos)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	struct debugfs_blob_wrapper *blob = file->private_data;
9008c2ecf20Sopenharmony_ci	struct dentry *dentry = F_DENTRY(file);
9018c2ecf20Sopenharmony_ci	ssize_t r;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	r = debugfs_file_get(dentry);
9048c2ecf20Sopenharmony_ci	if (unlikely(r))
9058c2ecf20Sopenharmony_ci		return r;
9068c2ecf20Sopenharmony_ci	r = simple_read_from_buffer(user_buf, count, ppos, blob->data,
9078c2ecf20Sopenharmony_ci				blob->size);
9088c2ecf20Sopenharmony_ci	debugfs_file_put(dentry);
9098c2ecf20Sopenharmony_ci	return r;
9108c2ecf20Sopenharmony_ci}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_cistatic const struct file_operations fops_blob = {
9138c2ecf20Sopenharmony_ci	.read =		read_file_blob,
9148c2ecf20Sopenharmony_ci	.open =		simple_open,
9158c2ecf20Sopenharmony_ci	.llseek =	default_llseek,
9168c2ecf20Sopenharmony_ci};
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci/**
9198c2ecf20Sopenharmony_ci * debugfs_create_blob - create a debugfs file that is used to read a binary blob
9208c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
9218c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
9228c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
9238c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
9248c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
9258c2ecf20Sopenharmony_ci * @blob: a pointer to a struct debugfs_blob_wrapper which contains a pointer
9268c2ecf20Sopenharmony_ci *        to the blob data and the size of the data.
9278c2ecf20Sopenharmony_ci *
9288c2ecf20Sopenharmony_ci * This function creates a file in debugfs with the given name that exports
9298c2ecf20Sopenharmony_ci * @blob->data as a binary blob. If the @mode variable is so set it can be
9308c2ecf20Sopenharmony_ci * read from. Writing is not supported.
9318c2ecf20Sopenharmony_ci *
9328c2ecf20Sopenharmony_ci * This function will return a pointer to a dentry if it succeeds.  This
9338c2ecf20Sopenharmony_ci * pointer must be passed to the debugfs_remove() function when the file is
9348c2ecf20Sopenharmony_ci * to be removed (no automatic cleanup happens if your module is unloaded,
9358c2ecf20Sopenharmony_ci * you are responsible here.)  If an error occurs, ERR_PTR(-ERROR) will be
9368c2ecf20Sopenharmony_ci * returned.
9378c2ecf20Sopenharmony_ci *
9388c2ecf20Sopenharmony_ci * If debugfs is not enabled in the kernel, the value ERR_PTR(-ENODEV) will
9398c2ecf20Sopenharmony_ci * be returned.
9408c2ecf20Sopenharmony_ci */
9418c2ecf20Sopenharmony_cistruct dentry *debugfs_create_blob(const char *name, umode_t mode,
9428c2ecf20Sopenharmony_ci				   struct dentry *parent,
9438c2ecf20Sopenharmony_ci				   struct debugfs_blob_wrapper *blob)
9448c2ecf20Sopenharmony_ci{
9458c2ecf20Sopenharmony_ci	return debugfs_create_file_unsafe(name, mode, parent, blob, &fops_blob);
9468c2ecf20Sopenharmony_ci}
9478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_blob);
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_cistatic size_t u32_format_array(char *buf, size_t bufsize,
9508c2ecf20Sopenharmony_ci			       u32 *array, int array_size)
9518c2ecf20Sopenharmony_ci{
9528c2ecf20Sopenharmony_ci	size_t ret = 0;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	while (--array_size >= 0) {
9558c2ecf20Sopenharmony_ci		size_t len;
9568c2ecf20Sopenharmony_ci		char term = array_size ? ' ' : '\n';
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci		len = snprintf(buf, bufsize, "%u%c", *array++, term);
9598c2ecf20Sopenharmony_ci		ret += len;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci		buf += len;
9628c2ecf20Sopenharmony_ci		bufsize -= len;
9638c2ecf20Sopenharmony_ci	}
9648c2ecf20Sopenharmony_ci	return ret;
9658c2ecf20Sopenharmony_ci}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_cistatic int u32_array_open(struct inode *inode, struct file *file)
9688c2ecf20Sopenharmony_ci{
9698c2ecf20Sopenharmony_ci	struct debugfs_u32_array *data = inode->i_private;
9708c2ecf20Sopenharmony_ci	int size, elements = data->n_elements;
9718c2ecf20Sopenharmony_ci	char *buf;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	/*
9748c2ecf20Sopenharmony_ci	 * Max size:
9758c2ecf20Sopenharmony_ci	 *  - 10 digits + ' '/'\n' = 11 bytes per number
9768c2ecf20Sopenharmony_ci	 *  - terminating NUL character
9778c2ecf20Sopenharmony_ci	 */
9788c2ecf20Sopenharmony_ci	size = elements*11;
9798c2ecf20Sopenharmony_ci	buf = kmalloc(size+1, GFP_KERNEL);
9808c2ecf20Sopenharmony_ci	if (!buf)
9818c2ecf20Sopenharmony_ci		return -ENOMEM;
9828c2ecf20Sopenharmony_ci	buf[size] = 0;
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	file->private_data = buf;
9858c2ecf20Sopenharmony_ci	u32_format_array(buf, size, data->array, data->n_elements);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	return nonseekable_open(inode, file);
9888c2ecf20Sopenharmony_ci}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_cistatic ssize_t u32_array_read(struct file *file, char __user *buf, size_t len,
9918c2ecf20Sopenharmony_ci			      loff_t *ppos)
9928c2ecf20Sopenharmony_ci{
9938c2ecf20Sopenharmony_ci	size_t size = strlen(file->private_data);
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, len, ppos,
9968c2ecf20Sopenharmony_ci					file->private_data, size);
9978c2ecf20Sopenharmony_ci}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_cistatic int u32_array_release(struct inode *inode, struct file *file)
10008c2ecf20Sopenharmony_ci{
10018c2ecf20Sopenharmony_ci	kfree(file->private_data);
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	return 0;
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_cistatic const struct file_operations u32_array_fops = {
10078c2ecf20Sopenharmony_ci	.owner	 = THIS_MODULE,
10088c2ecf20Sopenharmony_ci	.open	 = u32_array_open,
10098c2ecf20Sopenharmony_ci	.release = u32_array_release,
10108c2ecf20Sopenharmony_ci	.read	 = u32_array_read,
10118c2ecf20Sopenharmony_ci	.llseek  = no_llseek,
10128c2ecf20Sopenharmony_ci};
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci/**
10158c2ecf20Sopenharmony_ci * debugfs_create_u32_array - create a debugfs file that is used to read u32
10168c2ecf20Sopenharmony_ci * array.
10178c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
10188c2ecf20Sopenharmony_ci * @mode: the permission that the file should have.
10198c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
10208c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
10218c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
10228c2ecf20Sopenharmony_ci * @array: wrapper struct containing data pointer and size of the array.
10238c2ecf20Sopenharmony_ci *
10248c2ecf20Sopenharmony_ci * This function creates a file in debugfs with the given name that exports
10258c2ecf20Sopenharmony_ci * @array as data. If the @mode variable is so set it can be read from.
10268c2ecf20Sopenharmony_ci * Writing is not supported. Seek within the file is also not supported.
10278c2ecf20Sopenharmony_ci * Once array is created its size can not be changed.
10288c2ecf20Sopenharmony_ci */
10298c2ecf20Sopenharmony_civoid debugfs_create_u32_array(const char *name, umode_t mode,
10308c2ecf20Sopenharmony_ci			      struct dentry *parent,
10318c2ecf20Sopenharmony_ci			      struct debugfs_u32_array *array)
10328c2ecf20Sopenharmony_ci{
10338c2ecf20Sopenharmony_ci	debugfs_create_file_unsafe(name, mode, parent, array, &u32_array_fops);
10348c2ecf20Sopenharmony_ci}
10358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_u32_array);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci#ifdef CONFIG_HAS_IOMEM
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci/*
10408c2ecf20Sopenharmony_ci * The regset32 stuff is used to print 32-bit registers using the
10418c2ecf20Sopenharmony_ci * seq_file utilities. We offer printing a register set in an already-opened
10428c2ecf20Sopenharmony_ci * sequential file or create a debugfs file that only prints a regset32.
10438c2ecf20Sopenharmony_ci */
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci/**
10468c2ecf20Sopenharmony_ci * debugfs_print_regs32 - use seq_print to describe a set of registers
10478c2ecf20Sopenharmony_ci * @s: the seq_file structure being used to generate output
10488c2ecf20Sopenharmony_ci * @regs: an array if struct debugfs_reg32 structures
10498c2ecf20Sopenharmony_ci * @nregs: the length of the above array
10508c2ecf20Sopenharmony_ci * @base: the base address to be used in reading the registers
10518c2ecf20Sopenharmony_ci * @prefix: a string to be prefixed to every output line
10528c2ecf20Sopenharmony_ci *
10538c2ecf20Sopenharmony_ci * This function outputs a text block describing the current values of
10548c2ecf20Sopenharmony_ci * some 32-bit hardware registers. It is meant to be used within debugfs
10558c2ecf20Sopenharmony_ci * files based on seq_file that need to show registers, intermixed with other
10568c2ecf20Sopenharmony_ci * information. The prefix argument may be used to specify a leading string,
10578c2ecf20Sopenharmony_ci * because some peripherals have several blocks of identical registers,
10588c2ecf20Sopenharmony_ci * for example configuration of dma channels
10598c2ecf20Sopenharmony_ci */
10608c2ecf20Sopenharmony_civoid debugfs_print_regs32(struct seq_file *s, const struct debugfs_reg32 *regs,
10618c2ecf20Sopenharmony_ci			  int nregs, void __iomem *base, char *prefix)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	int i;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	for (i = 0; i < nregs; i++, regs++) {
10668c2ecf20Sopenharmony_ci		if (prefix)
10678c2ecf20Sopenharmony_ci			seq_printf(s, "%s", prefix);
10688c2ecf20Sopenharmony_ci		seq_printf(s, "%s = 0x%08x\n", regs->name,
10698c2ecf20Sopenharmony_ci			   readl(base + regs->offset));
10708c2ecf20Sopenharmony_ci		if (seq_has_overflowed(s))
10718c2ecf20Sopenharmony_ci			break;
10728c2ecf20Sopenharmony_ci	}
10738c2ecf20Sopenharmony_ci}
10748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_print_regs32);
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_cistatic int debugfs_show_regset32(struct seq_file *s, void *data)
10778c2ecf20Sopenharmony_ci{
10788c2ecf20Sopenharmony_ci	struct debugfs_regset32 *regset = s->private;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	if (regset->dev)
10818c2ecf20Sopenharmony_ci		pm_runtime_get_sync(regset->dev);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	debugfs_print_regs32(s, regset->regs, regset->nregs, regset->base, "");
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	if (regset->dev)
10868c2ecf20Sopenharmony_ci		pm_runtime_put(regset->dev);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	return 0;
10898c2ecf20Sopenharmony_ci}
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_cistatic int debugfs_open_regset32(struct inode *inode, struct file *file)
10928c2ecf20Sopenharmony_ci{
10938c2ecf20Sopenharmony_ci	return single_open(file, debugfs_show_regset32, inode->i_private);
10948c2ecf20Sopenharmony_ci}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_cistatic const struct file_operations fops_regset32 = {
10978c2ecf20Sopenharmony_ci	.open =		debugfs_open_regset32,
10988c2ecf20Sopenharmony_ci	.read =		seq_read,
10998c2ecf20Sopenharmony_ci	.llseek =	seq_lseek,
11008c2ecf20Sopenharmony_ci	.release =	single_release,
11018c2ecf20Sopenharmony_ci};
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci/**
11048c2ecf20Sopenharmony_ci * debugfs_create_regset32 - create a debugfs file that returns register values
11058c2ecf20Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create.
11068c2ecf20Sopenharmony_ci * @mode: the permission that the file should have
11078c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
11088c2ecf20Sopenharmony_ci *          directory dentry if set.  If this parameter is %NULL, then the
11098c2ecf20Sopenharmony_ci *          file will be created in the root of the debugfs filesystem.
11108c2ecf20Sopenharmony_ci * @regset: a pointer to a struct debugfs_regset32, which contains a pointer
11118c2ecf20Sopenharmony_ci *          to an array of register definitions, the array size and the base
11128c2ecf20Sopenharmony_ci *          address where the register bank is to be found.
11138c2ecf20Sopenharmony_ci *
11148c2ecf20Sopenharmony_ci * This function creates a file in debugfs with the given name that reports
11158c2ecf20Sopenharmony_ci * the names and values of a set of 32-bit registers. If the @mode variable
11168c2ecf20Sopenharmony_ci * is so set it can be read from. Writing is not supported.
11178c2ecf20Sopenharmony_ci */
11188c2ecf20Sopenharmony_civoid debugfs_create_regset32(const char *name, umode_t mode,
11198c2ecf20Sopenharmony_ci			     struct dentry *parent,
11208c2ecf20Sopenharmony_ci			     struct debugfs_regset32 *regset)
11218c2ecf20Sopenharmony_ci{
11228c2ecf20Sopenharmony_ci	debugfs_create_file(name, mode, parent, regset, &fops_regset32);
11238c2ecf20Sopenharmony_ci}
11248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_regset32);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci#endif /* CONFIG_HAS_IOMEM */
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_cistruct debugfs_devm_entry {
11298c2ecf20Sopenharmony_ci	int (*read)(struct seq_file *seq, void *data);
11308c2ecf20Sopenharmony_ci	struct device *dev;
11318c2ecf20Sopenharmony_ci};
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_cistatic int debugfs_devm_entry_open(struct inode *inode, struct file *f)
11348c2ecf20Sopenharmony_ci{
11358c2ecf20Sopenharmony_ci	struct debugfs_devm_entry *entry = inode->i_private;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	return single_open(f, entry->read, entry->dev);
11388c2ecf20Sopenharmony_ci}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_cistatic const struct file_operations debugfs_devm_entry_ops = {
11418c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
11428c2ecf20Sopenharmony_ci	.open = debugfs_devm_entry_open,
11438c2ecf20Sopenharmony_ci	.release = single_release,
11448c2ecf20Sopenharmony_ci	.read = seq_read,
11458c2ecf20Sopenharmony_ci	.llseek = seq_lseek
11468c2ecf20Sopenharmony_ci};
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci/**
11498c2ecf20Sopenharmony_ci * debugfs_create_devm_seqfile - create a debugfs file that is bound to device.
11508c2ecf20Sopenharmony_ci *
11518c2ecf20Sopenharmony_ci * @dev: device related to this debugfs file.
11528c2ecf20Sopenharmony_ci * @name: name of the debugfs file.
11538c2ecf20Sopenharmony_ci * @parent: a pointer to the parent dentry for this file.  This should be a
11548c2ecf20Sopenharmony_ci *	directory dentry if set.  If this parameter is %NULL, then the
11558c2ecf20Sopenharmony_ci *	file will be created in the root of the debugfs filesystem.
11568c2ecf20Sopenharmony_ci * @read_fn: function pointer called to print the seq_file content.
11578c2ecf20Sopenharmony_ci */
11588c2ecf20Sopenharmony_civoid debugfs_create_devm_seqfile(struct device *dev, const char *name,
11598c2ecf20Sopenharmony_ci				 struct dentry *parent,
11608c2ecf20Sopenharmony_ci				 int (*read_fn)(struct seq_file *s, void *data))
11618c2ecf20Sopenharmony_ci{
11628c2ecf20Sopenharmony_ci	struct debugfs_devm_entry *entry;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	if (IS_ERR(parent))
11658c2ecf20Sopenharmony_ci		return;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	entry = devm_kzalloc(dev, sizeof(*entry), GFP_KERNEL);
11688c2ecf20Sopenharmony_ci	if (!entry)
11698c2ecf20Sopenharmony_ci		return;
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	entry->read = read_fn;
11728c2ecf20Sopenharmony_ci	entry->dev = dev;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	debugfs_create_file(name, S_IRUGO, parent, entry,
11758c2ecf20Sopenharmony_ci			    &debugfs_devm_entry_ops);
11768c2ecf20Sopenharmony_ci}
11778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(debugfs_create_devm_seqfile);
1178