linux/drivers/remoteproc/remoteproc_sysfs.c
<<
>>
Prefs
   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                if (rproc->state == RPROC_RUNNING ||
 198                    rproc->state == RPROC_ATTACHED)
 199                        return -EBUSY;
 200
 201                ret = rproc_boot(rproc);
 202                if (ret)
 203                        dev_err(&rproc->dev, "Boot failed: %d\n", ret);
 204        } else if (sysfs_streq(buf, "stop")) {
 205                if (rproc->state != RPROC_RUNNING &&
 206                    rproc->state != RPROC_ATTACHED)
 207                        return -EINVAL;
 208
 209                rproc_shutdown(rproc);
 210        } else if (sysfs_streq(buf, "detach")) {
 211                if (rproc->state != RPROC_ATTACHED)
 212                        return -EINVAL;
 213
 214                ret = rproc_detach(rproc);
 215        } else {
 216                dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
 217                ret = -EINVAL;
 218        }
 219        return ret ? ret : count;
 220}
 221static DEVICE_ATTR_RW(state);
 222
 223/* Expose the name of the remote processor via sysfs */
 224static ssize_t name_show(struct device *dev, struct device_attribute *attr,
 225                         char *buf)
 226{
 227        struct rproc *rproc = to_rproc(dev);
 228
 229        return sprintf(buf, "%s\n", rproc->name);
 230}
 231static DEVICE_ATTR_RO(name);
 232
 233static struct attribute *rproc_attrs[] = {
 234        &dev_attr_coredump.attr,
 235        &dev_attr_recovery.attr,
 236        &dev_attr_firmware.attr,
 237        &dev_attr_state.attr,
 238        &dev_attr_name.attr,
 239        NULL
 240};
 241
 242static const struct attribute_group rproc_devgroup = {
 243        .attrs = rproc_attrs
 244};
 245
 246static const struct attribute_group *rproc_devgroups[] = {
 247        &rproc_devgroup,
 248        NULL
 249};
 250
 251struct class rproc_class = {
 252        .name           = "remoteproc",
 253        .dev_groups     = rproc_devgroups,
 254};
 255
 256int __init rproc_init_sysfs(void)
 257{
 258        /* create remoteproc device class for sysfs */
 259        int err = class_register(&rproc_class);
 260
 261        if (err)
 262                pr_err("remoteproc: unable to register class\n");
 263        return err;
 264}
 265
 266void __exit rproc_exit_sysfs(void)
 267{
 268        class_unregister(&rproc_class);
 269}
 270