2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-10-19 15:05:47 +03:00
/*
* Remote Processor Framework
*/
# include <linux/remoteproc.h>
2020-04-07 03:39:37 +03:00
# include <linux/slab.h>
2016-10-19 15:05:47 +03:00
# include "remoteproc_internal.h"
# define to_rproc(d) container_of(d, struct rproc, dev)
2020-10-02 21:09:04 +03:00
static ssize_t recovery_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct rproc * rproc = to_rproc ( dev ) ;
return sprintf ( buf , " %s " , rproc - > recovery_disabled ? " disabled \n " : " enabled \n " ) ;
}
/*
* By writing to the ' recovery ' sysfs entry , we control the behavior of the
* recovery mechanism dynamically . The default value of this entry is " enabled " .
*
* The ' recovery ' sysfs entry supports these commands :
*
* enabled : When enabled , the remote processor will be automatically
* recovered whenever it crashes . Moreover , if the remote
* processor crashes while recovery is disabled , it will
* be automatically recovered too as soon as recovery is enabled .
*
* disabled : When disabled , a remote processor will remain in a crashed
* state if it crashes . This is useful for debugging purposes ;
* without it , debugging a crash is substantially harder .
*
* recover : This function will trigger an immediate recovery if the
* remote processor is in a crashed state , without changing
* or checking the recovery state ( enabled / disabled ) .
* This is useful during debugging sessions , when one expects
* additional crashes to happen after enabling recovery . In this
* case , enabling recovery will make it hard to debug subsequent
* crashes , so it ' s recommended to keep recovery disabled , and
* instead use the " recover " command as needed .
*/
static ssize_t recovery_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct rproc * rproc = to_rproc ( dev ) ;
if ( sysfs_streq ( buf , " enabled " ) ) {
/* change the flag and begin the recovery process if needed */
rproc - > recovery_disabled = false ;
rproc_trigger_recovery ( rproc ) ;
} else if ( sysfs_streq ( buf , " disabled " ) ) {
rproc - > recovery_disabled = true ;
} else if ( sysfs_streq ( buf , " recover " ) ) {
/* begin the recovery process without changing the flag */
rproc_trigger_recovery ( rproc ) ;
} else {
return - EINVAL ;
}
return count ;
}
static DEVICE_ATTR_RW ( recovery ) ;
2020-10-02 21:09:03 +03:00
/*
* A coredump - configuration - to - string lookup table , for exposing a
* human readable configuration via sysfs . Always keep in sync with
* enum rproc_coredump_mechanism
*/
static const char * const rproc_coredump_str [ ] = {
[ RPROC_COREDUMP_DISABLED ] = " disabled " ,
[ RPROC_COREDUMP_ENABLED ] = " enabled " ,
[ RPROC_COREDUMP_INLINE ] = " inline " ,
} ;
/* Expose the current coredump configuration via debugfs */
static ssize_t coredump_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct rproc * rproc = to_rproc ( dev ) ;
return sprintf ( buf , " %s \n " , rproc_coredump_str [ rproc - > dump_conf ] ) ;
}
/*
* By writing to the ' coredump ' sysfs entry , we control the behavior of the
* coredump mechanism dynamically . The default value of this entry is " default " .
*
* The ' coredump ' sysfs entry supports these commands :
*
* disabled : This is the default coredump mechanism . Recovery will proceed
* without collecting any dump .
*
* default : When the remoteproc crashes the entire coredump will be
* copied to a separate buffer and exposed to userspace .
*
* inline : The coredump will not be copied to a separate buffer and the
* recovery process will have to wait until data is read by
* userspace . But this avoid usage of extra memory .
*/
static ssize_t coredump_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct rproc * rproc = to_rproc ( dev ) ;
if ( rproc - > state = = RPROC_CRASHED ) {
dev_err ( & rproc - > dev , " can't change coredump configuration \n " ) ;
return - EBUSY ;
}
if ( sysfs_streq ( buf , " disabled " ) ) {
rproc - > dump_conf = RPROC_COREDUMP_DISABLED ;
} else if ( sysfs_streq ( buf , " enabled " ) ) {
rproc - > dump_conf = RPROC_COREDUMP_ENABLED ;
} else if ( sysfs_streq ( buf , " inline " ) ) {
rproc - > dump_conf = RPROC_COREDUMP_INLINE ;
} else {
dev_err ( & rproc - > dev , " Invalid coredump configuration \n " ) ;
return - EINVAL ;
}
return count ;
}
static DEVICE_ATTR_RW ( coredump ) ;
2016-10-19 15:05:47 +03:00
/* Expose the loaded / running firmware name via sysfs */
static ssize_t firmware_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct rproc * rproc = to_rproc ( dev ) ;
2020-07-14 22:50:35 +03:00
const char * firmware = rproc - > firmware ;
2016-10-19 15:05:47 +03:00
2020-07-14 22:50:35 +03:00
/*
* If the remote processor has been started by an external
* entity we have no idea of what image it is running . As such
* simply display a generic string rather then rproc - > firmware .
*
* Here we rely on the autonomous flag because a remote processor
* may have been attached to and currently in a running state .
*/
if ( rproc - > autonomous )
firmware = " unknown " ;
return sprintf ( buf , " %s \n " , firmware ) ;
2016-10-19 15:05:47 +03:00
}
/* Change firmware name via sysfs */
static ssize_t firmware_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct rproc * rproc = to_rproc ( dev ) ;
char * p ;
int err , len = count ;
err = mutex_lock_interruptible ( & rproc - > lock ) ;
if ( err ) {
dev_err ( dev , " can't lock rproc %s: %d \n " , rproc - > name , err ) ;
return - EINVAL ;
}
if ( rproc - > state ! = RPROC_OFFLINE ) {
dev_err ( dev , " can't change firmware while running \n " ) ;
err = - EBUSY ;
goto out ;
}
len = strcspn ( buf , " \n " ) ;
2018-09-15 03:37:22 +03:00
if ( ! len ) {
dev_err ( dev , " can't provide a NULL firmware \n " ) ;
err = - EINVAL ;
goto out ;
}
2016-10-19 15:05:47 +03:00
p = kstrndup ( buf , len , GFP_KERNEL ) ;
if ( ! p ) {
err = - ENOMEM ;
goto out ;
}
kfree ( rproc - > firmware ) ;
rproc - > firmware = p ;
out :
mutex_unlock ( & rproc - > lock ) ;
return err ? err : count ;
}
static DEVICE_ATTR_RW ( firmware ) ;
/*
* A state - to - string lookup table , for exposing a human readable state
* via sysfs . Always keep in sync with enum rproc_state
*/
static const char * const rproc_state_string [ ] = {
[ RPROC_OFFLINE ] = " offline " ,
[ RPROC_SUSPENDED ] = " suspended " ,
[ RPROC_RUNNING ] = " running " ,
[ RPROC_CRASHED ] = " crashed " ,
2017-01-24 04:53:18 +03:00
[ RPROC_DELETED ] = " deleted " ,
2020-07-14 22:50:27 +03:00
[ RPROC_DETACHED ] = " detached " ,
2016-10-19 15:05:47 +03:00
[ RPROC_LAST ] = " invalid " ,
} ;
/* Expose the state of the remote processor via sysfs */
static ssize_t state_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct rproc * rproc = to_rproc ( dev ) ;
unsigned int state ;
state = rproc - > state > RPROC_LAST ? RPROC_LAST : rproc - > state ;
return sprintf ( buf , " %s \n " , rproc_state_string [ state ] ) ;
}
/* Change remote processor state via sysfs */
static ssize_t state_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct rproc * rproc = to_rproc ( dev ) ;
int ret = 0 ;
if ( sysfs_streq ( buf , " start " ) ) {
if ( rproc - > state = = RPROC_RUNNING )
return - EBUSY ;
ret = rproc_boot ( rproc ) ;
if ( ret )
dev_err ( & rproc - > dev , " Boot failed: %d \n " , ret ) ;
} else if ( sysfs_streq ( buf , " stop " ) ) {
if ( rproc - > state ! = RPROC_RUNNING )
return - EINVAL ;
rproc_shutdown ( rproc ) ;
} else {
dev_err ( & rproc - > dev , " Unrecognised option: %s \n " , buf ) ;
ret = - EINVAL ;
}
return ret ? ret : count ;
}
static DEVICE_ATTR_RW ( state ) ;
2019-08-10 01:20:57 +03:00
/* Expose the name of the remote processor via sysfs */
static ssize_t name_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct rproc * rproc = to_rproc ( dev ) ;
return sprintf ( buf , " %s \n " , rproc - > name ) ;
}
static DEVICE_ATTR_RO ( name ) ;
2016-10-19 15:05:47 +03:00
static struct attribute * rproc_attrs [ ] = {
2020-10-02 21:09:03 +03:00
& dev_attr_coredump . attr ,
2020-10-02 21:09:04 +03:00
& dev_attr_recovery . attr ,
2016-10-19 15:05:47 +03:00
& dev_attr_firmware . attr ,
& dev_attr_state . attr ,
2019-08-10 01:20:57 +03:00
& dev_attr_name . attr ,
2016-10-19 15:05:47 +03:00
NULL
} ;
static const struct attribute_group rproc_devgroup = {
. attrs = rproc_attrs
} ;
static const struct attribute_group * rproc_devgroups [ ] = {
& rproc_devgroup ,
NULL
} ;
struct class rproc_class = {
. name = " remoteproc " ,
. dev_groups = rproc_devgroups ,
} ;
int __init rproc_init_sysfs ( void )
{
/* create remoteproc device class for sysfs */
int err = class_register ( & rproc_class ) ;
if ( err )
pr_err ( " remoteproc: unable to register class \n " ) ;
return err ;
}
void __exit rproc_exit_sysfs ( void )
{
class_unregister ( & rproc_class ) ;
}