cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

remoteproc_sysfs.c (7948B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Remote Processor Framework
      4 */
      5
      6#include <linux/remoteproc.h>
      7#include <linux/slab.h>
      8
      9#include "remoteproc_internal.h"
     10
     11#define to_rproc(d) container_of(d, struct rproc, dev)
     12
     13static ssize_t recovery_show(struct device *dev,
     14			     struct device_attribute *attr, char *buf)
     15{
     16	struct rproc *rproc = to_rproc(dev);
     17
     18	return sysfs_emit(buf, "%s", rproc->recovery_disabled ? "disabled\n" : "enabled\n");
     19}
     20
     21/*
     22 * By writing to the 'recovery' sysfs entry, we control the behavior of the
     23 * recovery mechanism dynamically. The default value of this entry is "enabled".
     24 *
     25 * The 'recovery' sysfs entry supports these commands:
     26 *
     27 * enabled:	When enabled, the remote processor will be automatically
     28 *		recovered whenever it crashes. Moreover, if the remote
     29 *		processor crashes while recovery is disabled, it will
     30 *		be automatically recovered too as soon as recovery is enabled.
     31 *
     32 * disabled:	When disabled, a remote processor will remain in a crashed
     33 *		state if it crashes. This is useful for debugging purposes;
     34 *		without it, debugging a crash is substantially harder.
     35 *
     36 * recover:	This function will trigger an immediate recovery if the
     37 *		remote processor is in a crashed state, without changing
     38 *		or checking the recovery state (enabled/disabled).
     39 *		This is useful during debugging sessions, when one expects
     40 *		additional crashes to happen after enabling recovery. In this
     41 *		case, enabling recovery will make it hard to debug subsequent
     42 *		crashes, so it's recommended to keep recovery disabled, and
     43 *		instead use the "recover" command as needed.
     44 */
     45static ssize_t recovery_store(struct device *dev,
     46			      struct device_attribute *attr,
     47			      const char *buf, size_t count)
     48{
     49	struct rproc *rproc = to_rproc(dev);
     50
     51	if (sysfs_streq(buf, "enabled")) {
     52		/* change the flag and begin the recovery process if needed */
     53		rproc->recovery_disabled = false;
     54		rproc_trigger_recovery(rproc);
     55	} else if (sysfs_streq(buf, "disabled")) {
     56		rproc->recovery_disabled = true;
     57	} else if (sysfs_streq(buf, "recover")) {
     58		/* begin the recovery process without changing the flag */
     59		rproc_trigger_recovery(rproc);
     60	} else {
     61		return -EINVAL;
     62	}
     63
     64	return count;
     65}
     66static DEVICE_ATTR_RW(recovery);
     67
     68/*
     69 * A coredump-configuration-to-string lookup table, for exposing a
     70 * human readable configuration via sysfs. Always keep in sync with
     71 * enum rproc_coredump_mechanism
     72 */
     73static const char * const rproc_coredump_str[] = {
     74	[RPROC_COREDUMP_DISABLED]	= "disabled",
     75	[RPROC_COREDUMP_ENABLED]	= "enabled",
     76	[RPROC_COREDUMP_INLINE]		= "inline",
     77};
     78
     79/* Expose the current coredump configuration via debugfs */
     80static ssize_t coredump_show(struct device *dev,
     81			     struct device_attribute *attr, char *buf)
     82{
     83	struct rproc *rproc = to_rproc(dev);
     84
     85	return sysfs_emit(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]);
     86}
     87
     88/*
     89 * By writing to the 'coredump' sysfs entry, we control the behavior of the
     90 * coredump mechanism dynamically. The default value of this entry is "default".
     91 *
     92 * The 'coredump' sysfs entry supports these commands:
     93 *
     94 * disabled:	This is the default coredump mechanism. Recovery will proceed
     95 *		without collecting any dump.
     96 *
     97 * default:	When the remoteproc crashes the entire coredump will be
     98 *		copied to a separate buffer and exposed to userspace.
     99 *
    100 * inline:	The coredump will not be copied to a separate buffer and the
    101 *		recovery process will have to wait until data is read by
    102 *		userspace. But this avoid usage of extra memory.
    103 */
    104static ssize_t coredump_store(struct device *dev,
    105			      struct device_attribute *attr,
    106			      const char *buf, size_t count)
    107{
    108	struct rproc *rproc = to_rproc(dev);
    109
    110	if (rproc->state == RPROC_CRASHED) {
    111		dev_err(&rproc->dev, "can't change coredump configuration\n");
    112		return -EBUSY;
    113	}
    114
    115	if (sysfs_streq(buf, "disabled")) {
    116		rproc->dump_conf = RPROC_COREDUMP_DISABLED;
    117	} else if (sysfs_streq(buf, "enabled")) {
    118		rproc->dump_conf = RPROC_COREDUMP_ENABLED;
    119	} else if (sysfs_streq(buf, "inline")) {
    120		rproc->dump_conf = RPROC_COREDUMP_INLINE;
    121	} else {
    122		dev_err(&rproc->dev, "Invalid coredump configuration\n");
    123		return -EINVAL;
    124	}
    125
    126	return count;
    127}
    128static DEVICE_ATTR_RW(coredump);
    129
    130/* Expose the loaded / running firmware name via sysfs */
    131static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
    132			  char *buf)
    133{
    134	struct rproc *rproc = to_rproc(dev);
    135	const char *firmware = rproc->firmware;
    136
    137	/*
    138	 * If the remote processor has been started by an external
    139	 * entity we have no idea of what image it is running.  As such
    140	 * simply display a generic string rather then rproc->firmware.
    141	 */
    142	if (rproc->state == RPROC_ATTACHED)
    143		firmware = "unknown";
    144
    145	return sprintf(buf, "%s\n", firmware);
    146}
    147
    148/* Change firmware name via sysfs */
    149static ssize_t firmware_store(struct device *dev,
    150			      struct device_attribute *attr,
    151			      const char *buf, size_t count)
    152{
    153	struct rproc *rproc = to_rproc(dev);
    154	int err;
    155
    156	err = rproc_set_firmware(rproc, buf);
    157
    158	return err ? err : count;
    159}
    160static DEVICE_ATTR_RW(firmware);
    161
    162/*
    163 * A state-to-string lookup table, for exposing a human readable state
    164 * via sysfs. Always keep in sync with enum rproc_state
    165 */
    166static const char * const rproc_state_string[] = {
    167	[RPROC_OFFLINE]		= "offline",
    168	[RPROC_SUSPENDED]	= "suspended",
    169	[RPROC_RUNNING]		= "running",
    170	[RPROC_CRASHED]		= "crashed",
    171	[RPROC_DELETED]		= "deleted",
    172	[RPROC_ATTACHED]	= "attached",
    173	[RPROC_DETACHED]	= "detached",
    174	[RPROC_LAST]		= "invalid",
    175};
    176
    177/* Expose the state of the remote processor via sysfs */
    178static ssize_t state_show(struct device *dev, struct device_attribute *attr,
    179			  char *buf)
    180{
    181	struct rproc *rproc = to_rproc(dev);
    182	unsigned int state;
    183
    184	state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
    185	return sprintf(buf, "%s\n", rproc_state_string[state]);
    186}
    187
    188/* Change remote processor state via sysfs */
    189static ssize_t state_store(struct device *dev,
    190			      struct device_attribute *attr,
    191			      const char *buf, size_t count)
    192{
    193	struct rproc *rproc = to_rproc(dev);
    194	int ret = 0;
    195
    196	if (sysfs_streq(buf, "start")) {
    197		ret = rproc_boot(rproc);
    198		if (ret)
    199			dev_err(&rproc->dev, "Boot failed: %d\n", ret);
    200	} else if (sysfs_streq(buf, "stop")) {
    201		ret = rproc_shutdown(rproc);
    202	} else if (sysfs_streq(buf, "detach")) {
    203		ret = rproc_detach(rproc);
    204	} else {
    205		dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
    206		ret = -EINVAL;
    207	}
    208	return ret ? ret : count;
    209}
    210static DEVICE_ATTR_RW(state);
    211
    212/* Expose the name of the remote processor via sysfs */
    213static ssize_t name_show(struct device *dev, struct device_attribute *attr,
    214			 char *buf)
    215{
    216	struct rproc *rproc = to_rproc(dev);
    217
    218	return sprintf(buf, "%s\n", rproc->name);
    219}
    220static DEVICE_ATTR_RO(name);
    221
    222static umode_t rproc_is_visible(struct kobject *kobj, struct attribute *attr,
    223				int n)
    224{
    225	struct device *dev = kobj_to_dev(kobj);
    226	struct rproc *rproc = to_rproc(dev);
    227	umode_t mode = attr->mode;
    228
    229	if (rproc->sysfs_read_only && (attr == &dev_attr_recovery.attr ||
    230				       attr == &dev_attr_firmware.attr ||
    231				       attr == &dev_attr_state.attr ||
    232				       attr == &dev_attr_coredump.attr))
    233		mode = 0444;
    234
    235	return mode;
    236}
    237
    238static struct attribute *rproc_attrs[] = {
    239	&dev_attr_coredump.attr,
    240	&dev_attr_recovery.attr,
    241	&dev_attr_firmware.attr,
    242	&dev_attr_state.attr,
    243	&dev_attr_name.attr,
    244	NULL
    245};
    246
    247static const struct attribute_group rproc_devgroup = {
    248	.attrs = rproc_attrs,
    249	.is_visible = rproc_is_visible,
    250};
    251
    252static const struct attribute_group *rproc_devgroups[] = {
    253	&rproc_devgroup,
    254	NULL
    255};
    256
    257struct class rproc_class = {
    258	.name		= "remoteproc",
    259	.dev_groups	= rproc_devgroups,
    260};
    261
    262int __init rproc_init_sysfs(void)
    263{
    264	/* create remoteproc device class for sysfs */
    265	int err = class_register(&rproc_class);
    266
    267	if (err)
    268		pr_err("remoteproc: unable to register class\n");
    269	return err;
    270}
    271
    272void __exit rproc_exit_sysfs(void)
    273{
    274	class_unregister(&rproc_class);
    275}