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 sysfs_emit(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 sysfs_emit(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 if (rproc->state == RPROC_ATTACHED) 143 firmware = "unknown"; 144 145 return sprintf(buf, "%s\n", firmware); 146 } 147 148 /* Change firmware name via sysfs */ 149 static ssize_t firmware_store(struct device *dev, 150 struct device_attribute *attr, 151 const char *buf, size_t count) 152 { 153 struct rproc *rproc = to_rproc(dev); 154 int err; 155 156 err = rproc_set_firmware(rproc, buf); 157 158 return err ? err : count; 159 } 160 static DEVICE_ATTR_RW(firmware); 161 162 /* 163 * A state-to-string lookup table, for exposing a human readable state 164 * via sysfs. Always keep in sync with enum rproc_state 165 */ 166 static const char * const rproc_state_string[] = { 167 [RPROC_OFFLINE] = "offline", 168 [RPROC_SUSPENDED] = "suspended", 169 [RPROC_RUNNING] = "running", 170 [RPROC_CRASHED] = "crashed", 171 [RPROC_DELETED] = "deleted", 172 [RPROC_ATTACHED] = "attached", 173 [RPROC_DETACHED] = "detached", 174 [RPROC_LAST] = "invalid", 175 }; 176 177 /* Expose the state of the remote processor via sysfs */ 178 static ssize_t state_show(struct device *dev, struct device_attribute *attr, 179 char *buf) 180 { 181 struct rproc *rproc = to_rproc(dev); 182 unsigned int state; 183 184 state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state; 185 return sprintf(buf, "%s\n", rproc_state_string[state]); 186 } 187 188 /* Change remote processor state via sysfs */ 189 static ssize_t state_store(struct device *dev, 190 struct device_attribute *attr, 191 const char *buf, size_t count) 192 { 193 struct rproc *rproc = to_rproc(dev); 194 int ret = 0; 195 196 if (sysfs_streq(buf, "start")) { 197 ret = rproc_boot(rproc); 198 if (ret) 199 dev_err(&rproc->dev, "Boot failed: %d\n", ret); 200 } else if (sysfs_streq(buf, "stop")) { 201 ret = rproc_shutdown(rproc); 202 } else if (sysfs_streq(buf, "detach")) { 203 ret = rproc_detach(rproc); 204 } else { 205 dev_err(&rproc->dev, "Unrecognised option: %s\n", buf); 206 ret = -EINVAL; 207 } 208 return ret ? ret : count; 209 } 210 static DEVICE_ATTR_RW(state); 211 212 /* Expose the name of the remote processor via sysfs */ 213 static ssize_t name_show(struct device *dev, struct device_attribute *attr, 214 char *buf) 215 { 216 struct rproc *rproc = to_rproc(dev); 217 218 return sprintf(buf, "%s\n", rproc->name); 219 } 220 static DEVICE_ATTR_RO(name); 221 222 static umode_t rproc_is_visible(struct kobject *kobj, struct attribute *attr, 223 int n) 224 { 225 struct device *dev = kobj_to_dev(kobj); 226 struct rproc *rproc = to_rproc(dev); 227 umode_t mode = attr->mode; 228 229 if (rproc->sysfs_read_only && (attr == &dev_attr_recovery.attr || 230 attr == &dev_attr_firmware.attr || 231 attr == &dev_attr_state.attr || 232 attr == &dev_attr_coredump.attr)) 233 mode = 0444; 234 235 return mode; 236 } 237 238 static struct attribute *rproc_attrs[] = { 239 &dev_attr_coredump.attr, 240 &dev_attr_recovery.attr, 241 &dev_attr_firmware.attr, 242 &dev_attr_state.attr, 243 &dev_attr_name.attr, 244 NULL 245 }; 246 247 static const struct attribute_group rproc_devgroup = { 248 .attrs = rproc_attrs, 249 .is_visible = rproc_is_visible, 250 }; 251 252 static const struct attribute_group *rproc_devgroups[] = { 253 &rproc_devgroup, 254 NULL 255 }; 256 257 struct class rproc_class = { 258 .name = "remoteproc", 259 .dev_groups = rproc_devgroups, 260 }; 261 262 int __init rproc_init_sysfs(void) 263 { 264 /* create remoteproc device class for sysfs */ 265 int err = class_register(&rproc_class); 266 267 if (err) 268 pr_err("remoteproc: unable to register class\n"); 269 return err; 270 } 271 272 void __exit rproc_exit_sysfs(void) 273 { 274 class_unregister(&rproc_class); 275 } 276