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 18145e1da3SRaghavendra 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 85145e1da3SRaghavendra 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 */ 14276f4c875SMathieu Poirier if (rproc->state == RPROC_ATTACHED) 1434a4dca19SMathieu Poirier firmware = "unknown"; 1444a4dca19SMathieu Poirier 1454a4dca19SMathieu Poirier return sprintf(buf, "%s\n", firmware); 1462aefbef0SMatt Redfearn } 1472aefbef0SMatt Redfearn 1482aefbef0SMatt Redfearn /* Change firmware name via sysfs */ 1492aefbef0SMatt Redfearn static ssize_t firmware_store(struct device *dev, 1502aefbef0SMatt Redfearn struct device_attribute *attr, 1512aefbef0SMatt Redfearn const char *buf, size_t count) 1522aefbef0SMatt Redfearn { 1532aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 1544c1ad562SSuman Anna int err; 1552aefbef0SMatt Redfearn 1564c1ad562SSuman Anna err = rproc_set_firmware(rproc, buf); 1572aefbef0SMatt Redfearn 1582aefbef0SMatt Redfearn return err ? err : count; 1592aefbef0SMatt Redfearn } 1602aefbef0SMatt Redfearn static DEVICE_ATTR_RW(firmware); 1612aefbef0SMatt Redfearn 1622aefbef0SMatt Redfearn /* 1632aefbef0SMatt Redfearn * A state-to-string lookup table, for exposing a human readable state 1642aefbef0SMatt Redfearn * via sysfs. Always keep in sync with enum rproc_state 1652aefbef0SMatt Redfearn */ 1662aefbef0SMatt Redfearn static const char * const rproc_state_string[] = { 1672aefbef0SMatt Redfearn [RPROC_OFFLINE] = "offline", 1682aefbef0SMatt Redfearn [RPROC_SUSPENDED] = "suspended", 1692aefbef0SMatt Redfearn [RPROC_RUNNING] = "running", 1702aefbef0SMatt Redfearn [RPROC_CRASHED] = "crashed", 171608d7921SSarangdhar Joshi [RPROC_DELETED] = "deleted", 1724196d189SMathieu Poirier [RPROC_ATTACHED] = "attached", 173e2e5c55eSMathieu Poirier [RPROC_DETACHED] = "detached", 1742aefbef0SMatt Redfearn [RPROC_LAST] = "invalid", 1752aefbef0SMatt Redfearn }; 1762aefbef0SMatt Redfearn 1772aefbef0SMatt Redfearn /* Expose the state of the remote processor via sysfs */ 1782aefbef0SMatt Redfearn static ssize_t state_show(struct device *dev, struct device_attribute *attr, 1792aefbef0SMatt Redfearn char *buf) 1802aefbef0SMatt Redfearn { 1812aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 1822aefbef0SMatt Redfearn unsigned int state; 1832aefbef0SMatt Redfearn 1842aefbef0SMatt Redfearn state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state; 1852aefbef0SMatt Redfearn return sprintf(buf, "%s\n", rproc_state_string[state]); 1862aefbef0SMatt Redfearn } 1872aefbef0SMatt Redfearn 1882aefbef0SMatt Redfearn /* Change remote processor state via sysfs */ 1892aefbef0SMatt Redfearn static ssize_t state_store(struct device *dev, 1902aefbef0SMatt Redfearn struct device_attribute *attr, 1912aefbef0SMatt Redfearn const char *buf, size_t count) 1922aefbef0SMatt Redfearn { 1932aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 1942aefbef0SMatt Redfearn int ret = 0; 1952aefbef0SMatt Redfearn 1962aefbef0SMatt Redfearn if (sysfs_streq(buf, "start")) { 19783d4e671SMathieu Poirier if (rproc->state == RPROC_RUNNING || 19883d4e671SMathieu Poirier rproc->state == RPROC_ATTACHED) 1992aefbef0SMatt Redfearn return -EBUSY; 2002aefbef0SMatt Redfearn 2012aefbef0SMatt Redfearn ret = rproc_boot(rproc); 2022aefbef0SMatt Redfearn if (ret) 2032aefbef0SMatt Redfearn dev_err(&rproc->dev, "Boot failed: %d\n", ret); 2042aefbef0SMatt Redfearn } else if (sysfs_streq(buf, "stop")) { 205d2008a96SMathieu Poirier if (rproc->state != RPROC_RUNNING && 206d2008a96SMathieu Poirier rproc->state != RPROC_ATTACHED) 2072aefbef0SMatt Redfearn return -EINVAL; 2082aefbef0SMatt Redfearn 2092aefbef0SMatt Redfearn rproc_shutdown(rproc); 2105daaeb5fSMathieu Poirier } else if (sysfs_streq(buf, "detach")) { 2115daaeb5fSMathieu Poirier if (rproc->state != RPROC_ATTACHED) 2125daaeb5fSMathieu Poirier return -EINVAL; 2135daaeb5fSMathieu Poirier 2145daaeb5fSMathieu Poirier ret = rproc_detach(rproc); 2152aefbef0SMatt Redfearn } else { 2162aefbef0SMatt Redfearn dev_err(&rproc->dev, "Unrecognised option: %s\n", buf); 2172aefbef0SMatt Redfearn ret = -EINVAL; 2182aefbef0SMatt Redfearn } 2192aefbef0SMatt Redfearn return ret ? ret : count; 2202aefbef0SMatt Redfearn } 2212aefbef0SMatt Redfearn static DEVICE_ATTR_RW(state); 2222aefbef0SMatt Redfearn 2236ed756aaSSuman Anna /* Expose the name of the remote processor via sysfs */ 2246ed756aaSSuman Anna static ssize_t name_show(struct device *dev, struct device_attribute *attr, 2256ed756aaSSuman Anna char *buf) 2266ed756aaSSuman Anna { 2276ed756aaSSuman Anna struct rproc *rproc = to_rproc(dev); 2286ed756aaSSuman Anna 2296ed756aaSSuman Anna return sprintf(buf, "%s\n", rproc->name); 2306ed756aaSSuman Anna } 2316ed756aaSSuman Anna static DEVICE_ATTR_RO(name); 2326ed756aaSSuman Anna 233*26c9da51SPuranjay Mohan static umode_t rproc_is_visible(struct kobject *kobj, struct attribute *attr, 234*26c9da51SPuranjay Mohan int n) 235*26c9da51SPuranjay Mohan { 236*26c9da51SPuranjay Mohan struct device *dev = kobj_to_dev(kobj); 237*26c9da51SPuranjay Mohan struct rproc *rproc = to_rproc(dev); 238*26c9da51SPuranjay Mohan umode_t mode = attr->mode; 239*26c9da51SPuranjay Mohan 240*26c9da51SPuranjay Mohan if (rproc->sysfs_read_only && (attr == &dev_attr_recovery.attr || 241*26c9da51SPuranjay Mohan attr == &dev_attr_firmware.attr || 242*26c9da51SPuranjay Mohan attr == &dev_attr_state.attr || 243*26c9da51SPuranjay Mohan attr == &dev_attr_coredump.attr)) 244*26c9da51SPuranjay Mohan mode = 0444; 245*26c9da51SPuranjay Mohan 246*26c9da51SPuranjay Mohan return mode; 247*26c9da51SPuranjay Mohan } 248*26c9da51SPuranjay Mohan 2492aefbef0SMatt Redfearn static struct attribute *rproc_attrs[] = { 250f75c6043SRishabh Bhatnagar &dev_attr_coredump.attr, 251526b9e0cSRishabh Bhatnagar &dev_attr_recovery.attr, 2522aefbef0SMatt Redfearn &dev_attr_firmware.attr, 2532aefbef0SMatt Redfearn &dev_attr_state.attr, 2546ed756aaSSuman Anna &dev_attr_name.attr, 2552aefbef0SMatt Redfearn NULL 2562aefbef0SMatt Redfearn }; 2572aefbef0SMatt Redfearn 2582aefbef0SMatt Redfearn static const struct attribute_group rproc_devgroup = { 259*26c9da51SPuranjay Mohan .attrs = rproc_attrs, 260*26c9da51SPuranjay Mohan .is_visible = rproc_is_visible, 2612aefbef0SMatt Redfearn }; 2622aefbef0SMatt Redfearn 2632aefbef0SMatt Redfearn static const struct attribute_group *rproc_devgroups[] = { 2642aefbef0SMatt Redfearn &rproc_devgroup, 2652aefbef0SMatt Redfearn NULL 2662aefbef0SMatt Redfearn }; 2672aefbef0SMatt Redfearn 2682aefbef0SMatt Redfearn struct class rproc_class = { 2692aefbef0SMatt Redfearn .name = "remoteproc", 2702aefbef0SMatt Redfearn .dev_groups = rproc_devgroups, 2712aefbef0SMatt Redfearn }; 2722aefbef0SMatt Redfearn 2732aefbef0SMatt Redfearn int __init rproc_init_sysfs(void) 2742aefbef0SMatt Redfearn { 2752aefbef0SMatt Redfearn /* create remoteproc device class for sysfs */ 2762aefbef0SMatt Redfearn int err = class_register(&rproc_class); 2772aefbef0SMatt Redfearn 2782aefbef0SMatt Redfearn if (err) 2792aefbef0SMatt Redfearn pr_err("remoteproc: unable to register class\n"); 2802aefbef0SMatt Redfearn return err; 2812aefbef0SMatt Redfearn } 2822aefbef0SMatt Redfearn 2832aefbef0SMatt Redfearn void __exit rproc_exit_sysfs(void) 2842aefbef0SMatt Redfearn { 2852aefbef0SMatt Redfearn class_unregister(&rproc_class); 2862aefbef0SMatt Redfearn } 287