1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Remote Processor Framework 4 */ 5 6 #include <linux/remoteproc.h> 7 #include <linux/slab.h> 8 9 #include "remoteproc_internal.h" 10 11 #define to_rproc(d) container_of(d, struct rproc, dev) 12 13 static ssize_t recovery_show(struct device *dev, 14 struct device_attribute *attr, char *buf) 15 { 16 struct rproc *rproc = to_rproc(dev); 17 18 return sprintf(buf, "%s", rproc->recovery_disabled ? "disabled\n" : "enabled\n"); 19 } 20 21 /* 22 * By writing to the 'recovery' sysfs entry, we control the behavior of the 23 * recovery mechanism dynamically. The default value of this entry is "enabled". 24 * 25 * The 'recovery' sysfs entry supports these commands: 26 * 27 * enabled: When enabled, the remote processor will be automatically 28 * recovered whenever it crashes. Moreover, if the remote 29 * processor crashes while recovery is disabled, it will 30 * be automatically recovered too as soon as recovery is enabled. 31 * 32 * disabled: When disabled, a remote processor will remain in a crashed 33 * state if it crashes. This is useful for debugging purposes; 34 * without it, debugging a crash is substantially harder. 35 * 36 * recover: This function will trigger an immediate recovery if the 37 * remote processor is in a crashed state, without changing 38 * or checking the recovery state (enabled/disabled). 39 * This is useful during debugging sessions, when one expects 40 * additional crashes to happen after enabling recovery. In this 41 * case, enabling recovery will make it hard to debug subsequent 42 * crashes, so it's recommended to keep recovery disabled, and 43 * instead use the "recover" command as needed. 44 */ 45 static ssize_t recovery_store(struct device *dev, 46 struct device_attribute *attr, 47 const char *buf, size_t count) 48 { 49 struct rproc *rproc = to_rproc(dev); 50 51 if (sysfs_streq(buf, "enabled")) { 52 /* change the flag and begin the recovery process if needed */ 53 rproc->recovery_disabled = false; 54 rproc_trigger_recovery(rproc); 55 } else if (sysfs_streq(buf, "disabled")) { 56 rproc->recovery_disabled = true; 57 } else if (sysfs_streq(buf, "recover")) { 58 /* begin the recovery process without changing the flag */ 59 rproc_trigger_recovery(rproc); 60 } else { 61 return -EINVAL; 62 } 63 64 return count; 65 } 66 static DEVICE_ATTR_RW(recovery); 67 68 /* 69 * A coredump-configuration-to-string lookup table, for exposing a 70 * human readable configuration via sysfs. Always keep in sync with 71 * enum rproc_coredump_mechanism 72 */ 73 static const char * const rproc_coredump_str[] = { 74 [RPROC_COREDUMP_DISABLED] = "disabled", 75 [RPROC_COREDUMP_ENABLED] = "enabled", 76 [RPROC_COREDUMP_INLINE] = "inline", 77 }; 78 79 /* Expose the current coredump configuration via debugfs */ 80 static ssize_t coredump_show(struct device *dev, 81 struct device_attribute *attr, char *buf) 82 { 83 struct rproc *rproc = to_rproc(dev); 84 85 return sprintf(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]); 86 } 87 88 /* 89 * By writing to the 'coredump' sysfs entry, we control the behavior of the 90 * coredump mechanism dynamically. The default value of this entry is "default". 91 * 92 * The 'coredump' sysfs entry supports these commands: 93 * 94 * disabled: This is the default coredump mechanism. Recovery will proceed 95 * without collecting any dump. 96 * 97 * default: When the remoteproc crashes the entire coredump will be 98 * copied to a separate buffer and exposed to userspace. 99 * 100 * inline: The coredump will not be copied to a separate buffer and the 101 * recovery process will have to wait until data is read by 102 * userspace. But this avoid usage of extra memory. 103 */ 104 static ssize_t coredump_store(struct device *dev, 105 struct device_attribute *attr, 106 const char *buf, size_t count) 107 { 108 struct rproc *rproc = to_rproc(dev); 109 110 if (rproc->state == RPROC_CRASHED) { 111 dev_err(&rproc->dev, "can't change coredump configuration\n"); 112 return -EBUSY; 113 } 114 115 if (sysfs_streq(buf, "disabled")) { 116 rproc->dump_conf = RPROC_COREDUMP_DISABLED; 117 } else if (sysfs_streq(buf, "enabled")) { 118 rproc->dump_conf = RPROC_COREDUMP_ENABLED; 119 } else if (sysfs_streq(buf, "inline")) { 120 rproc->dump_conf = RPROC_COREDUMP_INLINE; 121 } else { 122 dev_err(&rproc->dev, "Invalid coredump configuration\n"); 123 return -EINVAL; 124 } 125 126 return count; 127 } 128 static DEVICE_ATTR_RW(coredump); 129 130 /* Expose the loaded / running firmware name via sysfs */ 131 static ssize_t firmware_show(struct device *dev, struct device_attribute *attr, 132 char *buf) 133 { 134 struct rproc *rproc = to_rproc(dev); 135 const char *firmware = rproc->firmware; 136 137 /* 138 * If the remote processor has been started by an external 139 * entity we have no idea of what image it is running. As such 140 * simply display a generic string rather then rproc->firmware. 141 * 142 * Here we rely on the autonomous flag because a remote processor 143 * may have been attached to and currently in a running state. 144 */ 145 if (rproc->autonomous) 146 firmware = "unknown"; 147 148 return sprintf(buf, "%s\n", firmware); 149 } 150 151 /* Change firmware name via sysfs */ 152 static ssize_t firmware_store(struct device *dev, 153 struct device_attribute *attr, 154 const char *buf, size_t count) 155 { 156 struct rproc *rproc = to_rproc(dev); 157 int err; 158 159 err = rproc_set_firmware(rproc, buf); 160 161 return err ? err : count; 162 } 163 static DEVICE_ATTR_RW(firmware); 164 165 /* 166 * A state-to-string lookup table, for exposing a human readable state 167 * via sysfs. Always keep in sync with enum rproc_state 168 */ 169 static const char * const rproc_state_string[] = { 170 [RPROC_OFFLINE] = "offline", 171 [RPROC_SUSPENDED] = "suspended", 172 [RPROC_RUNNING] = "running", 173 [RPROC_CRASHED] = "crashed", 174 [RPROC_DELETED] = "deleted", 175 [RPROC_DETACHED] = "detached", 176 [RPROC_LAST] = "invalid", 177 }; 178 179 /* Expose the state of the remote processor via sysfs */ 180 static ssize_t state_show(struct device *dev, struct device_attribute *attr, 181 char *buf) 182 { 183 struct rproc *rproc = to_rproc(dev); 184 unsigned int state; 185 186 state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state; 187 return sprintf(buf, "%s\n", rproc_state_string[state]); 188 } 189 190 /* Change remote processor state via sysfs */ 191 static ssize_t state_store(struct device *dev, 192 struct device_attribute *attr, 193 const char *buf, size_t count) 194 { 195 struct rproc *rproc = to_rproc(dev); 196 int ret = 0; 197 198 if (sysfs_streq(buf, "start")) { 199 if (rproc->state == RPROC_RUNNING) 200 return -EBUSY; 201 202 ret = rproc_boot(rproc); 203 if (ret) 204 dev_err(&rproc->dev, "Boot failed: %d\n", ret); 205 } else if (sysfs_streq(buf, "stop")) { 206 if (rproc->state != RPROC_RUNNING) 207 return -EINVAL; 208 209 rproc_shutdown(rproc); 210 } else { 211 dev_err(&rproc->dev, "Unrecognised option: %s\n", buf); 212 ret = -EINVAL; 213 } 214 return ret ? ret : count; 215 } 216 static DEVICE_ATTR_RW(state); 217 218 /* Expose the name of the remote processor via sysfs */ 219 static ssize_t name_show(struct device *dev, struct device_attribute *attr, 220 char *buf) 221 { 222 struct rproc *rproc = to_rproc(dev); 223 224 return sprintf(buf, "%s\n", rproc->name); 225 } 226 static DEVICE_ATTR_RO(name); 227 228 static struct attribute *rproc_attrs[] = { 229 &dev_attr_coredump.attr, 230 &dev_attr_recovery.attr, 231 &dev_attr_firmware.attr, 232 &dev_attr_state.attr, 233 &dev_attr_name.attr, 234 NULL 235 }; 236 237 static const struct attribute_group rproc_devgroup = { 238 .attrs = rproc_attrs 239 }; 240 241 static const struct attribute_group *rproc_devgroups[] = { 242 &rproc_devgroup, 243 NULL 244 }; 245 246 struct class rproc_class = { 247 .name = "remoteproc", 248 .dev_groups = rproc_devgroups, 249 }; 250 251 int __init rproc_init_sysfs(void) 252 { 253 /* create remoteproc device class for sysfs */ 254 int err = class_register(&rproc_class); 255 256 if (err) 257 pr_err("remoteproc: unable to register class\n"); 258 return err; 259 } 260 261 void __exit rproc_exit_sysfs(void) 262 { 263 class_unregister(&rproc_class); 264 } 265