11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 22aefbef0SMatt Redfearn /* 32aefbef0SMatt Redfearn * Remote Processor Framework 42aefbef0SMatt Redfearn */ 52aefbef0SMatt Redfearn 62aefbef0SMatt Redfearn #include <linux/remoteproc.h> 7bf89a7c0SMichael S. Tsirkin #include <linux/slab.h> 82aefbef0SMatt Redfearn 92aefbef0SMatt Redfearn #include "remoteproc_internal.h" 102aefbef0SMatt Redfearn 112aefbef0SMatt Redfearn #define to_rproc(d) container_of(d, struct rproc, dev) 122aefbef0SMatt Redfearn 13*f75c6043SRishabh Bhatnagar /* 14*f75c6043SRishabh Bhatnagar * A coredump-configuration-to-string lookup table, for exposing a 15*f75c6043SRishabh Bhatnagar * human readable configuration via sysfs. Always keep in sync with 16*f75c6043SRishabh Bhatnagar * enum rproc_coredump_mechanism 17*f75c6043SRishabh Bhatnagar */ 18*f75c6043SRishabh Bhatnagar static const char * const rproc_coredump_str[] = { 19*f75c6043SRishabh Bhatnagar [RPROC_COREDUMP_DISABLED] = "disabled", 20*f75c6043SRishabh Bhatnagar [RPROC_COREDUMP_ENABLED] = "enabled", 21*f75c6043SRishabh Bhatnagar [RPROC_COREDUMP_INLINE] = "inline", 22*f75c6043SRishabh Bhatnagar }; 23*f75c6043SRishabh Bhatnagar 24*f75c6043SRishabh Bhatnagar /* Expose the current coredump configuration via debugfs */ 25*f75c6043SRishabh Bhatnagar static ssize_t coredump_show(struct device *dev, 26*f75c6043SRishabh Bhatnagar struct device_attribute *attr, char *buf) 27*f75c6043SRishabh Bhatnagar { 28*f75c6043SRishabh Bhatnagar struct rproc *rproc = to_rproc(dev); 29*f75c6043SRishabh Bhatnagar 30*f75c6043SRishabh Bhatnagar return sprintf(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]); 31*f75c6043SRishabh Bhatnagar } 32*f75c6043SRishabh Bhatnagar 33*f75c6043SRishabh Bhatnagar /* 34*f75c6043SRishabh Bhatnagar * By writing to the 'coredump' sysfs entry, we control the behavior of the 35*f75c6043SRishabh Bhatnagar * coredump mechanism dynamically. The default value of this entry is "default". 36*f75c6043SRishabh Bhatnagar * 37*f75c6043SRishabh Bhatnagar * The 'coredump' sysfs entry supports these commands: 38*f75c6043SRishabh Bhatnagar * 39*f75c6043SRishabh Bhatnagar * disabled: This is the default coredump mechanism. Recovery will proceed 40*f75c6043SRishabh Bhatnagar * without collecting any dump. 41*f75c6043SRishabh Bhatnagar * 42*f75c6043SRishabh Bhatnagar * default: When the remoteproc crashes the entire coredump will be 43*f75c6043SRishabh Bhatnagar * copied to a separate buffer and exposed to userspace. 44*f75c6043SRishabh Bhatnagar * 45*f75c6043SRishabh Bhatnagar * inline: The coredump will not be copied to a separate buffer and the 46*f75c6043SRishabh Bhatnagar * recovery process will have to wait until data is read by 47*f75c6043SRishabh Bhatnagar * userspace. But this avoid usage of extra memory. 48*f75c6043SRishabh Bhatnagar */ 49*f75c6043SRishabh Bhatnagar static ssize_t coredump_store(struct device *dev, 50*f75c6043SRishabh Bhatnagar struct device_attribute *attr, 51*f75c6043SRishabh Bhatnagar const char *buf, size_t count) 52*f75c6043SRishabh Bhatnagar { 53*f75c6043SRishabh Bhatnagar struct rproc *rproc = to_rproc(dev); 54*f75c6043SRishabh Bhatnagar 55*f75c6043SRishabh Bhatnagar if (rproc->state == RPROC_CRASHED) { 56*f75c6043SRishabh Bhatnagar dev_err(&rproc->dev, "can't change coredump configuration\n"); 57*f75c6043SRishabh Bhatnagar return -EBUSY; 58*f75c6043SRishabh Bhatnagar } 59*f75c6043SRishabh Bhatnagar 60*f75c6043SRishabh Bhatnagar if (sysfs_streq(buf, "disabled")) { 61*f75c6043SRishabh Bhatnagar rproc->dump_conf = RPROC_COREDUMP_DISABLED; 62*f75c6043SRishabh Bhatnagar } else if (sysfs_streq(buf, "enabled")) { 63*f75c6043SRishabh Bhatnagar rproc->dump_conf = RPROC_COREDUMP_ENABLED; 64*f75c6043SRishabh Bhatnagar } else if (sysfs_streq(buf, "inline")) { 65*f75c6043SRishabh Bhatnagar rproc->dump_conf = RPROC_COREDUMP_INLINE; 66*f75c6043SRishabh Bhatnagar } else { 67*f75c6043SRishabh Bhatnagar dev_err(&rproc->dev, "Invalid coredump configuration\n"); 68*f75c6043SRishabh Bhatnagar return -EINVAL; 69*f75c6043SRishabh Bhatnagar } 70*f75c6043SRishabh Bhatnagar 71*f75c6043SRishabh Bhatnagar return count; 72*f75c6043SRishabh Bhatnagar } 73*f75c6043SRishabh Bhatnagar static DEVICE_ATTR_RW(coredump); 74*f75c6043SRishabh Bhatnagar 752aefbef0SMatt Redfearn /* Expose the loaded / running firmware name via sysfs */ 762aefbef0SMatt Redfearn static ssize_t firmware_show(struct device *dev, struct device_attribute *attr, 772aefbef0SMatt Redfearn char *buf) 782aefbef0SMatt Redfearn { 792aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 804a4dca19SMathieu Poirier const char *firmware = rproc->firmware; 812aefbef0SMatt Redfearn 824a4dca19SMathieu Poirier /* 834a4dca19SMathieu Poirier * If the remote processor has been started by an external 844a4dca19SMathieu Poirier * entity we have no idea of what image it is running. As such 854a4dca19SMathieu Poirier * simply display a generic string rather then rproc->firmware. 864a4dca19SMathieu Poirier * 874a4dca19SMathieu Poirier * Here we rely on the autonomous flag because a remote processor 884a4dca19SMathieu Poirier * may have been attached to and currently in a running state. 894a4dca19SMathieu Poirier */ 904a4dca19SMathieu Poirier if (rproc->autonomous) 914a4dca19SMathieu Poirier firmware = "unknown"; 924a4dca19SMathieu Poirier 934a4dca19SMathieu Poirier return sprintf(buf, "%s\n", firmware); 942aefbef0SMatt Redfearn } 952aefbef0SMatt Redfearn 962aefbef0SMatt Redfearn /* Change firmware name via sysfs */ 972aefbef0SMatt Redfearn static ssize_t firmware_store(struct device *dev, 982aefbef0SMatt Redfearn struct device_attribute *attr, 992aefbef0SMatt Redfearn const char *buf, size_t count) 1002aefbef0SMatt Redfearn { 1012aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 1022aefbef0SMatt Redfearn char *p; 1032aefbef0SMatt Redfearn int err, len = count; 1042aefbef0SMatt Redfearn 1052aefbef0SMatt Redfearn err = mutex_lock_interruptible(&rproc->lock); 1062aefbef0SMatt Redfearn if (err) { 1072aefbef0SMatt Redfearn dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, err); 1082aefbef0SMatt Redfearn return -EINVAL; 1092aefbef0SMatt Redfearn } 1102aefbef0SMatt Redfearn 1112aefbef0SMatt Redfearn if (rproc->state != RPROC_OFFLINE) { 1122aefbef0SMatt Redfearn dev_err(dev, "can't change firmware while running\n"); 1132aefbef0SMatt Redfearn err = -EBUSY; 1142aefbef0SMatt Redfearn goto out; 1152aefbef0SMatt Redfearn } 1162aefbef0SMatt Redfearn 1172aefbef0SMatt Redfearn len = strcspn(buf, "\n"); 118faeadbb6SSuman Anna if (!len) { 119faeadbb6SSuman Anna dev_err(dev, "can't provide a NULL firmware\n"); 120faeadbb6SSuman Anna err = -EINVAL; 121faeadbb6SSuman Anna goto out; 122faeadbb6SSuman Anna } 1232aefbef0SMatt Redfearn 1242aefbef0SMatt Redfearn p = kstrndup(buf, len, GFP_KERNEL); 1252aefbef0SMatt Redfearn if (!p) { 1262aefbef0SMatt Redfearn err = -ENOMEM; 1272aefbef0SMatt Redfearn goto out; 1282aefbef0SMatt Redfearn } 1292aefbef0SMatt Redfearn 1302aefbef0SMatt Redfearn kfree(rproc->firmware); 1312aefbef0SMatt Redfearn rproc->firmware = p; 1322aefbef0SMatt Redfearn out: 1332aefbef0SMatt Redfearn mutex_unlock(&rproc->lock); 1342aefbef0SMatt Redfearn 1352aefbef0SMatt Redfearn return err ? err : count; 1362aefbef0SMatt Redfearn } 1372aefbef0SMatt Redfearn static DEVICE_ATTR_RW(firmware); 1382aefbef0SMatt Redfearn 1392aefbef0SMatt Redfearn /* 1402aefbef0SMatt Redfearn * A state-to-string lookup table, for exposing a human readable state 1412aefbef0SMatt Redfearn * via sysfs. Always keep in sync with enum rproc_state 1422aefbef0SMatt Redfearn */ 1432aefbef0SMatt Redfearn static const char * const rproc_state_string[] = { 1442aefbef0SMatt Redfearn [RPROC_OFFLINE] = "offline", 1452aefbef0SMatt Redfearn [RPROC_SUSPENDED] = "suspended", 1462aefbef0SMatt Redfearn [RPROC_RUNNING] = "running", 1472aefbef0SMatt Redfearn [RPROC_CRASHED] = "crashed", 148608d7921SSarangdhar Joshi [RPROC_DELETED] = "deleted", 149e2e5c55eSMathieu Poirier [RPROC_DETACHED] = "detached", 1502aefbef0SMatt Redfearn [RPROC_LAST] = "invalid", 1512aefbef0SMatt Redfearn }; 1522aefbef0SMatt Redfearn 1532aefbef0SMatt Redfearn /* Expose the state of the remote processor via sysfs */ 1542aefbef0SMatt Redfearn static ssize_t state_show(struct device *dev, struct device_attribute *attr, 1552aefbef0SMatt Redfearn char *buf) 1562aefbef0SMatt Redfearn { 1572aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 1582aefbef0SMatt Redfearn unsigned int state; 1592aefbef0SMatt Redfearn 1602aefbef0SMatt Redfearn state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state; 1612aefbef0SMatt Redfearn return sprintf(buf, "%s\n", rproc_state_string[state]); 1622aefbef0SMatt Redfearn } 1632aefbef0SMatt Redfearn 1642aefbef0SMatt Redfearn /* Change remote processor state via sysfs */ 1652aefbef0SMatt Redfearn static ssize_t state_store(struct device *dev, 1662aefbef0SMatt Redfearn struct device_attribute *attr, 1672aefbef0SMatt Redfearn const char *buf, size_t count) 1682aefbef0SMatt Redfearn { 1692aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 1702aefbef0SMatt Redfearn int ret = 0; 1712aefbef0SMatt Redfearn 1722aefbef0SMatt Redfearn if (sysfs_streq(buf, "start")) { 1732aefbef0SMatt Redfearn if (rproc->state == RPROC_RUNNING) 1742aefbef0SMatt Redfearn return -EBUSY; 1752aefbef0SMatt Redfearn 1762aefbef0SMatt Redfearn ret = rproc_boot(rproc); 1772aefbef0SMatt Redfearn if (ret) 1782aefbef0SMatt Redfearn dev_err(&rproc->dev, "Boot failed: %d\n", ret); 1792aefbef0SMatt Redfearn } else if (sysfs_streq(buf, "stop")) { 1802aefbef0SMatt Redfearn if (rproc->state != RPROC_RUNNING) 1812aefbef0SMatt Redfearn return -EINVAL; 1822aefbef0SMatt Redfearn 1832aefbef0SMatt Redfearn rproc_shutdown(rproc); 1842aefbef0SMatt Redfearn } else { 1852aefbef0SMatt Redfearn dev_err(&rproc->dev, "Unrecognised option: %s\n", buf); 1862aefbef0SMatt Redfearn ret = -EINVAL; 1872aefbef0SMatt Redfearn } 1882aefbef0SMatt Redfearn return ret ? ret : count; 1892aefbef0SMatt Redfearn } 1902aefbef0SMatt Redfearn static DEVICE_ATTR_RW(state); 1912aefbef0SMatt Redfearn 1926ed756aaSSuman Anna /* Expose the name of the remote processor via sysfs */ 1936ed756aaSSuman Anna static ssize_t name_show(struct device *dev, struct device_attribute *attr, 1946ed756aaSSuman Anna char *buf) 1956ed756aaSSuman Anna { 1966ed756aaSSuman Anna struct rproc *rproc = to_rproc(dev); 1976ed756aaSSuman Anna 1986ed756aaSSuman Anna return sprintf(buf, "%s\n", rproc->name); 1996ed756aaSSuman Anna } 2006ed756aaSSuman Anna static DEVICE_ATTR_RO(name); 2016ed756aaSSuman Anna 2022aefbef0SMatt Redfearn static struct attribute *rproc_attrs[] = { 203*f75c6043SRishabh Bhatnagar &dev_attr_coredump.attr, 2042aefbef0SMatt Redfearn &dev_attr_firmware.attr, 2052aefbef0SMatt Redfearn &dev_attr_state.attr, 2066ed756aaSSuman Anna &dev_attr_name.attr, 2072aefbef0SMatt Redfearn NULL 2082aefbef0SMatt Redfearn }; 2092aefbef0SMatt Redfearn 2102aefbef0SMatt Redfearn static const struct attribute_group rproc_devgroup = { 2112aefbef0SMatt Redfearn .attrs = rproc_attrs 2122aefbef0SMatt Redfearn }; 2132aefbef0SMatt Redfearn 2142aefbef0SMatt Redfearn static const struct attribute_group *rproc_devgroups[] = { 2152aefbef0SMatt Redfearn &rproc_devgroup, 2162aefbef0SMatt Redfearn NULL 2172aefbef0SMatt Redfearn }; 2182aefbef0SMatt Redfearn 2192aefbef0SMatt Redfearn struct class rproc_class = { 2202aefbef0SMatt Redfearn .name = "remoteproc", 2212aefbef0SMatt Redfearn .dev_groups = rproc_devgroups, 2222aefbef0SMatt Redfearn }; 2232aefbef0SMatt Redfearn 2242aefbef0SMatt Redfearn int __init rproc_init_sysfs(void) 2252aefbef0SMatt Redfearn { 2262aefbef0SMatt Redfearn /* create remoteproc device class for sysfs */ 2272aefbef0SMatt Redfearn int err = class_register(&rproc_class); 2282aefbef0SMatt Redfearn 2292aefbef0SMatt Redfearn if (err) 2302aefbef0SMatt Redfearn pr_err("remoteproc: unable to register class\n"); 2312aefbef0SMatt Redfearn return err; 2322aefbef0SMatt Redfearn } 2332aefbef0SMatt Redfearn 2342aefbef0SMatt Redfearn void __exit rproc_exit_sysfs(void) 2352aefbef0SMatt Redfearn { 2362aefbef0SMatt Redfearn class_unregister(&rproc_class); 2372aefbef0SMatt Redfearn } 238