brintos

brintos / linux-shallow public Read only

0
0
Text · 16.0 KiB · 4221fed Raw
805 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2017, NVIDIA CORPORATION.  All rights reserved.4 */5#include <linux/debugfs.h>6#include <linux/dma-mapping.h>7#include <linux/slab.h>8#include <linux/uaccess.h>9 10#include <soc/tegra/bpmp.h>11#include <soc/tegra/bpmp-abi.h>12 13static DEFINE_MUTEX(bpmp_debug_lock);14 15struct seqbuf {16	char *buf;17	size_t pos;18	size_t size;19};20 21static void seqbuf_init(struct seqbuf *seqbuf, void *buf, size_t size)22{23	seqbuf->buf = buf;24	seqbuf->size = size;25	seqbuf->pos = 0;26}27 28static size_t seqbuf_avail(struct seqbuf *seqbuf)29{30	return seqbuf->pos < seqbuf->size ? seqbuf->size - seqbuf->pos : 0;31}32 33static size_t seqbuf_status(struct seqbuf *seqbuf)34{35	return seqbuf->pos <= seqbuf->size ? 0 : -EOVERFLOW;36}37 38static int seqbuf_eof(struct seqbuf *seqbuf)39{40	return seqbuf->pos >= seqbuf->size;41}42 43static int seqbuf_read(struct seqbuf *seqbuf, void *buf, size_t nbyte)44{45	nbyte = min(nbyte, seqbuf_avail(seqbuf));46	memcpy(buf, seqbuf->buf + seqbuf->pos, nbyte);47	seqbuf->pos += nbyte;48	return seqbuf_status(seqbuf);49}50 51static int seqbuf_read_u32(struct seqbuf *seqbuf, u32 *v)52{53	return seqbuf_read(seqbuf, v, 4);54}55 56static int seqbuf_read_str(struct seqbuf *seqbuf, const char **str)57{58	*str = seqbuf->buf + seqbuf->pos;59	seqbuf->pos += strnlen(*str, seqbuf_avail(seqbuf));60	seqbuf->pos++;61	return seqbuf_status(seqbuf);62}63 64static void seqbuf_seek(struct seqbuf *seqbuf, ssize_t offset)65{66	seqbuf->pos += offset;67}68 69/* map filename in Linux debugfs to corresponding entry in BPMP */70static const char *get_filename(struct tegra_bpmp *bpmp,71				const struct file *file, char *buf, int size)72{73	const char *root_path, *filename = NULL;74	char *root_path_buf;75	size_t root_len;76	size_t root_path_buf_len = 512;77 78	root_path_buf = kzalloc(root_path_buf_len, GFP_KERNEL);79	if (!root_path_buf)80		return NULL;81 82	root_path = dentry_path(bpmp->debugfs_mirror, root_path_buf,83				root_path_buf_len);84	if (IS_ERR(root_path))85		goto out;86 87	root_len = strlen(root_path);88 89	filename = dentry_path(file->f_path.dentry, buf, size);90	if (IS_ERR(filename)) {91		filename = NULL;92		goto out;93	}94 95	if (strlen(filename) < root_len || strncmp(filename, root_path, root_len)) {96		filename = NULL;97		goto out;98	}99 100	filename += root_len;101 102out:103	kfree(root_path_buf);104	return filename;105}106 107static int mrq_debug_open(struct tegra_bpmp *bpmp, const char *name,108			  u32 *fd, u32 *len, bool write)109{110	struct mrq_debug_request req = {111		.cmd = write ? CMD_DEBUG_OPEN_WO : CMD_DEBUG_OPEN_RO,112	};113	struct mrq_debug_response resp;114	struct tegra_bpmp_message msg = {115		.mrq = MRQ_DEBUG,116		.tx = {117			.data = &req,118			.size = sizeof(req),119		},120		.rx = {121			.data = &resp,122			.size = sizeof(resp),123		},124	};125	ssize_t sz_name;126	int err = 0;127 128	sz_name = strscpy(req.fop.name, name, sizeof(req.fop.name));129	if (sz_name < 0) {130		pr_err("File name too large: %s\n", name);131		return -EINVAL;132	}133 134	err = tegra_bpmp_transfer(bpmp, &msg);135	if (err < 0)136		return err;137	else if (msg.rx.ret < 0)138		return -EINVAL;139 140	*len = resp.fop.datalen;141	*fd = resp.fop.fd;142 143	return 0;144}145 146static int mrq_debug_close(struct tegra_bpmp *bpmp, u32 fd)147{148	struct mrq_debug_request req = {149		.cmd = CMD_DEBUG_CLOSE,150		.frd = {151			.fd = fd,152		},153	};154	struct mrq_debug_response resp;155	struct tegra_bpmp_message msg = {156		.mrq = MRQ_DEBUG,157		.tx = {158			.data = &req,159			.size = sizeof(req),160		},161		.rx = {162			.data = &resp,163			.size = sizeof(resp),164		},165	};166	int err = 0;167 168	err = tegra_bpmp_transfer(bpmp, &msg);169	if (err < 0)170		return err;171	else if (msg.rx.ret < 0)172		return -EINVAL;173 174	return 0;175}176 177static int mrq_debug_read(struct tegra_bpmp *bpmp, const char *name,178			  char *data, size_t sz_data, u32 *nbytes)179{180	struct mrq_debug_request req = {181		.cmd = CMD_DEBUG_READ,182	};183	struct mrq_debug_response resp;184	struct tegra_bpmp_message msg = {185		.mrq = MRQ_DEBUG,186		.tx = {187			.data = &req,188			.size = sizeof(req),189		},190		.rx = {191			.data = &resp,192			.size = sizeof(resp),193		},194	};195	u32 fd = 0, len = 0;196	int remaining, err, close_err;197 198	mutex_lock(&bpmp_debug_lock);199	err = mrq_debug_open(bpmp, name, &fd, &len, 0);200	if (err)201		goto out;202 203	if (len > sz_data) {204		err = -EFBIG;205		goto close;206	}207 208	req.frd.fd = fd;209	remaining = len;210 211	while (remaining > 0) {212		err = tegra_bpmp_transfer(bpmp, &msg);213		if (err < 0) {214			goto close;215		} else if (msg.rx.ret < 0) {216			err = -EINVAL;217			goto close;218		}219 220		if (resp.frd.readlen > remaining) {221			pr_err("%s: read data length invalid\n", __func__);222			err = -EINVAL;223			goto close;224		}225 226		memcpy(data, resp.frd.data, resp.frd.readlen);227		data += resp.frd.readlen;228		remaining -= resp.frd.readlen;229	}230 231	*nbytes = len;232 233close:234	close_err = mrq_debug_close(bpmp, fd);235	if (!err)236		err = close_err;237out:238	mutex_unlock(&bpmp_debug_lock);239	return err;240}241 242static int mrq_debug_write(struct tegra_bpmp *bpmp, const char *name,243			   uint8_t *data, size_t sz_data)244{245	struct mrq_debug_request req = {246		.cmd = CMD_DEBUG_WRITE247	};248	struct mrq_debug_response resp;249	struct tegra_bpmp_message msg = {250		.mrq = MRQ_DEBUG,251		.tx = {252			.data = &req,253			.size = sizeof(req),254		},255		.rx = {256			.data = &resp,257			.size = sizeof(resp),258		},259	};260	u32 fd = 0, len = 0;261	size_t remaining;262	int err;263 264	mutex_lock(&bpmp_debug_lock);265	err = mrq_debug_open(bpmp, name, &fd, &len, 1);266	if (err)267		goto out;268 269	if (sz_data > len) {270		err = -EINVAL;271		goto close;272	}273 274	req.fwr.fd = fd;275	remaining = sz_data;276 277	while (remaining > 0) {278		len = min(remaining, sizeof(req.fwr.data));279		memcpy(req.fwr.data, data, len);280		req.fwr.datalen = len;281 282		err = tegra_bpmp_transfer(bpmp, &msg);283		if (err < 0) {284			goto close;285		} else if (msg.rx.ret < 0) {286			err = -EINVAL;287			goto close;288		}289 290		data += req.fwr.datalen;291		remaining -= req.fwr.datalen;292	}293 294close:295	err = mrq_debug_close(bpmp, fd);296out:297	mutex_unlock(&bpmp_debug_lock);298	return err;299}300 301static int bpmp_debug_show(struct seq_file *m, void *p)302{303	struct file *file = m->private;304	struct inode *inode = file_inode(file);305	struct tegra_bpmp *bpmp = inode->i_private;306	char fnamebuf[256];307	const char *filename;308	struct mrq_debug_request req = {309		.cmd = CMD_DEBUG_READ,310	};311	struct mrq_debug_response resp;312	struct tegra_bpmp_message msg = {313		.mrq = MRQ_DEBUG,314		.tx = {315			.data = &req,316			.size = sizeof(req),317		},318		.rx = {319			.data = &resp,320			.size = sizeof(resp),321		},322	};323	u32 fd = 0, len = 0;324	int remaining, err, close_err;325 326	filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));327	if (!filename)328		return -ENOENT;329 330	mutex_lock(&bpmp_debug_lock);331	err = mrq_debug_open(bpmp, filename, &fd, &len, 0);332	if (err)333		goto out;334 335	req.frd.fd = fd;336	remaining = len;337 338	while (remaining > 0) {339		err = tegra_bpmp_transfer(bpmp, &msg);340		if (err < 0) {341			goto close;342		} else if (msg.rx.ret < 0) {343			err = -EINVAL;344			goto close;345		}346 347		if (resp.frd.readlen > remaining) {348			pr_err("%s: read data length invalid\n", __func__);349			err = -EINVAL;350			goto close;351		}352 353		seq_write(m, resp.frd.data, resp.frd.readlen);354		remaining -= resp.frd.readlen;355	}356 357close:358	close_err = mrq_debug_close(bpmp, fd);359	if (!err)360		err = close_err;361out:362	mutex_unlock(&bpmp_debug_lock);363	return err;364}365 366static ssize_t bpmp_debug_store(struct file *file, const char __user *buf,367		size_t count, loff_t *f_pos)368{369	struct inode *inode = file_inode(file);370	struct tegra_bpmp *bpmp = inode->i_private;371	char *databuf = NULL;372	char fnamebuf[256];373	const char *filename;374	ssize_t err;375 376	filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));377	if (!filename)378		return -ENOENT;379 380	databuf = memdup_user(buf, count);381	if (IS_ERR(databuf))382		return PTR_ERR(databuf);383 384	err = mrq_debug_write(bpmp, filename, databuf, count);385	kfree(databuf);386 387	return err ?: count;388}389 390static int bpmp_debug_open(struct inode *inode, struct file *file)391{392	return single_open_size(file, bpmp_debug_show, file, SZ_256K);393}394 395static const struct file_operations bpmp_debug_fops = {396	.open		= bpmp_debug_open,397	.read		= seq_read,398	.llseek		= seq_lseek,399	.write		= bpmp_debug_store,400	.release	= single_release,401};402 403static int bpmp_populate_debugfs_inband(struct tegra_bpmp *bpmp,404					struct dentry *parent,405					char *ppath)406{407	const size_t pathlen = SZ_256;408	const size_t bufsize = SZ_16K;409	struct dentry *dentry;410	u32 dsize, attrs = 0;411	struct seqbuf seqbuf;412	char *buf, *pathbuf;413	const char *name;414	int err = 0;415 416	if (!bpmp || !parent || !ppath)417		return -EINVAL;418 419	buf = kmalloc(bufsize, GFP_KERNEL);420	if (!buf)421		return -ENOMEM;422 423	pathbuf = kzalloc(pathlen, GFP_KERNEL);424	if (!pathbuf) {425		kfree(buf);426		return -ENOMEM;427	}428 429	err = mrq_debug_read(bpmp, ppath, buf, bufsize, &dsize);430	if (err)431		goto out;432 433	seqbuf_init(&seqbuf, buf, dsize);434 435	while (!seqbuf_eof(&seqbuf)) {436		err = seqbuf_read_u32(&seqbuf, &attrs);437		if (err)438			goto out;439 440		err = seqbuf_read_str(&seqbuf, &name);441		if (err < 0)442			goto out;443 444		if (attrs & DEBUGFS_S_ISDIR) {445			size_t len;446 447			dentry = debugfs_create_dir(name, parent);448			if (IS_ERR(dentry)) {449				err = PTR_ERR(dentry);450				goto out;451			}452 453			len = snprintf(pathbuf, pathlen, "%s%s/", ppath, name);454			if (len >= pathlen) {455				err = -EINVAL;456				goto out;457			}458 459			err = bpmp_populate_debugfs_inband(bpmp, dentry,460							   pathbuf);461			if (err < 0)462				goto out;463		} else {464			umode_t mode;465 466			mode = attrs & DEBUGFS_S_IRUSR ? 0400 : 0;467			mode |= attrs & DEBUGFS_S_IWUSR ? 0200 : 0;468			dentry = debugfs_create_file(name, mode, parent, bpmp,469						     &bpmp_debug_fops);470			if (IS_ERR(dentry)) {471				err = -ENOMEM;472				goto out;473			}474		}475	}476 477out:478	kfree(pathbuf);479	kfree(buf);480 481	return err;482}483 484static int mrq_debugfs_read(struct tegra_bpmp *bpmp,485			    dma_addr_t name, size_t sz_name,486			    dma_addr_t data, size_t sz_data,487			    size_t *nbytes)488{489	struct mrq_debugfs_request req = {490		.cmd = CMD_DEBUGFS_READ,491		.fop = {492			.fnameaddr = (u32)name,493			.fnamelen = (u32)sz_name,494			.dataaddr = (u32)data,495			.datalen = (u32)sz_data,496		},497	};498	struct mrq_debugfs_response resp;499	struct tegra_bpmp_message msg = {500		.mrq = MRQ_DEBUGFS,501		.tx = {502			.data = &req,503			.size = sizeof(req),504		},505		.rx = {506			.data = &resp,507			.size = sizeof(resp),508		},509	};510	int err;511 512	err = tegra_bpmp_transfer(bpmp, &msg);513	if (err < 0)514		return err;515	else if (msg.rx.ret < 0)516		return -EINVAL;517 518	*nbytes = (size_t)resp.fop.nbytes;519 520	return 0;521}522 523static int mrq_debugfs_write(struct tegra_bpmp *bpmp,524			     dma_addr_t name, size_t sz_name,525			     dma_addr_t data, size_t sz_data)526{527	const struct mrq_debugfs_request req = {528		.cmd = CMD_DEBUGFS_WRITE,529		.fop = {530			.fnameaddr = (u32)name,531			.fnamelen = (u32)sz_name,532			.dataaddr = (u32)data,533			.datalen = (u32)sz_data,534		},535	};536	struct tegra_bpmp_message msg = {537		.mrq = MRQ_DEBUGFS,538		.tx = {539			.data = &req,540			.size = sizeof(req),541		},542	};543 544	return tegra_bpmp_transfer(bpmp, &msg);545}546 547static int mrq_debugfs_dumpdir(struct tegra_bpmp *bpmp, dma_addr_t addr,548			       size_t size, size_t *nbytes)549{550	const struct mrq_debugfs_request req = {551		.cmd = CMD_DEBUGFS_DUMPDIR,552		.dumpdir = {553			.dataaddr = (u32)addr,554			.datalen = (u32)size,555		},556	};557	struct mrq_debugfs_response resp;558	struct tegra_bpmp_message msg = {559		.mrq = MRQ_DEBUGFS,560		.tx = {561			.data = &req,562			.size = sizeof(req),563		},564		.rx = {565			.data = &resp,566			.size = sizeof(resp),567		},568	};569	int err;570 571	err = tegra_bpmp_transfer(bpmp, &msg);572	if (err < 0)573		return err;574	else if (msg.rx.ret < 0)575		return -EINVAL;576 577	*nbytes = (size_t)resp.dumpdir.nbytes;578 579	return 0;580}581 582static int debugfs_show(struct seq_file *m, void *p)583{584	struct file *file = m->private;585	struct inode *inode = file_inode(file);586	struct tegra_bpmp *bpmp = inode->i_private;587	const size_t datasize = m->size;588	const size_t namesize = SZ_256;589	void *datavirt, *namevirt;590	dma_addr_t dataphys, namephys;591	char buf[256];592	const char *filename;593	size_t len, nbytes;594	int err;595 596	filename = get_filename(bpmp, file, buf, sizeof(buf));597	if (!filename)598		return -ENOENT;599 600	namevirt = dma_alloc_coherent(bpmp->dev, namesize, &namephys,601				      GFP_KERNEL | GFP_DMA32);602	if (!namevirt)603		return -ENOMEM;604 605	datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys,606				      GFP_KERNEL | GFP_DMA32);607	if (!datavirt) {608		err = -ENOMEM;609		goto free_namebuf;610	}611 612	len = strlen(filename);613	strscpy_pad(namevirt, filename, namesize);614 615	err = mrq_debugfs_read(bpmp, namephys, len, dataphys, datasize,616			       &nbytes);617 618	if (!err)619		seq_write(m, datavirt, nbytes);620 621	dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys);622free_namebuf:623	dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);624 625	return err;626}627 628static int debugfs_open(struct inode *inode, struct file *file)629{630	return single_open_size(file, debugfs_show, file, SZ_128K);631}632 633static ssize_t debugfs_store(struct file *file, const char __user *buf,634		size_t count, loff_t *f_pos)635{636	struct inode *inode = file_inode(file);637	struct tegra_bpmp *bpmp = inode->i_private;638	const size_t datasize = count;639	const size_t namesize = SZ_256;640	void *datavirt, *namevirt;641	dma_addr_t dataphys, namephys;642	char fnamebuf[256];643	const char *filename;644	size_t len;645	int err;646 647	filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));648	if (!filename)649		return -ENOENT;650 651	namevirt = dma_alloc_coherent(bpmp->dev, namesize, &namephys,652				      GFP_KERNEL | GFP_DMA32);653	if (!namevirt)654		return -ENOMEM;655 656	datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys,657				      GFP_KERNEL | GFP_DMA32);658	if (!datavirt) {659		err = -ENOMEM;660		goto free_namebuf;661	}662 663	len = strlen(filename);664	strscpy_pad(namevirt, filename, namesize);665 666	if (copy_from_user(datavirt, buf, count)) {667		err = -EFAULT;668		goto free_databuf;669	}670 671	err = mrq_debugfs_write(bpmp, namephys, len, dataphys,672				count);673 674free_databuf:675	dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys);676free_namebuf:677	dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);678 679	return err ?: count;680}681 682static const struct file_operations debugfs_fops = {683	.open		= debugfs_open,684	.read		= seq_read,685	.llseek		= seq_lseek,686	.write		= debugfs_store,687	.release	= single_release,688};689 690static int bpmp_populate_dir(struct tegra_bpmp *bpmp, struct seqbuf *seqbuf,691			     struct dentry *parent, u32 depth)692{693	int err;694	u32 d, t;695	const char *name;696	struct dentry *dentry;697 698	while (!seqbuf_eof(seqbuf)) {699		err = seqbuf_read_u32(seqbuf, &d);700		if (err < 0)701			return err;702 703		if (d < depth) {704			seqbuf_seek(seqbuf, -4);705			/* go up a level */706			return 0;707		} else if (d != depth) {708			/* malformed data received from BPMP */709			return -EIO;710		}711 712		err = seqbuf_read_u32(seqbuf, &t);713		if (err < 0)714			return err;715		err = seqbuf_read_str(seqbuf, &name);716		if (err < 0)717			return err;718 719		if (t & DEBUGFS_S_ISDIR) {720			dentry = debugfs_create_dir(name, parent);721			if (IS_ERR(dentry))722				return -ENOMEM;723			err = bpmp_populate_dir(bpmp, seqbuf, dentry, depth+1);724			if (err < 0)725				return err;726		} else {727			umode_t mode;728 729			mode = t & DEBUGFS_S_IRUSR ? S_IRUSR : 0;730			mode |= t & DEBUGFS_S_IWUSR ? S_IWUSR : 0;731			dentry = debugfs_create_file(name, mode,732						     parent, bpmp,733						     &debugfs_fops);734			if (IS_ERR(dentry))735				return -ENOMEM;736		}737	}738 739	return 0;740}741 742static int bpmp_populate_debugfs_shmem(struct tegra_bpmp *bpmp)743{744	struct seqbuf seqbuf;745	const size_t sz = SZ_512K;746	dma_addr_t phys;747	size_t nbytes;748	void *virt;749	int err;750 751	virt = dma_alloc_coherent(bpmp->dev, sz, &phys,752				  GFP_KERNEL | GFP_DMA32);753	if (!virt)754		return -ENOMEM;755 756	err = mrq_debugfs_dumpdir(bpmp, phys, sz, &nbytes);757	if (err < 0) {758		goto free;759	} else if (nbytes > sz) {760		err = -EINVAL;761		goto free;762	}763 764	seqbuf_init(&seqbuf, virt, nbytes);765	err = bpmp_populate_dir(bpmp, &seqbuf, bpmp->debugfs_mirror, 0);766free:767	dma_free_coherent(bpmp->dev, sz, virt, phys);768 769	return err;770}771 772int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp)773{774	struct dentry *root;775	bool inband;776	int err;777 778	inband = tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUG);779 780	if (!inband && !tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS))781		return 0;782 783	root = debugfs_create_dir("bpmp", NULL);784	if (IS_ERR(root))785		return -ENOMEM;786 787	bpmp->debugfs_mirror = debugfs_create_dir("debug", root);788	if (IS_ERR(bpmp->debugfs_mirror)) {789		err = -ENOMEM;790		goto out;791	}792 793	if (inband)794		err = bpmp_populate_debugfs_inband(bpmp, bpmp->debugfs_mirror,795						   "/");796	else797		err = bpmp_populate_debugfs_shmem(bpmp);798 799out:800	if (err < 0)801		debugfs_remove_recursive(root);802 803	return err;804}805