Commit | Line | Data |
---|---|---|
1802d0be | 1 | // SPDX-License-Identifier: GPL-2.0-only |
2aefbef0 MR |
2 | /* |
3 | * Remote Processor Framework | |
2aefbef0 MR |
4 | */ |
5 | ||
6 | #include <linux/remoteproc.h> | |
bf89a7c0 | 7 | #include <linux/slab.h> |
2aefbef0 MR |
8 | |
9 | #include "remoteproc_internal.h" | |
10 | ||
11 | #define to_rproc(d) container_of(d, struct rproc, dev) | |
12 | ||
526b9e0c RB |
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 | ||
145e1da3 | 18 | return sysfs_emit(buf, "%s", rproc->recovery_disabled ? "disabled\n" : "enabled\n"); |
526b9e0c RB |
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 | ||
f75c6043 RB |
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 | ||
145e1da3 | 85 | return sysfs_emit(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]); |
f75c6043 RB |
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 | ||
2aefbef0 MR |
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); | |
4a4dca19 | 135 | const char *firmware = rproc->firmware; |
2aefbef0 | 136 | |
4a4dca19 MP |
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. | |
4a4dca19 | 141 | */ |
76f4c875 | 142 | if (rproc->state == RPROC_ATTACHED) |
4a4dca19 MP |
143 | firmware = "unknown"; |
144 | ||
145 | return sprintf(buf, "%s\n", firmware); | |
2aefbef0 MR |
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); | |
4c1ad562 | 154 | int err; |
2aefbef0 | 155 | |
4c1ad562 | 156 | err = rproc_set_firmware(rproc, buf); |
2aefbef0 MR |
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", | |
608d7921 | 171 | [RPROC_DELETED] = "deleted", |
4196d189 | 172 | [RPROC_ATTACHED] = "attached", |
e2e5c55e | 173 | [RPROC_DETACHED] = "detached", |
2aefbef0 MR |
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")) { | |
83d4e671 MP |
197 | if (rproc->state == RPROC_RUNNING || |
198 | rproc->state == RPROC_ATTACHED) | |
2aefbef0 MR |
199 | return -EBUSY; |
200 | ||
201 | ret = rproc_boot(rproc); | |
202 | if (ret) | |
203 | dev_err(&rproc->dev, "Boot failed: %d\n", ret); | |
204 | } else if (sysfs_streq(buf, "stop")) { | |
d2008a96 MP |
205 | if (rproc->state != RPROC_RUNNING && |
206 | rproc->state != RPROC_ATTACHED) | |
2aefbef0 MR |
207 | return -EINVAL; |
208 | ||
209 | rproc_shutdown(rproc); | |
5daaeb5f MP |
210 | } else if (sysfs_streq(buf, "detach")) { |
211 | if (rproc->state != RPROC_ATTACHED) | |
212 | return -EINVAL; | |
213 | ||
214 | ret = rproc_detach(rproc); | |
2aefbef0 MR |
215 | } else { |
216 | dev_err(&rproc->dev, "Unrecognised option: %s\n", buf); | |
217 | ret = -EINVAL; | |
218 | } | |
219 | return ret ? ret : count; | |
220 | } | |
221 | static DEVICE_ATTR_RW(state); | |
222 | ||
6ed756aa SA |
223 | /* Expose the name of the remote processor via sysfs */ |
224 | static ssize_t name_show(struct device *dev, struct device_attribute *attr, | |
225 | char *buf) | |
226 | { | |
227 | struct rproc *rproc = to_rproc(dev); | |
228 | ||
229 | return sprintf(buf, "%s\n", rproc->name); | |
230 | } | |
231 | static DEVICE_ATTR_RO(name); | |
232 | ||
2aefbef0 | 233 | static struct attribute *rproc_attrs[] = { |
f75c6043 | 234 | &dev_attr_coredump.attr, |
526b9e0c | 235 | &dev_attr_recovery.attr, |
2aefbef0 MR |
236 | &dev_attr_firmware.attr, |
237 | &dev_attr_state.attr, | |
6ed756aa | 238 | &dev_attr_name.attr, |
2aefbef0 MR |
239 | NULL |
240 | }; | |
241 | ||
242 | static const struct attribute_group rproc_devgroup = { | |
243 | .attrs = rproc_attrs | |
244 | }; | |
245 | ||
246 | static const struct attribute_group *rproc_devgroups[] = { | |
247 | &rproc_devgroup, | |
248 | NULL | |
249 | }; | |
250 | ||
251 | struct class rproc_class = { | |
252 | .name = "remoteproc", | |
253 | .dev_groups = rproc_devgroups, | |
254 | }; | |
255 | ||
256 | int __init rproc_init_sysfs(void) | |
257 | { | |
258 | /* create remoteproc device class for sysfs */ | |
259 | int err = class_register(&rproc_class); | |
260 | ||
261 | if (err) | |
262 | pr_err("remoteproc: unable to register class\n"); | |
263 | return err; | |
264 | } | |
265 | ||
266 | void __exit rproc_exit_sysfs(void) | |
267 | { | |
268 | class_unregister(&rproc_class); | |
269 | } |