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 ) ;
2021-03-04 00:47:02 +03:00
return sysfs_emit ( buf , " %s " , rproc - > recovery_disabled ? " disabled \n " : " enabled \n " ) ;
2020-10-02 21:09:04 +03:00
}
/*
* 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 ) ;
2021-03-04 00:47:02 +03:00
return sysfs_emit ( buf , " %s \n " , rproc_coredump_str [ rproc - > dump_conf ] ) ;
2020-10-02 21:09:03 +03:00
}
/*
* 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 .
*/
2021-03-12 19:24:40 +03:00
if ( rproc - > state = = RPROC_ATTACHED )
2020-07-14 22:50:35 +03:00
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 ) ;
2020-11-21 06:20:42 +03:00
int err ;
2016-10-19 15:05:47 +03:00
2020-11-21 06:20:42 +03:00
err = rproc_set_firmware ( rproc , buf ) ;
2016-10-19 15:05:47 +03:00
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 " ,
2021-03-12 19:24:39 +03:00
[ RPROC_ATTACHED ] = " attached " ,
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 " ) ) {
2021-03-12 19:24:50 +03:00
if ( rproc - > state = = RPROC_RUNNING | |
rproc - > state = = RPROC_ATTACHED )
2016-10-19 15:05:47 +03:00
return - EBUSY ;
ret = rproc_boot ( rproc ) ;
if ( ret )
dev_err ( & rproc - > dev , " Boot failed: %d \n " , ret ) ;
} else if ( sysfs_streq ( buf , " stop " ) ) {
2021-03-12 19:24:51 +03:00
if ( rproc - > state ! = RPROC_RUNNING & &
rproc - > state ! = RPROC_ATTACHED )
2016-10-19 15:05:47 +03:00
return - EINVAL ;
rproc_shutdown ( rproc ) ;
2021-03-12 19:24:52 +03:00
} else if ( sysfs_streq ( buf , " detach " ) ) {
if ( rproc - > state ! = RPROC_ATTACHED )
return - EINVAL ;
ret = rproc_detach ( rproc ) ;
2016-10-19 15:05:47 +03:00
} 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 ) ;
2022-02-16 11:12:23 +03:00
static umode_t rproc_is_visible ( struct kobject * kobj , struct attribute * attr ,
int n )
{
struct device * dev = kobj_to_dev ( kobj ) ;
struct rproc * rproc = to_rproc ( dev ) ;
umode_t mode = attr - > mode ;
if ( rproc - > sysfs_read_only & & ( attr = = & dev_attr_recovery . attr | |
attr = = & dev_attr_firmware . attr | |
attr = = & dev_attr_state . attr | |
attr = = & dev_attr_coredump . attr ) )
mode = 0444 ;
return mode ;
}
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 = {
2022-02-16 11:12:23 +03:00
. attrs = rproc_attrs ,
. is_visible = rproc_is_visible ,
2016-10-19 15:05:47 +03:00
} ;
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 ) ;
}