18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Remote Processor Framework 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/remoteproc.h> 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "remoteproc_internal.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define to_rproc(d) container_of(d, struct rproc, dev) 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic ssize_t recovery_show(struct device *dev, 148c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci struct rproc *rproc = to_rproc(dev); 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci return sprintf(buf, "%s", rproc->recovery_disabled ? "disabled\n" : "enabled\n"); 198c2ecf20Sopenharmony_ci} 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * By writing to the 'recovery' sysfs entry, we control the behavior of the 238c2ecf20Sopenharmony_ci * recovery mechanism dynamically. The default value of this entry is "enabled". 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * The 'recovery' sysfs entry supports these commands: 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * enabled: When enabled, the remote processor will be automatically 288c2ecf20Sopenharmony_ci * recovered whenever it crashes. Moreover, if the remote 298c2ecf20Sopenharmony_ci * processor crashes while recovery is disabled, it will 308c2ecf20Sopenharmony_ci * be automatically recovered too as soon as recovery is enabled. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * disabled: When disabled, a remote processor will remain in a crashed 338c2ecf20Sopenharmony_ci * state if it crashes. This is useful for debugging purposes; 348c2ecf20Sopenharmony_ci * without it, debugging a crash is substantially harder. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * recover: This function will trigger an immediate recovery if the 378c2ecf20Sopenharmony_ci * remote processor is in a crashed state, without changing 388c2ecf20Sopenharmony_ci * or checking the recovery state (enabled/disabled). 398c2ecf20Sopenharmony_ci * This is useful during debugging sessions, when one expects 408c2ecf20Sopenharmony_ci * additional crashes to happen after enabling recovery. In this 418c2ecf20Sopenharmony_ci * case, enabling recovery will make it hard to debug subsequent 428c2ecf20Sopenharmony_ci * crashes, so it's recommended to keep recovery disabled, and 438c2ecf20Sopenharmony_ci * instead use the "recover" command as needed. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistatic ssize_t recovery_store(struct device *dev, 468c2ecf20Sopenharmony_ci struct device_attribute *attr, 478c2ecf20Sopenharmony_ci const char *buf, size_t count) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct rproc *rproc = to_rproc(dev); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (sysfs_streq(buf, "enabled")) { 528c2ecf20Sopenharmony_ci /* change the flag and begin the recovery process if needed */ 538c2ecf20Sopenharmony_ci rproc->recovery_disabled = false; 548c2ecf20Sopenharmony_ci rproc_trigger_recovery(rproc); 558c2ecf20Sopenharmony_ci } else if (sysfs_streq(buf, "disabled")) { 568c2ecf20Sopenharmony_ci rproc->recovery_disabled = true; 578c2ecf20Sopenharmony_ci } else if (sysfs_streq(buf, "recover")) { 588c2ecf20Sopenharmony_ci /* begin the recovery process without changing the flag */ 598c2ecf20Sopenharmony_ci rproc_trigger_recovery(rproc); 608c2ecf20Sopenharmony_ci } else { 618c2ecf20Sopenharmony_ci return -EINVAL; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return count; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(recovery); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * A coredump-configuration-to-string lookup table, for exposing a 708c2ecf20Sopenharmony_ci * human readable configuration via sysfs. Always keep in sync with 718c2ecf20Sopenharmony_ci * enum rproc_coredump_mechanism 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistatic const char * const rproc_coredump_str[] = { 748c2ecf20Sopenharmony_ci [RPROC_COREDUMP_DISABLED] = "disabled", 758c2ecf20Sopenharmony_ci [RPROC_COREDUMP_ENABLED] = "enabled", 768c2ecf20Sopenharmony_ci [RPROC_COREDUMP_INLINE] = "inline", 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* Expose the current coredump configuration via debugfs */ 808c2ecf20Sopenharmony_cistatic ssize_t coredump_show(struct device *dev, 818c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct rproc *rproc = to_rproc(dev); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* 898c2ecf20Sopenharmony_ci * By writing to the 'coredump' sysfs entry, we control the behavior of the 908c2ecf20Sopenharmony_ci * coredump mechanism dynamically. The default value of this entry is "default". 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * The 'coredump' sysfs entry supports these commands: 938c2ecf20Sopenharmony_ci * 948c2ecf20Sopenharmony_ci * disabled: This is the default coredump mechanism. Recovery will proceed 958c2ecf20Sopenharmony_ci * without collecting any dump. 968c2ecf20Sopenharmony_ci * 978c2ecf20Sopenharmony_ci * default: When the remoteproc crashes the entire coredump will be 988c2ecf20Sopenharmony_ci * copied to a separate buffer and exposed to userspace. 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci * inline: The coredump will not be copied to a separate buffer and the 1018c2ecf20Sopenharmony_ci * recovery process will have to wait until data is read by 1028c2ecf20Sopenharmony_ci * userspace. But this avoid usage of extra memory. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistatic ssize_t coredump_store(struct device *dev, 1058c2ecf20Sopenharmony_ci struct device_attribute *attr, 1068c2ecf20Sopenharmony_ci const char *buf, size_t count) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct rproc *rproc = to_rproc(dev); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (rproc->state == RPROC_CRASHED) { 1118c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "can't change coredump configuration\n"); 1128c2ecf20Sopenharmony_ci return -EBUSY; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (sysfs_streq(buf, "disabled")) { 1168c2ecf20Sopenharmony_ci rproc->dump_conf = RPROC_COREDUMP_DISABLED; 1178c2ecf20Sopenharmony_ci } else if (sysfs_streq(buf, "enabled")) { 1188c2ecf20Sopenharmony_ci rproc->dump_conf = RPROC_COREDUMP_ENABLED; 1198c2ecf20Sopenharmony_ci } else if (sysfs_streq(buf, "inline")) { 1208c2ecf20Sopenharmony_ci rproc->dump_conf = RPROC_COREDUMP_INLINE; 1218c2ecf20Sopenharmony_ci } else { 1228c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "Invalid coredump configuration\n"); 1238c2ecf20Sopenharmony_ci return -EINVAL; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return count; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(coredump); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* Expose the loaded / running firmware name via sysfs */ 1318c2ecf20Sopenharmony_cistatic ssize_t firmware_show(struct device *dev, struct device_attribute *attr, 1328c2ecf20Sopenharmony_ci char *buf) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct rproc *rproc = to_rproc(dev); 1358c2ecf20Sopenharmony_ci const char *firmware = rproc->firmware; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * If the remote processor has been started by an external 1398c2ecf20Sopenharmony_ci * entity we have no idea of what image it is running. As such 1408c2ecf20Sopenharmony_ci * simply display a generic string rather then rproc->firmware. 1418c2ecf20Sopenharmony_ci * 1428c2ecf20Sopenharmony_ci * Here we rely on the autonomous flag because a remote processor 1438c2ecf20Sopenharmony_ci * may have been attached to and currently in a running state. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci if (rproc->autonomous) 1468c2ecf20Sopenharmony_ci firmware = "unknown"; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", firmware); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* Change firmware name via sysfs */ 1528c2ecf20Sopenharmony_cistatic ssize_t firmware_store(struct device *dev, 1538c2ecf20Sopenharmony_ci struct device_attribute *attr, 1548c2ecf20Sopenharmony_ci const char *buf, size_t count) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct rproc *rproc = to_rproc(dev); 1578c2ecf20Sopenharmony_ci char *p; 1588c2ecf20Sopenharmony_ci int err, len = count; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci err = mutex_lock_interruptible(&rproc->lock); 1618c2ecf20Sopenharmony_ci if (err) { 1628c2ecf20Sopenharmony_ci dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, err); 1638c2ecf20Sopenharmony_ci return -EINVAL; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (rproc->state != RPROC_OFFLINE) { 1678c2ecf20Sopenharmony_ci dev_err(dev, "can't change firmware while running\n"); 1688c2ecf20Sopenharmony_ci err = -EBUSY; 1698c2ecf20Sopenharmony_ci goto out; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci len = strcspn(buf, "\n"); 1738c2ecf20Sopenharmony_ci if (!len) { 1748c2ecf20Sopenharmony_ci dev_err(dev, "can't provide a NULL firmware\n"); 1758c2ecf20Sopenharmony_ci err = -EINVAL; 1768c2ecf20Sopenharmony_ci goto out; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci p = kstrndup(buf, len, GFP_KERNEL); 1808c2ecf20Sopenharmony_ci if (!p) { 1818c2ecf20Sopenharmony_ci err = -ENOMEM; 1828c2ecf20Sopenharmony_ci goto out; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci kfree(rproc->firmware); 1868c2ecf20Sopenharmony_ci rproc->firmware = p; 1878c2ecf20Sopenharmony_ciout: 1888c2ecf20Sopenharmony_ci mutex_unlock(&rproc->lock); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return err ? err : count; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(firmware); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* 1958c2ecf20Sopenharmony_ci * A state-to-string lookup table, for exposing a human readable state 1968c2ecf20Sopenharmony_ci * via sysfs. Always keep in sync with enum rproc_state 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_cistatic const char * const rproc_state_string[] = { 1998c2ecf20Sopenharmony_ci [RPROC_OFFLINE] = "offline", 2008c2ecf20Sopenharmony_ci [RPROC_SUSPENDED] = "suspended", 2018c2ecf20Sopenharmony_ci [RPROC_RUNNING] = "running", 2028c2ecf20Sopenharmony_ci [RPROC_CRASHED] = "crashed", 2038c2ecf20Sopenharmony_ci [RPROC_DELETED] = "deleted", 2048c2ecf20Sopenharmony_ci [RPROC_DETACHED] = "detached", 2058c2ecf20Sopenharmony_ci [RPROC_LAST] = "invalid", 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/* Expose the state of the remote processor via sysfs */ 2098c2ecf20Sopenharmony_cistatic ssize_t state_show(struct device *dev, struct device_attribute *attr, 2108c2ecf20Sopenharmony_ci char *buf) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct rproc *rproc = to_rproc(dev); 2138c2ecf20Sopenharmony_ci unsigned int state; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state; 2168c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", rproc_state_string[state]); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* Change remote processor state via sysfs */ 2208c2ecf20Sopenharmony_cistatic ssize_t state_store(struct device *dev, 2218c2ecf20Sopenharmony_ci struct device_attribute *attr, 2228c2ecf20Sopenharmony_ci const char *buf, size_t count) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct rproc *rproc = to_rproc(dev); 2258c2ecf20Sopenharmony_ci int ret = 0; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (sysfs_streq(buf, "start")) { 2288c2ecf20Sopenharmony_ci if (rproc->state == RPROC_RUNNING) 2298c2ecf20Sopenharmony_ci return -EBUSY; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci ret = rproc_boot(rproc); 2328c2ecf20Sopenharmony_ci if (ret) 2338c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "Boot failed: %d\n", ret); 2348c2ecf20Sopenharmony_ci } else if (sysfs_streq(buf, "stop")) { 2358c2ecf20Sopenharmony_ci if (rproc->state != RPROC_RUNNING) 2368c2ecf20Sopenharmony_ci return -EINVAL; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci rproc_shutdown(rproc); 2398c2ecf20Sopenharmony_ci } else { 2408c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "Unrecognised option: %s\n", buf); 2418c2ecf20Sopenharmony_ci ret = -EINVAL; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci return ret ? ret : count; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(state); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* Expose the name of the remote processor via sysfs */ 2488c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *attr, 2498c2ecf20Sopenharmony_ci char *buf) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct rproc *rproc = to_rproc(dev); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", rproc->name); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic struct attribute *rproc_attrs[] = { 2588c2ecf20Sopenharmony_ci &dev_attr_coredump.attr, 2598c2ecf20Sopenharmony_ci &dev_attr_recovery.attr, 2608c2ecf20Sopenharmony_ci &dev_attr_firmware.attr, 2618c2ecf20Sopenharmony_ci &dev_attr_state.attr, 2628c2ecf20Sopenharmony_ci &dev_attr_name.attr, 2638c2ecf20Sopenharmony_ci NULL 2648c2ecf20Sopenharmony_ci}; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic const struct attribute_group rproc_devgroup = { 2678c2ecf20Sopenharmony_ci .attrs = rproc_attrs 2688c2ecf20Sopenharmony_ci}; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic const struct attribute_group *rproc_devgroups[] = { 2718c2ecf20Sopenharmony_ci &rproc_devgroup, 2728c2ecf20Sopenharmony_ci NULL 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistruct class rproc_class = { 2768c2ecf20Sopenharmony_ci .name = "remoteproc", 2778c2ecf20Sopenharmony_ci .dev_groups = rproc_devgroups, 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ciint __init rproc_init_sysfs(void) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci /* create remoteproc device class for sysfs */ 2838c2ecf20Sopenharmony_ci int err = class_register(&rproc_class); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (err) 2868c2ecf20Sopenharmony_ci pr_err("remoteproc: unable to register class\n"); 2878c2ecf20Sopenharmony_ci return err; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_civoid __exit rproc_exit_sysfs(void) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci class_unregister(&rproc_class); 2938c2ecf20Sopenharmony_ci} 294