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 | ||
f75c6043 RB |
13 | /* |
14 | * A coredump-configuration-to-string lookup table, for exposing a | |
15 | * human readable configuration via sysfs. Always keep in sync with | |
16 | * enum rproc_coredump_mechanism | |
17 | */ | |
18 | static const char * const rproc_coredump_str[] = { | |
19 | [RPROC_COREDUMP_DISABLED] = "disabled", | |
20 | [RPROC_COREDUMP_ENABLED] = "enabled", | |
21 | [RPROC_COREDUMP_INLINE] = "inline", | |
22 | }; | |
23 | ||
24 | /* Expose the current coredump configuration via debugfs */ | |
25 | static ssize_t coredump_show(struct device *dev, | |
26 | struct device_attribute *attr, char *buf) | |
27 | { | |
28 | struct rproc *rproc = to_rproc(dev); | |
29 | ||
30 | return sprintf(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]); | |
31 | } | |
32 | ||
33 | /* | |
34 | * By writing to the 'coredump' sysfs entry, we control the behavior of the | |
35 | * coredump mechanism dynamically. The default value of this entry is "default". | |
36 | * | |
37 | * The 'coredump' sysfs entry supports these commands: | |
38 | * | |
39 | * disabled: This is the default coredump mechanism. Recovery will proceed | |
40 | * without collecting any dump. | |
41 | * | |
42 | * default: When the remoteproc crashes the entire coredump will be | |
43 | * copied to a separate buffer and exposed to userspace. | |
44 | * | |
45 | * inline: The coredump will not be copied to a separate buffer and the | |
46 | * recovery process will have to wait until data is read by | |
47 | * userspace. But this avoid usage of extra memory. | |
48 | */ | |
49 | static ssize_t coredump_store(struct device *dev, | |
50 | struct device_attribute *attr, | |
51 | const char *buf, size_t count) | |
52 | { | |
53 | struct rproc *rproc = to_rproc(dev); | |
54 | ||
55 | if (rproc->state == RPROC_CRASHED) { | |
56 | dev_err(&rproc->dev, "can't change coredump configuration\n"); | |
57 | return -EBUSY; | |
58 | } | |
59 | ||
60 | if (sysfs_streq(buf, "disabled")) { | |
61 | rproc->dump_conf = RPROC_COREDUMP_DISABLED; | |
62 | } else if (sysfs_streq(buf, "enabled")) { | |
63 | rproc->dump_conf = RPROC_COREDUMP_ENABLED; | |
64 | } else if (sysfs_streq(buf, "inline")) { | |
65 | rproc->dump_conf = RPROC_COREDUMP_INLINE; | |
66 | } else { | |
67 | dev_err(&rproc->dev, "Invalid coredump configuration\n"); | |
68 | return -EINVAL; | |
69 | } | |
70 | ||
71 | return count; | |
72 | } | |
73 | static DEVICE_ATTR_RW(coredump); | |
74 | ||
2aefbef0 MR |
75 | /* Expose the loaded / running firmware name via sysfs */ |
76 | static ssize_t firmware_show(struct device *dev, struct device_attribute *attr, | |
77 | char *buf) | |
78 | { | |
79 | struct rproc *rproc = to_rproc(dev); | |
4a4dca19 | 80 | const char *firmware = rproc->firmware; |
2aefbef0 | 81 | |
4a4dca19 MP |
82 | /* |
83 | * If the remote processor has been started by an external | |
84 | * entity we have no idea of what image it is running. As such | |
85 | * simply display a generic string rather then rproc->firmware. | |
86 | * | |
87 | * Here we rely on the autonomous flag because a remote processor | |
88 | * may have been attached to and currently in a running state. | |
89 | */ | |
90 | if (rproc->autonomous) | |
91 | firmware = "unknown"; | |
92 | ||
93 | return sprintf(buf, "%s\n", firmware); | |
2aefbef0 MR |
94 | } |
95 | ||
96 | /* Change firmware name via sysfs */ | |
97 | static ssize_t firmware_store(struct device *dev, | |
98 | struct device_attribute *attr, | |
99 | const char *buf, size_t count) | |
100 | { | |
101 | struct rproc *rproc = to_rproc(dev); | |
102 | char *p; | |
103 | int err, len = count; | |
104 | ||
105 | err = mutex_lock_interruptible(&rproc->lock); | |
106 | if (err) { | |
107 | dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, err); | |
108 | return -EINVAL; | |
109 | } | |
110 | ||
111 | if (rproc->state != RPROC_OFFLINE) { | |
112 | dev_err(dev, "can't change firmware while running\n"); | |
113 | err = -EBUSY; | |
114 | goto out; | |
115 | } | |
116 | ||
117 | len = strcspn(buf, "\n"); | |
faeadbb6 SA |
118 | if (!len) { |
119 | dev_err(dev, "can't provide a NULL firmware\n"); | |
120 | err = -EINVAL; | |
121 | goto out; | |
122 | } | |
2aefbef0 MR |
123 | |
124 | p = kstrndup(buf, len, GFP_KERNEL); | |
125 | if (!p) { | |
126 | err = -ENOMEM; | |
127 | goto out; | |
128 | } | |
129 | ||
130 | kfree(rproc->firmware); | |
131 | rproc->firmware = p; | |
132 | out: | |
133 | mutex_unlock(&rproc->lock); | |
134 | ||
135 | return err ? err : count; | |
136 | } | |
137 | static DEVICE_ATTR_RW(firmware); | |
138 | ||
139 | /* | |
140 | * A state-to-string lookup table, for exposing a human readable state | |
141 | * via sysfs. Always keep in sync with enum rproc_state | |
142 | */ | |
143 | static const char * const rproc_state_string[] = { | |
144 | [RPROC_OFFLINE] = "offline", | |
145 | [RPROC_SUSPENDED] = "suspended", | |
146 | [RPROC_RUNNING] = "running", | |
147 | [RPROC_CRASHED] = "crashed", | |
608d7921 | 148 | [RPROC_DELETED] = "deleted", |
e2e5c55e | 149 | [RPROC_DETACHED] = "detached", |
2aefbef0 MR |
150 | [RPROC_LAST] = "invalid", |
151 | }; | |
152 | ||
153 | /* Expose the state of the remote processor via sysfs */ | |
154 | static ssize_t state_show(struct device *dev, struct device_attribute *attr, | |
155 | char *buf) | |
156 | { | |
157 | struct rproc *rproc = to_rproc(dev); | |
158 | unsigned int state; | |
159 | ||
160 | state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state; | |
161 | return sprintf(buf, "%s\n", rproc_state_string[state]); | |
162 | } | |
163 | ||
164 | /* Change remote processor state via sysfs */ | |
165 | static ssize_t state_store(struct device *dev, | |
166 | struct device_attribute *attr, | |
167 | const char *buf, size_t count) | |
168 | { | |
169 | struct rproc *rproc = to_rproc(dev); | |
170 | int ret = 0; | |
171 | ||
172 | if (sysfs_streq(buf, "start")) { | |
173 | if (rproc->state == RPROC_RUNNING) | |
174 | return -EBUSY; | |
175 | ||
176 | ret = rproc_boot(rproc); | |
177 | if (ret) | |
178 | dev_err(&rproc->dev, "Boot failed: %d\n", ret); | |
179 | } else if (sysfs_streq(buf, "stop")) { | |
180 | if (rproc->state != RPROC_RUNNING) | |
181 | return -EINVAL; | |
182 | ||
183 | rproc_shutdown(rproc); | |
184 | } else { | |
185 | dev_err(&rproc->dev, "Unrecognised option: %s\n", buf); | |
186 | ret = -EINVAL; | |
187 | } | |
188 | return ret ? ret : count; | |
189 | } | |
190 | static DEVICE_ATTR_RW(state); | |
191 | ||
6ed756aa SA |
192 | /* Expose the name of the remote processor via sysfs */ |
193 | static ssize_t name_show(struct device *dev, struct device_attribute *attr, | |
194 | char *buf) | |
195 | { | |
196 | struct rproc *rproc = to_rproc(dev); | |
197 | ||
198 | return sprintf(buf, "%s\n", rproc->name); | |
199 | } | |
200 | static DEVICE_ATTR_RO(name); | |
201 | ||
2aefbef0 | 202 | static struct attribute *rproc_attrs[] = { |
f75c6043 | 203 | &dev_attr_coredump.attr, |
2aefbef0 MR |
204 | &dev_attr_firmware.attr, |
205 | &dev_attr_state.attr, | |
6ed756aa | 206 | &dev_attr_name.attr, |
2aefbef0 MR |
207 | NULL |
208 | }; | |
209 | ||
210 | static const struct attribute_group rproc_devgroup = { | |
211 | .attrs = rproc_attrs | |
212 | }; | |
213 | ||
214 | static const struct attribute_group *rproc_devgroups[] = { | |
215 | &rproc_devgroup, | |
216 | NULL | |
217 | }; | |
218 | ||
219 | struct class rproc_class = { | |
220 | .name = "remoteproc", | |
221 | .dev_groups = rproc_devgroups, | |
222 | }; | |
223 | ||
224 | int __init rproc_init_sysfs(void) | |
225 | { | |
226 | /* create remoteproc device class for sysfs */ | |
227 | int err = class_register(&rproc_class); | |
228 | ||
229 | if (err) | |
230 | pr_err("remoteproc: unable to register class\n"); | |
231 | return err; | |
232 | } | |
233 | ||
234 | void __exit rproc_exit_sysfs(void) | |
235 | { | |
236 | class_unregister(&rproc_class); | |
237 | } |