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