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 13526b9e0cSRishabh Bhatnagar static ssize_t recovery_show(struct device *dev, 14526b9e0cSRishabh Bhatnagar struct device_attribute *attr, char *buf) 15526b9e0cSRishabh Bhatnagar { 16526b9e0cSRishabh Bhatnagar struct rproc *rproc = to_rproc(dev); 17526b9e0cSRishabh Bhatnagar 18*145e1da3SRaghavendra Rao Ananta return sysfs_emit(buf, "%s", rproc->recovery_disabled ? "disabled\n" : "enabled\n"); 19526b9e0cSRishabh Bhatnagar } 20526b9e0cSRishabh Bhatnagar 21526b9e0cSRishabh Bhatnagar /* 22526b9e0cSRishabh Bhatnagar * By writing to the 'recovery' sysfs entry, we control the behavior of the 23526b9e0cSRishabh Bhatnagar * recovery mechanism dynamically. The default value of this entry is "enabled". 24526b9e0cSRishabh Bhatnagar * 25526b9e0cSRishabh Bhatnagar * The 'recovery' sysfs entry supports these commands: 26526b9e0cSRishabh Bhatnagar * 27526b9e0cSRishabh Bhatnagar * enabled: When enabled, the remote processor will be automatically 28526b9e0cSRishabh Bhatnagar * recovered whenever it crashes. Moreover, if the remote 29526b9e0cSRishabh Bhatnagar * processor crashes while recovery is disabled, it will 30526b9e0cSRishabh Bhatnagar * be automatically recovered too as soon as recovery is enabled. 31526b9e0cSRishabh Bhatnagar * 32526b9e0cSRishabh Bhatnagar * disabled: When disabled, a remote processor will remain in a crashed 33526b9e0cSRishabh Bhatnagar * state if it crashes. This is useful for debugging purposes; 34526b9e0cSRishabh Bhatnagar * without it, debugging a crash is substantially harder. 35526b9e0cSRishabh Bhatnagar * 36526b9e0cSRishabh Bhatnagar * recover: This function will trigger an immediate recovery if the 37526b9e0cSRishabh Bhatnagar * remote processor is in a crashed state, without changing 38526b9e0cSRishabh Bhatnagar * or checking the recovery state (enabled/disabled). 39526b9e0cSRishabh Bhatnagar * This is useful during debugging sessions, when one expects 40526b9e0cSRishabh Bhatnagar * additional crashes to happen after enabling recovery. In this 41526b9e0cSRishabh Bhatnagar * case, enabling recovery will make it hard to debug subsequent 42526b9e0cSRishabh Bhatnagar * crashes, so it's recommended to keep recovery disabled, and 43526b9e0cSRishabh Bhatnagar * instead use the "recover" command as needed. 44526b9e0cSRishabh Bhatnagar */ 45526b9e0cSRishabh Bhatnagar static ssize_t recovery_store(struct device *dev, 46526b9e0cSRishabh Bhatnagar struct device_attribute *attr, 47526b9e0cSRishabh Bhatnagar const char *buf, size_t count) 48526b9e0cSRishabh Bhatnagar { 49526b9e0cSRishabh Bhatnagar struct rproc *rproc = to_rproc(dev); 50526b9e0cSRishabh Bhatnagar 51526b9e0cSRishabh Bhatnagar if (sysfs_streq(buf, "enabled")) { 52526b9e0cSRishabh Bhatnagar /* change the flag and begin the recovery process if needed */ 53526b9e0cSRishabh Bhatnagar rproc->recovery_disabled = false; 54526b9e0cSRishabh Bhatnagar rproc_trigger_recovery(rproc); 55526b9e0cSRishabh Bhatnagar } else if (sysfs_streq(buf, "disabled")) { 56526b9e0cSRishabh Bhatnagar rproc->recovery_disabled = true; 57526b9e0cSRishabh Bhatnagar } else if (sysfs_streq(buf, "recover")) { 58526b9e0cSRishabh Bhatnagar /* begin the recovery process without changing the flag */ 59526b9e0cSRishabh Bhatnagar rproc_trigger_recovery(rproc); 60526b9e0cSRishabh Bhatnagar } else { 61526b9e0cSRishabh Bhatnagar return -EINVAL; 62526b9e0cSRishabh Bhatnagar } 63526b9e0cSRishabh Bhatnagar 64526b9e0cSRishabh Bhatnagar return count; 65526b9e0cSRishabh Bhatnagar } 66526b9e0cSRishabh Bhatnagar static DEVICE_ATTR_RW(recovery); 67526b9e0cSRishabh Bhatnagar 68f75c6043SRishabh Bhatnagar /* 69f75c6043SRishabh Bhatnagar * A coredump-configuration-to-string lookup table, for exposing a 70f75c6043SRishabh Bhatnagar * human readable configuration via sysfs. Always keep in sync with 71f75c6043SRishabh Bhatnagar * enum rproc_coredump_mechanism 72f75c6043SRishabh Bhatnagar */ 73f75c6043SRishabh Bhatnagar static const char * const rproc_coredump_str[] = { 74f75c6043SRishabh Bhatnagar [RPROC_COREDUMP_DISABLED] = "disabled", 75f75c6043SRishabh Bhatnagar [RPROC_COREDUMP_ENABLED] = "enabled", 76f75c6043SRishabh Bhatnagar [RPROC_COREDUMP_INLINE] = "inline", 77f75c6043SRishabh Bhatnagar }; 78f75c6043SRishabh Bhatnagar 79f75c6043SRishabh Bhatnagar /* Expose the current coredump configuration via debugfs */ 80f75c6043SRishabh Bhatnagar static ssize_t coredump_show(struct device *dev, 81f75c6043SRishabh Bhatnagar struct device_attribute *attr, char *buf) 82f75c6043SRishabh Bhatnagar { 83f75c6043SRishabh Bhatnagar struct rproc *rproc = to_rproc(dev); 84f75c6043SRishabh Bhatnagar 85*145e1da3SRaghavendra Rao Ananta return sysfs_emit(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]); 86f75c6043SRishabh Bhatnagar } 87f75c6043SRishabh Bhatnagar 88f75c6043SRishabh Bhatnagar /* 89f75c6043SRishabh Bhatnagar * By writing to the 'coredump' sysfs entry, we control the behavior of the 90f75c6043SRishabh Bhatnagar * coredump mechanism dynamically. The default value of this entry is "default". 91f75c6043SRishabh Bhatnagar * 92f75c6043SRishabh Bhatnagar * The 'coredump' sysfs entry supports these commands: 93f75c6043SRishabh Bhatnagar * 94f75c6043SRishabh Bhatnagar * disabled: This is the default coredump mechanism. Recovery will proceed 95f75c6043SRishabh Bhatnagar * without collecting any dump. 96f75c6043SRishabh Bhatnagar * 97f75c6043SRishabh Bhatnagar * default: When the remoteproc crashes the entire coredump will be 98f75c6043SRishabh Bhatnagar * copied to a separate buffer and exposed to userspace. 99f75c6043SRishabh Bhatnagar * 100f75c6043SRishabh Bhatnagar * inline: The coredump will not be copied to a separate buffer and the 101f75c6043SRishabh Bhatnagar * recovery process will have to wait until data is read by 102f75c6043SRishabh Bhatnagar * userspace. But this avoid usage of extra memory. 103f75c6043SRishabh Bhatnagar */ 104f75c6043SRishabh Bhatnagar static ssize_t coredump_store(struct device *dev, 105f75c6043SRishabh Bhatnagar struct device_attribute *attr, 106f75c6043SRishabh Bhatnagar const char *buf, size_t count) 107f75c6043SRishabh Bhatnagar { 108f75c6043SRishabh Bhatnagar struct rproc *rproc = to_rproc(dev); 109f75c6043SRishabh Bhatnagar 110f75c6043SRishabh Bhatnagar if (rproc->state == RPROC_CRASHED) { 111f75c6043SRishabh Bhatnagar dev_err(&rproc->dev, "can't change coredump configuration\n"); 112f75c6043SRishabh Bhatnagar return -EBUSY; 113f75c6043SRishabh Bhatnagar } 114f75c6043SRishabh Bhatnagar 115f75c6043SRishabh Bhatnagar if (sysfs_streq(buf, "disabled")) { 116f75c6043SRishabh Bhatnagar rproc->dump_conf = RPROC_COREDUMP_DISABLED; 117f75c6043SRishabh Bhatnagar } else if (sysfs_streq(buf, "enabled")) { 118f75c6043SRishabh Bhatnagar rproc->dump_conf = RPROC_COREDUMP_ENABLED; 119f75c6043SRishabh Bhatnagar } else if (sysfs_streq(buf, "inline")) { 120f75c6043SRishabh Bhatnagar rproc->dump_conf = RPROC_COREDUMP_INLINE; 121f75c6043SRishabh Bhatnagar } else { 122f75c6043SRishabh Bhatnagar dev_err(&rproc->dev, "Invalid coredump configuration\n"); 123f75c6043SRishabh Bhatnagar return -EINVAL; 124f75c6043SRishabh Bhatnagar } 125f75c6043SRishabh Bhatnagar 126f75c6043SRishabh Bhatnagar return count; 127f75c6043SRishabh Bhatnagar } 128f75c6043SRishabh Bhatnagar static DEVICE_ATTR_RW(coredump); 129f75c6043SRishabh Bhatnagar 1302aefbef0SMatt Redfearn /* Expose the loaded / running firmware name via sysfs */ 1312aefbef0SMatt Redfearn static ssize_t firmware_show(struct device *dev, struct device_attribute *attr, 1322aefbef0SMatt Redfearn char *buf) 1332aefbef0SMatt Redfearn { 1342aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 1354a4dca19SMathieu Poirier const char *firmware = rproc->firmware; 1362aefbef0SMatt Redfearn 1374a4dca19SMathieu Poirier /* 1384a4dca19SMathieu Poirier * If the remote processor has been started by an external 1394a4dca19SMathieu Poirier * entity we have no idea of what image it is running. As such 1404a4dca19SMathieu Poirier * simply display a generic string rather then rproc->firmware. 1414a4dca19SMathieu Poirier * 1424a4dca19SMathieu Poirier * Here we rely on the autonomous flag because a remote processor 1434a4dca19SMathieu Poirier * may have been attached to and currently in a running state. 1444a4dca19SMathieu Poirier */ 1454a4dca19SMathieu Poirier if (rproc->autonomous) 1464a4dca19SMathieu Poirier firmware = "unknown"; 1474a4dca19SMathieu Poirier 1484a4dca19SMathieu Poirier return sprintf(buf, "%s\n", firmware); 1492aefbef0SMatt Redfearn } 1502aefbef0SMatt Redfearn 1512aefbef0SMatt Redfearn /* Change firmware name via sysfs */ 1522aefbef0SMatt Redfearn static ssize_t firmware_store(struct device *dev, 1532aefbef0SMatt Redfearn struct device_attribute *attr, 1542aefbef0SMatt Redfearn const char *buf, size_t count) 1552aefbef0SMatt Redfearn { 1562aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 1574c1ad562SSuman Anna int err; 1582aefbef0SMatt Redfearn 1594c1ad562SSuman Anna err = rproc_set_firmware(rproc, buf); 1602aefbef0SMatt Redfearn 1612aefbef0SMatt Redfearn return err ? err : count; 1622aefbef0SMatt Redfearn } 1632aefbef0SMatt Redfearn static DEVICE_ATTR_RW(firmware); 1642aefbef0SMatt Redfearn 1652aefbef0SMatt Redfearn /* 1662aefbef0SMatt Redfearn * A state-to-string lookup table, for exposing a human readable state 1672aefbef0SMatt Redfearn * via sysfs. Always keep in sync with enum rproc_state 1682aefbef0SMatt Redfearn */ 1692aefbef0SMatt Redfearn static const char * const rproc_state_string[] = { 1702aefbef0SMatt Redfearn [RPROC_OFFLINE] = "offline", 1712aefbef0SMatt Redfearn [RPROC_SUSPENDED] = "suspended", 1722aefbef0SMatt Redfearn [RPROC_RUNNING] = "running", 1732aefbef0SMatt Redfearn [RPROC_CRASHED] = "crashed", 174608d7921SSarangdhar Joshi [RPROC_DELETED] = "deleted", 175e2e5c55eSMathieu Poirier [RPROC_DETACHED] = "detached", 1762aefbef0SMatt Redfearn [RPROC_LAST] = "invalid", 1772aefbef0SMatt Redfearn }; 1782aefbef0SMatt Redfearn 1792aefbef0SMatt Redfearn /* Expose the state of the remote processor via sysfs */ 1802aefbef0SMatt Redfearn static ssize_t state_show(struct device *dev, struct device_attribute *attr, 1812aefbef0SMatt Redfearn char *buf) 1822aefbef0SMatt Redfearn { 1832aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 1842aefbef0SMatt Redfearn unsigned int state; 1852aefbef0SMatt Redfearn 1862aefbef0SMatt Redfearn state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state; 1872aefbef0SMatt Redfearn return sprintf(buf, "%s\n", rproc_state_string[state]); 1882aefbef0SMatt Redfearn } 1892aefbef0SMatt Redfearn 1902aefbef0SMatt Redfearn /* Change remote processor state via sysfs */ 1912aefbef0SMatt Redfearn static ssize_t state_store(struct device *dev, 1922aefbef0SMatt Redfearn struct device_attribute *attr, 1932aefbef0SMatt Redfearn const char *buf, size_t count) 1942aefbef0SMatt Redfearn { 1952aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 1962aefbef0SMatt Redfearn int ret = 0; 1972aefbef0SMatt Redfearn 1982aefbef0SMatt Redfearn if (sysfs_streq(buf, "start")) { 1992aefbef0SMatt Redfearn if (rproc->state == RPROC_RUNNING) 2002aefbef0SMatt Redfearn return -EBUSY; 2012aefbef0SMatt Redfearn 2022aefbef0SMatt Redfearn ret = rproc_boot(rproc); 2032aefbef0SMatt Redfearn if (ret) 2042aefbef0SMatt Redfearn dev_err(&rproc->dev, "Boot failed: %d\n", ret); 2052aefbef0SMatt Redfearn } else if (sysfs_streq(buf, "stop")) { 2062aefbef0SMatt Redfearn if (rproc->state != RPROC_RUNNING) 2072aefbef0SMatt Redfearn return -EINVAL; 2082aefbef0SMatt Redfearn 2092aefbef0SMatt Redfearn rproc_shutdown(rproc); 2102aefbef0SMatt Redfearn } else { 2112aefbef0SMatt Redfearn dev_err(&rproc->dev, "Unrecognised option: %s\n", buf); 2122aefbef0SMatt Redfearn ret = -EINVAL; 2132aefbef0SMatt Redfearn } 2142aefbef0SMatt Redfearn return ret ? ret : count; 2152aefbef0SMatt Redfearn } 2162aefbef0SMatt Redfearn static DEVICE_ATTR_RW(state); 2172aefbef0SMatt Redfearn 2186ed756aaSSuman Anna /* Expose the name of the remote processor via sysfs */ 2196ed756aaSSuman Anna static ssize_t name_show(struct device *dev, struct device_attribute *attr, 2206ed756aaSSuman Anna char *buf) 2216ed756aaSSuman Anna { 2226ed756aaSSuman Anna struct rproc *rproc = to_rproc(dev); 2236ed756aaSSuman Anna 2246ed756aaSSuman Anna return sprintf(buf, "%s\n", rproc->name); 2256ed756aaSSuman Anna } 2266ed756aaSSuman Anna static DEVICE_ATTR_RO(name); 2276ed756aaSSuman Anna 2282aefbef0SMatt Redfearn static struct attribute *rproc_attrs[] = { 229f75c6043SRishabh Bhatnagar &dev_attr_coredump.attr, 230526b9e0cSRishabh Bhatnagar &dev_attr_recovery.attr, 2312aefbef0SMatt Redfearn &dev_attr_firmware.attr, 2322aefbef0SMatt Redfearn &dev_attr_state.attr, 2336ed756aaSSuman Anna &dev_attr_name.attr, 2342aefbef0SMatt Redfearn NULL 2352aefbef0SMatt Redfearn }; 2362aefbef0SMatt Redfearn 2372aefbef0SMatt Redfearn static const struct attribute_group rproc_devgroup = { 2382aefbef0SMatt Redfearn .attrs = rproc_attrs 2392aefbef0SMatt Redfearn }; 2402aefbef0SMatt Redfearn 2412aefbef0SMatt Redfearn static const struct attribute_group *rproc_devgroups[] = { 2422aefbef0SMatt Redfearn &rproc_devgroup, 2432aefbef0SMatt Redfearn NULL 2442aefbef0SMatt Redfearn }; 2452aefbef0SMatt Redfearn 2462aefbef0SMatt Redfearn struct class rproc_class = { 2472aefbef0SMatt Redfearn .name = "remoteproc", 2482aefbef0SMatt Redfearn .dev_groups = rproc_devgroups, 2492aefbef0SMatt Redfearn }; 2502aefbef0SMatt Redfearn 2512aefbef0SMatt Redfearn int __init rproc_init_sysfs(void) 2522aefbef0SMatt Redfearn { 2532aefbef0SMatt Redfearn /* create remoteproc device class for sysfs */ 2542aefbef0SMatt Redfearn int err = class_register(&rproc_class); 2552aefbef0SMatt Redfearn 2562aefbef0SMatt Redfearn if (err) 2572aefbef0SMatt Redfearn pr_err("remoteproc: unable to register class\n"); 2582aefbef0SMatt Redfearn return err; 2592aefbef0SMatt Redfearn } 2602aefbef0SMatt Redfearn 2612aefbef0SMatt Redfearn void __exit rproc_exit_sysfs(void) 2622aefbef0SMatt Redfearn { 2632aefbef0SMatt Redfearn class_unregister(&rproc_class); 2642aefbef0SMatt Redfearn } 265