Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Remote Processor Framework
0004  */
0005 
0006 #include <linux/remoteproc.h>
0007 #include <linux/slab.h>
0008 
0009 #include "remoteproc_internal.h"
0010 
0011 #define to_rproc(d) container_of(d, struct rproc, dev)
0012 
0013 static ssize_t recovery_show(struct device *dev,
0014                  struct device_attribute *attr, char *buf)
0015 {
0016     struct rproc *rproc = to_rproc(dev);
0017 
0018     return sysfs_emit(buf, "%s", rproc->recovery_disabled ? "disabled\n" : "enabled\n");
0019 }
0020 
0021 /*
0022  * By writing to the 'recovery' sysfs entry, we control the behavior of the
0023  * recovery mechanism dynamically. The default value of this entry is "enabled".
0024  *
0025  * The 'recovery' sysfs entry supports these commands:
0026  *
0027  * enabled: When enabled, the remote processor will be automatically
0028  *      recovered whenever it crashes. Moreover, if the remote
0029  *      processor crashes while recovery is disabled, it will
0030  *      be automatically recovered too as soon as recovery is enabled.
0031  *
0032  * disabled:    When disabled, a remote processor will remain in a crashed
0033  *      state if it crashes. This is useful for debugging purposes;
0034  *      without it, debugging a crash is substantially harder.
0035  *
0036  * recover: This function will trigger an immediate recovery if the
0037  *      remote processor is in a crashed state, without changing
0038  *      or checking the recovery state (enabled/disabled).
0039  *      This is useful during debugging sessions, when one expects
0040  *      additional crashes to happen after enabling recovery. In this
0041  *      case, enabling recovery will make it hard to debug subsequent
0042  *      crashes, so it's recommended to keep recovery disabled, and
0043  *      instead use the "recover" command as needed.
0044  */
0045 static ssize_t recovery_store(struct device *dev,
0046                   struct device_attribute *attr,
0047                   const char *buf, size_t count)
0048 {
0049     struct rproc *rproc = to_rproc(dev);
0050 
0051     if (sysfs_streq(buf, "enabled")) {
0052         /* change the flag and begin the recovery process if needed */
0053         rproc->recovery_disabled = false;
0054         rproc_trigger_recovery(rproc);
0055     } else if (sysfs_streq(buf, "disabled")) {
0056         rproc->recovery_disabled = true;
0057     } else if (sysfs_streq(buf, "recover")) {
0058         /* begin the recovery process without changing the flag */
0059         rproc_trigger_recovery(rproc);
0060     } else {
0061         return -EINVAL;
0062     }
0063 
0064     return count;
0065 }
0066 static DEVICE_ATTR_RW(recovery);
0067 
0068 /*
0069  * A coredump-configuration-to-string lookup table, for exposing a
0070  * human readable configuration via sysfs. Always keep in sync with
0071  * enum rproc_coredump_mechanism
0072  */
0073 static const char * const rproc_coredump_str[] = {
0074     [RPROC_COREDUMP_DISABLED]   = "disabled",
0075     [RPROC_COREDUMP_ENABLED]    = "enabled",
0076     [RPROC_COREDUMP_INLINE]     = "inline",
0077 };
0078 
0079 /* Expose the current coredump configuration via debugfs */
0080 static ssize_t coredump_show(struct device *dev,
0081                  struct device_attribute *attr, char *buf)
0082 {
0083     struct rproc *rproc = to_rproc(dev);
0084 
0085     return sysfs_emit(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]);
0086 }
0087 
0088 /*
0089  * By writing to the 'coredump' sysfs entry, we control the behavior of the
0090  * coredump mechanism dynamically. The default value of this entry is "default".
0091  *
0092  * The 'coredump' sysfs entry supports these commands:
0093  *
0094  * disabled:    This is the default coredump mechanism. Recovery will proceed
0095  *      without collecting any dump.
0096  *
0097  * default: When the remoteproc crashes the entire coredump will be
0098  *      copied to a separate buffer and exposed to userspace.
0099  *
0100  * inline:  The coredump will not be copied to a separate buffer and the
0101  *      recovery process will have to wait until data is read by
0102  *      userspace. But this avoid usage of extra memory.
0103  */
0104 static ssize_t coredump_store(struct device *dev,
0105                   struct device_attribute *attr,
0106                   const char *buf, size_t count)
0107 {
0108     struct rproc *rproc = to_rproc(dev);
0109 
0110     if (rproc->state == RPROC_CRASHED) {
0111         dev_err(&rproc->dev, "can't change coredump configuration\n");
0112         return -EBUSY;
0113     }
0114 
0115     if (sysfs_streq(buf, "disabled")) {
0116         rproc->dump_conf = RPROC_COREDUMP_DISABLED;
0117     } else if (sysfs_streq(buf, "enabled")) {
0118         rproc->dump_conf = RPROC_COREDUMP_ENABLED;
0119     } else if (sysfs_streq(buf, "inline")) {
0120         rproc->dump_conf = RPROC_COREDUMP_INLINE;
0121     } else {
0122         dev_err(&rproc->dev, "Invalid coredump configuration\n");
0123         return -EINVAL;
0124     }
0125 
0126     return count;
0127 }
0128 static DEVICE_ATTR_RW(coredump);
0129 
0130 /* Expose the loaded / running firmware name via sysfs */
0131 static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
0132               char *buf)
0133 {
0134     struct rproc *rproc = to_rproc(dev);
0135     const char *firmware = rproc->firmware;
0136 
0137     /*
0138      * If the remote processor has been started by an external
0139      * entity we have no idea of what image it is running.  As such
0140      * simply display a generic string rather then rproc->firmware.
0141      */
0142     if (rproc->state == RPROC_ATTACHED)
0143         firmware = "unknown";
0144 
0145     return sprintf(buf, "%s\n", firmware);
0146 }
0147 
0148 /* Change firmware name via sysfs */
0149 static ssize_t firmware_store(struct device *dev,
0150                   struct device_attribute *attr,
0151                   const char *buf, size_t count)
0152 {
0153     struct rproc *rproc = to_rproc(dev);
0154     int err;
0155 
0156     err = rproc_set_firmware(rproc, buf);
0157 
0158     return err ? err : count;
0159 }
0160 static DEVICE_ATTR_RW(firmware);
0161 
0162 /*
0163  * A state-to-string lookup table, for exposing a human readable state
0164  * via sysfs. Always keep in sync with enum rproc_state
0165  */
0166 static const char * const rproc_state_string[] = {
0167     [RPROC_OFFLINE]     = "offline",
0168     [RPROC_SUSPENDED]   = "suspended",
0169     [RPROC_RUNNING]     = "running",
0170     [RPROC_CRASHED]     = "crashed",
0171     [RPROC_DELETED]     = "deleted",
0172     [RPROC_ATTACHED]    = "attached",
0173     [RPROC_DETACHED]    = "detached",
0174     [RPROC_LAST]        = "invalid",
0175 };
0176 
0177 /* Expose the state of the remote processor via sysfs */
0178 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
0179               char *buf)
0180 {
0181     struct rproc *rproc = to_rproc(dev);
0182     unsigned int state;
0183 
0184     state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
0185     return sprintf(buf, "%s\n", rproc_state_string[state]);
0186 }
0187 
0188 /* Change remote processor state via sysfs */
0189 static ssize_t state_store(struct device *dev,
0190                   struct device_attribute *attr,
0191                   const char *buf, size_t count)
0192 {
0193     struct rproc *rproc = to_rproc(dev);
0194     int ret = 0;
0195 
0196     if (sysfs_streq(buf, "start")) {
0197         ret = rproc_boot(rproc);
0198         if (ret)
0199             dev_err(&rproc->dev, "Boot failed: %d\n", ret);
0200     } else if (sysfs_streq(buf, "stop")) {
0201         ret = rproc_shutdown(rproc);
0202     } else if (sysfs_streq(buf, "detach")) {
0203         ret = rproc_detach(rproc);
0204     } else {
0205         dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
0206         ret = -EINVAL;
0207     }
0208     return ret ? ret : count;
0209 }
0210 static DEVICE_ATTR_RW(state);
0211 
0212 /* Expose the name of the remote processor via sysfs */
0213 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
0214              char *buf)
0215 {
0216     struct rproc *rproc = to_rproc(dev);
0217 
0218     return sprintf(buf, "%s\n", rproc->name);
0219 }
0220 static DEVICE_ATTR_RO(name);
0221 
0222 static umode_t rproc_is_visible(struct kobject *kobj, struct attribute *attr,
0223                 int n)
0224 {
0225     struct device *dev = kobj_to_dev(kobj);
0226     struct rproc *rproc = to_rproc(dev);
0227     umode_t mode = attr->mode;
0228 
0229     if (rproc->sysfs_read_only && (attr == &dev_attr_recovery.attr ||
0230                        attr == &dev_attr_firmware.attr ||
0231                        attr == &dev_attr_state.attr ||
0232                        attr == &dev_attr_coredump.attr))
0233         mode = 0444;
0234 
0235     return mode;
0236 }
0237 
0238 static struct attribute *rproc_attrs[] = {
0239     &dev_attr_coredump.attr,
0240     &dev_attr_recovery.attr,
0241     &dev_attr_firmware.attr,
0242     &dev_attr_state.attr,
0243     &dev_attr_name.attr,
0244     NULL
0245 };
0246 
0247 static const struct attribute_group rproc_devgroup = {
0248     .attrs = rproc_attrs,
0249     .is_visible = rproc_is_visible,
0250 };
0251 
0252 static const struct attribute_group *rproc_devgroups[] = {
0253     &rproc_devgroup,
0254     NULL
0255 };
0256 
0257 struct class rproc_class = {
0258     .name       = "remoteproc",
0259     .dev_groups = rproc_devgroups,
0260 };
0261 
0262 int __init rproc_init_sysfs(void)
0263 {
0264     /* create remoteproc device class for sysfs */
0265     int err = class_register(&rproc_class);
0266 
0267     if (err)
0268         pr_err("remoteproc: unable to register class\n");
0269     return err;
0270 }
0271 
0272 void __exit rproc_exit_sysfs(void)
0273 {
0274     class_unregister(&rproc_class);
0275 }