2019-05-29 17:18:02 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-10-03 09:12:13 +03:00
/*
* Copyright ( c ) 2017 , NVIDIA CORPORATION . All rights reserved .
*/
# include <linux/debugfs.h>
# include <linux/dma-mapping.h>
# include <linux/uaccess.h>
# include <soc/tegra/bpmp.h>
# include <soc/tegra/bpmp-abi.h>
struct seqbuf {
char * buf ;
size_t pos ;
size_t size ;
} ;
static void seqbuf_init ( struct seqbuf * seqbuf , void * buf , size_t size )
{
seqbuf - > buf = buf ;
seqbuf - > size = size ;
seqbuf - > pos = 0 ;
}
static size_t seqbuf_avail ( struct seqbuf * seqbuf )
{
return seqbuf - > pos < seqbuf - > size ? seqbuf - > size - seqbuf - > pos : 0 ;
}
static size_t seqbuf_status ( struct seqbuf * seqbuf )
{
return seqbuf - > pos < = seqbuf - > size ? 0 : - EOVERFLOW ;
}
static int seqbuf_eof ( struct seqbuf * seqbuf )
{
return seqbuf - > pos > = seqbuf - > size ;
}
static int seqbuf_read ( struct seqbuf * seqbuf , void * buf , size_t nbyte )
{
nbyte = min ( nbyte , seqbuf_avail ( seqbuf ) ) ;
memcpy ( buf , seqbuf - > buf + seqbuf - > pos , nbyte ) ;
seqbuf - > pos + = nbyte ;
return seqbuf_status ( seqbuf ) ;
}
static int seqbuf_read_u32 ( struct seqbuf * seqbuf , uint32_t * v )
{
int err ;
err = seqbuf_read ( seqbuf , v , 4 ) ;
* v = le32_to_cpu ( * v ) ;
return err ;
}
static int seqbuf_read_str ( struct seqbuf * seqbuf , const char * * str )
{
* str = seqbuf - > buf + seqbuf - > pos ;
seqbuf - > pos + = strnlen ( * str , seqbuf_avail ( seqbuf ) ) ;
seqbuf - > pos + + ;
return seqbuf_status ( seqbuf ) ;
}
static void seqbuf_seek ( struct seqbuf * seqbuf , ssize_t offset )
{
seqbuf - > pos + = offset ;
}
/* map filename in Linux debugfs to corresponding entry in BPMP */
static const char * get_filename ( struct tegra_bpmp * bpmp ,
const struct file * file , char * buf , int size )
{
char root_path_buf [ 512 ] ;
const char * root_path ;
const char * filename ;
size_t root_len ;
root_path = dentry_path ( bpmp - > debugfs_mirror , root_path_buf ,
sizeof ( root_path_buf ) ) ;
if ( IS_ERR ( root_path ) )
return NULL ;
root_len = strlen ( root_path ) ;
filename = dentry_path ( file - > f_path . dentry , buf , size ) ;
if ( IS_ERR ( filename ) )
return NULL ;
if ( strlen ( filename ) < root_len | |
strncmp ( filename , root_path , root_len ) )
return NULL ;
filename + = root_len ;
return filename ;
}
static int mrq_debugfs_read ( struct tegra_bpmp * bpmp ,
dma_addr_t name , size_t sz_name ,
dma_addr_t data , size_t sz_data ,
size_t * nbytes )
{
struct mrq_debugfs_request req = {
. cmd = cpu_to_le32 ( CMD_DEBUGFS_READ ) ,
. fop = {
. fnameaddr = cpu_to_le32 ( ( uint32_t ) name ) ,
. fnamelen = cpu_to_le32 ( ( uint32_t ) sz_name ) ,
. dataaddr = cpu_to_le32 ( ( uint32_t ) data ) ,
. datalen = cpu_to_le32 ( ( uint32_t ) sz_data ) ,
} ,
} ;
struct mrq_debugfs_response resp ;
struct tegra_bpmp_message msg = {
. mrq = MRQ_DEBUGFS ,
. tx = {
. data = & req ,
. size = sizeof ( req ) ,
} ,
. rx = {
. data = & resp ,
. size = sizeof ( resp ) ,
} ,
} ;
int err ;
err = tegra_bpmp_transfer ( bpmp , & msg ) ;
if ( err < 0 )
return err ;
2020-07-12 13:01:14 +03:00
else if ( msg . rx . ret < 0 )
return - EINVAL ;
2017-10-03 09:12:13 +03:00
* nbytes = ( size_t ) resp . fop . nbytes ;
return 0 ;
}
static int mrq_debugfs_write ( struct tegra_bpmp * bpmp ,
dma_addr_t name , size_t sz_name ,
dma_addr_t data , size_t sz_data )
{
const struct mrq_debugfs_request req = {
. cmd = cpu_to_le32 ( CMD_DEBUGFS_WRITE ) ,
. fop = {
. fnameaddr = cpu_to_le32 ( ( uint32_t ) name ) ,
. fnamelen = cpu_to_le32 ( ( uint32_t ) sz_name ) ,
. dataaddr = cpu_to_le32 ( ( uint32_t ) data ) ,
. datalen = cpu_to_le32 ( ( uint32_t ) sz_data ) ,
} ,
} ;
struct tegra_bpmp_message msg = {
. mrq = MRQ_DEBUGFS ,
. tx = {
. data = & req ,
. size = sizeof ( req ) ,
} ,
} ;
return tegra_bpmp_transfer ( bpmp , & msg ) ;
}
static int mrq_debugfs_dumpdir ( struct tegra_bpmp * bpmp , dma_addr_t addr ,
size_t size , size_t * nbytes )
{
const struct mrq_debugfs_request req = {
. cmd = cpu_to_le32 ( CMD_DEBUGFS_DUMPDIR ) ,
. dumpdir = {
. dataaddr = cpu_to_le32 ( ( uint32_t ) addr ) ,
. datalen = cpu_to_le32 ( ( uint32_t ) size ) ,
} ,
} ;
struct mrq_debugfs_response resp ;
struct tegra_bpmp_message msg = {
. mrq = MRQ_DEBUGFS ,
. tx = {
. data = & req ,
. size = sizeof ( req ) ,
} ,
. rx = {
. data = & resp ,
. size = sizeof ( resp ) ,
} ,
} ;
int err ;
err = tegra_bpmp_transfer ( bpmp , & msg ) ;
if ( err < 0 )
return err ;
2020-07-12 13:01:14 +03:00
else if ( msg . rx . ret < 0 )
return - EINVAL ;
2017-10-03 09:12:13 +03:00
* nbytes = ( size_t ) resp . dumpdir . nbytes ;
return 0 ;
}
static int debugfs_show ( struct seq_file * m , void * p )
{
struct file * file = m - > private ;
struct inode * inode = file_inode ( file ) ;
struct tegra_bpmp * bpmp = inode - > i_private ;
const size_t datasize = m - > size ;
const size_t namesize = SZ_256 ;
void * datavirt , * namevirt ;
dma_addr_t dataphys , namephys ;
char buf [ 256 ] ;
const char * filename ;
size_t len , nbytes ;
2020-07-12 13:01:15 +03:00
int err ;
2017-10-03 09:12:13 +03:00
filename = get_filename ( bpmp , file , buf , sizeof ( buf ) ) ;
if ( ! filename )
return - ENOENT ;
namevirt = dma_alloc_coherent ( bpmp - > dev , namesize , & namephys ,
GFP_KERNEL | GFP_DMA32 ) ;
if ( ! namevirt )
return - ENOMEM ;
datavirt = dma_alloc_coherent ( bpmp - > dev , datasize , & dataphys ,
GFP_KERNEL | GFP_DMA32 ) ;
if ( ! datavirt ) {
2020-07-12 13:01:15 +03:00
err = - ENOMEM ;
2017-10-03 09:12:13 +03:00
goto free_namebuf ;
}
len = strlen ( filename ) ;
strncpy ( namevirt , filename , namesize ) ;
2020-07-12 13:01:15 +03:00
err = mrq_debugfs_read ( bpmp , namephys , len , dataphys , datasize ,
2017-10-03 09:12:13 +03:00
& nbytes ) ;
2020-07-12 13:01:15 +03:00
if ( ! err )
2017-10-03 09:12:13 +03:00
seq_write ( m , datavirt , nbytes ) ;
dma_free_coherent ( bpmp - > dev , datasize , datavirt , dataphys ) ;
free_namebuf :
dma_free_coherent ( bpmp - > dev , namesize , namevirt , namephys ) ;
2020-07-12 13:01:15 +03:00
return err ;
2017-10-03 09:12:13 +03:00
}
static int debugfs_open ( struct inode * inode , struct file * file )
{
return single_open_size ( file , debugfs_show , file , SZ_128K ) ;
}
static ssize_t debugfs_store ( struct file * file , const char __user * buf ,
size_t count , loff_t * f_pos )
{
struct inode * inode = file_inode ( file ) ;
struct tegra_bpmp * bpmp = inode - > i_private ;
const size_t datasize = count ;
const size_t namesize = SZ_256 ;
void * datavirt , * namevirt ;
dma_addr_t dataphys , namephys ;
char fnamebuf [ 256 ] ;
const char * filename ;
size_t len ;
2020-07-12 13:01:15 +03:00
int err ;
2017-10-03 09:12:13 +03:00
filename = get_filename ( bpmp , file , fnamebuf , sizeof ( fnamebuf ) ) ;
if ( ! filename )
return - ENOENT ;
namevirt = dma_alloc_coherent ( bpmp - > dev , namesize , & namephys ,
GFP_KERNEL | GFP_DMA32 ) ;
if ( ! namevirt )
return - ENOMEM ;
datavirt = dma_alloc_coherent ( bpmp - > dev , datasize , & dataphys ,
GFP_KERNEL | GFP_DMA32 ) ;
if ( ! datavirt ) {
2020-07-12 13:01:15 +03:00
err = - ENOMEM ;
2017-10-03 09:12:13 +03:00
goto free_namebuf ;
}
len = strlen ( filename ) ;
strncpy ( namevirt , filename , namesize ) ;
if ( copy_from_user ( datavirt , buf , count ) ) {
2020-07-12 13:01:15 +03:00
err = - EFAULT ;
2017-10-03 09:12:13 +03:00
goto free_databuf ;
}
2020-07-12 13:01:15 +03:00
err = mrq_debugfs_write ( bpmp , namephys , len , dataphys ,
2017-10-03 09:12:13 +03:00
count ) ;
free_databuf :
dma_free_coherent ( bpmp - > dev , datasize , datavirt , dataphys ) ;
free_namebuf :
dma_free_coherent ( bpmp - > dev , namesize , namevirt , namephys ) ;
2020-07-12 13:01:15 +03:00
return err ? : count ;
2017-10-03 09:12:13 +03:00
}
static const struct file_operations debugfs_fops = {
. open = debugfs_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. write = debugfs_store ,
. release = single_release ,
} ;
static int bpmp_populate_dir ( struct tegra_bpmp * bpmp , struct seqbuf * seqbuf ,
struct dentry * parent , uint32_t depth )
{
int err ;
uint32_t d , t ;
const char * name ;
struct dentry * dentry ;
while ( ! seqbuf_eof ( seqbuf ) ) {
err = seqbuf_read_u32 ( seqbuf , & d ) ;
if ( err < 0 )
return err ;
if ( d < depth ) {
seqbuf_seek ( seqbuf , - 4 ) ;
/* go up a level */
return 0 ;
} else if ( d ! = depth ) {
/* malformed data received from BPMP */
return - EIO ;
}
err = seqbuf_read_u32 ( seqbuf , & t ) ;
if ( err < 0 )
return err ;
err = seqbuf_read_str ( seqbuf , & name ) ;
if ( err < 0 )
return err ;
if ( t & DEBUGFS_S_ISDIR ) {
dentry = debugfs_create_dir ( name , parent ) ;
if ( ! dentry )
return - ENOMEM ;
err = bpmp_populate_dir ( bpmp , seqbuf , dentry , depth + 1 ) ;
if ( err < 0 )
return err ;
} else {
umode_t mode ;
mode = t & DEBUGFS_S_IRUSR ? S_IRUSR : 0 ;
mode | = t & DEBUGFS_S_IWUSR ? S_IWUSR : 0 ;
dentry = debugfs_create_file ( name , mode ,
parent , bpmp ,
& debugfs_fops ) ;
if ( ! dentry )
return - ENOMEM ;
}
}
return 0 ;
}
2020-07-12 13:01:16 +03:00
static int bpmp_populate_debugfs_shmem ( struct tegra_bpmp * bpmp ,
struct dentry * root )
2017-10-03 09:12:13 +03:00
{
struct seqbuf seqbuf ;
2020-07-12 13:01:16 +03:00
const size_t sz = SZ_512K ;
dma_addr_t phys ;
size_t nbytes ;
void * virt ;
2017-10-03 09:12:13 +03:00
int err ;
bpmp - > debugfs_mirror = debugfs_create_dir ( " debug " , root ) ;
if ( ! bpmp - > debugfs_mirror )
return - ENOMEM ;
2020-07-12 13:01:16 +03:00
virt = dma_alloc_coherent ( bpmp - > dev , sz , & phys ,
GFP_KERNEL | GFP_DMA32 ) ;
if ( ! virt )
return - ENOMEM ;
err = mrq_debugfs_dumpdir ( bpmp , phys , sz , & nbytes ) ;
2017-10-03 09:12:13 +03:00
if ( err < 0 ) {
2020-07-12 13:01:16 +03:00
goto free ;
} else if ( nbytes > sz ) {
err = - EINVAL ;
goto free ;
2017-10-03 09:12:13 +03:00
}
2020-07-12 13:01:16 +03:00
seqbuf_init ( & seqbuf , virt , nbytes ) ;
err = bpmp_populate_dir ( bpmp , & seqbuf , bpmp - > debugfs_mirror , 0 ) ;
free :
dma_free_coherent ( bpmp - > dev , sz , virt , phys ) ;
2017-10-03 09:12:13 +03:00
return err ;
}
int tegra_bpmp_init_debugfs ( struct tegra_bpmp * bpmp )
{
struct dentry * root ;
2020-07-12 13:01:15 +03:00
int err ;
2017-10-03 09:12:13 +03:00
2018-10-22 16:19:37 +03:00
if ( ! tegra_bpmp_mrq_is_supported ( bpmp , MRQ_DEBUGFS ) )
2017-10-03 09:12:13 +03:00
return 0 ;
root = debugfs_create_dir ( " bpmp " , NULL ) ;
if ( ! root )
return - ENOMEM ;
2020-07-12 13:01:16 +03:00
err = bpmp_populate_debugfs_shmem ( bpmp , root ) ;
2020-07-12 13:01:15 +03:00
if ( err < 0 )
2020-07-12 13:01:16 +03:00
debugfs_remove_recursive ( root ) ;
2017-10-03 09:12:13 +03:00
2020-07-12 13:01:15 +03:00
return err ;
2017-10-03 09:12:13 +03:00
}