]> Git Repo - J-linux.git/blob - drivers/base/firmware_loader/sysfs_upload.c
Merge tag 'kbuild-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy...
[J-linux.git] / drivers / base / firmware_loader / sysfs_upload.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <linux/firmware.h>
4 #include <linux/module.h>
5 #include <linux/slab.h>
6
7 #include "sysfs_upload.h"
8
9 /*
10  * Support for user-space to initiate a firmware upload to a device.
11  */
12
13 static const char * const fw_upload_prog_str[] = {
14         [FW_UPLOAD_PROG_IDLE]         = "idle",
15         [FW_UPLOAD_PROG_RECEIVING]    = "receiving",
16         [FW_UPLOAD_PROG_PREPARING]    = "preparing",
17         [FW_UPLOAD_PROG_TRANSFERRING] = "transferring",
18         [FW_UPLOAD_PROG_PROGRAMMING]  = "programming"
19 };
20
21 static const char * const fw_upload_err_str[] = {
22         [FW_UPLOAD_ERR_NONE]         = "none",
23         [FW_UPLOAD_ERR_HW_ERROR]     = "hw-error",
24         [FW_UPLOAD_ERR_TIMEOUT]      = "timeout",
25         [FW_UPLOAD_ERR_CANCELED]     = "user-abort",
26         [FW_UPLOAD_ERR_BUSY]         = "device-busy",
27         [FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size",
28         [FW_UPLOAD_ERR_RW_ERROR]     = "read-write-error",
29         [FW_UPLOAD_ERR_WEAROUT]      = "flash-wearout",
30         [FW_UPLOAD_ERR_FW_INVALID]   = "firmware-invalid",
31 };
32
33 static const char *fw_upload_progress(struct device *dev,
34                                       enum fw_upload_prog prog)
35 {
36         const char *status = "unknown-status";
37
38         if (prog < FW_UPLOAD_PROG_MAX)
39                 status = fw_upload_prog_str[prog];
40         else
41                 dev_err(dev, "Invalid status during secure update: %d\n", prog);
42
43         return status;
44 }
45
46 static const char *fw_upload_error(struct device *dev,
47                                    enum fw_upload_err err_code)
48 {
49         const char *error = "unknown-error";
50
51         if (err_code < FW_UPLOAD_ERR_MAX)
52                 error = fw_upload_err_str[err_code];
53         else
54                 dev_err(dev, "Invalid error code during secure update: %d\n",
55                         err_code);
56
57         return error;
58 }
59
60 static ssize_t
61 status_show(struct device *dev, struct device_attribute *attr, char *buf)
62 {
63         struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv;
64
65         return sysfs_emit(buf, "%s\n", fw_upload_progress(dev, fwlp->progress));
66 }
67 DEVICE_ATTR_RO(status);
68
69 static ssize_t
70 error_show(struct device *dev, struct device_attribute *attr, char *buf)
71 {
72         struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv;
73         int ret;
74
75         mutex_lock(&fwlp->lock);
76
77         if (fwlp->progress != FW_UPLOAD_PROG_IDLE)
78                 ret = -EBUSY;
79         else if (!fwlp->err_code)
80                 ret = 0;
81         else
82                 ret = sysfs_emit(buf, "%s:%s\n",
83                                  fw_upload_progress(dev, fwlp->err_progress),
84                                  fw_upload_error(dev, fwlp->err_code));
85
86         mutex_unlock(&fwlp->lock);
87
88         return ret;
89 }
90 DEVICE_ATTR_RO(error);
91
92 static ssize_t cancel_store(struct device *dev, struct device_attribute *attr,
93                             const char *buf, size_t count)
94 {
95         struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv;
96         int ret = count;
97         bool cancel;
98
99         if (kstrtobool(buf, &cancel) || !cancel)
100                 return -EINVAL;
101
102         mutex_lock(&fwlp->lock);
103         if (fwlp->progress == FW_UPLOAD_PROG_IDLE)
104                 ret = -ENODEV;
105
106         fwlp->ops->cancel(fwlp->fw_upload);
107         mutex_unlock(&fwlp->lock);
108
109         return ret;
110 }
111 DEVICE_ATTR_WO(cancel);
112
113 static ssize_t remaining_size_show(struct device *dev,
114                                    struct device_attribute *attr, char *buf)
115 {
116         struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv;
117
118         return sysfs_emit(buf, "%u\n", fwlp->remaining_size);
119 }
120 DEVICE_ATTR_RO(remaining_size);
121
122 umode_t
123 fw_upload_is_visible(struct kobject *kobj, struct attribute *attr, int n)
124 {
125         static struct fw_sysfs *fw_sysfs;
126
127         fw_sysfs = to_fw_sysfs(kobj_to_dev(kobj));
128
129         if (fw_sysfs->fw_upload_priv || attr == &dev_attr_loading.attr)
130                 return attr->mode;
131
132         return 0;
133 }
134
135 static void fw_upload_update_progress(struct fw_upload_priv *fwlp,
136                                       enum fw_upload_prog new_progress)
137 {
138         mutex_lock(&fwlp->lock);
139         fwlp->progress = new_progress;
140         mutex_unlock(&fwlp->lock);
141 }
142
143 static void fw_upload_set_error(struct fw_upload_priv *fwlp,
144                                 enum fw_upload_err err_code)
145 {
146         mutex_lock(&fwlp->lock);
147         fwlp->err_progress = fwlp->progress;
148         fwlp->err_code = err_code;
149         mutex_unlock(&fwlp->lock);
150 }
151
152 static void fw_upload_prog_complete(struct fw_upload_priv *fwlp)
153 {
154         mutex_lock(&fwlp->lock);
155         fwlp->progress = FW_UPLOAD_PROG_IDLE;
156         mutex_unlock(&fwlp->lock);
157 }
158
159 static void fw_upload_main(struct work_struct *work)
160 {
161         struct fw_upload_priv *fwlp;
162         struct fw_sysfs *fw_sysfs;
163         u32 written = 0, offset = 0;
164         enum fw_upload_err ret;
165         struct device *fw_dev;
166         struct fw_upload *fwl;
167
168         fwlp = container_of(work, struct fw_upload_priv, work);
169         fwl = fwlp->fw_upload;
170         fw_sysfs = (struct fw_sysfs *)fwl->priv;
171         fw_dev = &fw_sysfs->dev;
172
173         fw_upload_update_progress(fwlp, FW_UPLOAD_PROG_PREPARING);
174         ret = fwlp->ops->prepare(fwl, fwlp->data, fwlp->remaining_size);
175         if (ret != FW_UPLOAD_ERR_NONE) {
176                 fw_upload_set_error(fwlp, ret);
177                 goto putdev_exit;
178         }
179
180         fw_upload_update_progress(fwlp, FW_UPLOAD_PROG_TRANSFERRING);
181         while (fwlp->remaining_size) {
182                 ret = fwlp->ops->write(fwl, fwlp->data, offset,
183                                         fwlp->remaining_size, &written);
184                 if (ret != FW_UPLOAD_ERR_NONE || !written) {
185                         if (ret == FW_UPLOAD_ERR_NONE) {
186                                 dev_warn(fw_dev, "write-op wrote zero data\n");
187                                 ret = FW_UPLOAD_ERR_RW_ERROR;
188                         }
189                         fw_upload_set_error(fwlp, ret);
190                         goto done;
191                 }
192
193                 fwlp->remaining_size -= written;
194                 offset += written;
195         }
196
197         fw_upload_update_progress(fwlp, FW_UPLOAD_PROG_PROGRAMMING);
198         ret = fwlp->ops->poll_complete(fwl);
199         if (ret != FW_UPLOAD_ERR_NONE)
200                 fw_upload_set_error(fwlp, ret);
201
202 done:
203         if (fwlp->ops->cleanup)
204                 fwlp->ops->cleanup(fwl);
205
206 putdev_exit:
207         put_device(fw_dev->parent);
208
209         /*
210          * Note: fwlp->remaining_size is left unmodified here to provide
211          * additional information on errors. It will be reinitialized when
212          * the next firmeware upload begins.
213          */
214         mutex_lock(&fw_lock);
215         fw_free_paged_buf(fw_sysfs->fw_priv);
216         fw_state_init(fw_sysfs->fw_priv);
217         mutex_unlock(&fw_lock);
218         fwlp->data = NULL;
219         fw_upload_prog_complete(fwlp);
220 }
221
222 /*
223  * Start a worker thread to upload data to the parent driver.
224  * Must be called with fw_lock held.
225  */
226 int fw_upload_start(struct fw_sysfs *fw_sysfs)
227 {
228         struct fw_priv *fw_priv = fw_sysfs->fw_priv;
229         struct device *fw_dev = &fw_sysfs->dev;
230         struct fw_upload_priv *fwlp;
231
232         if (!fw_sysfs->fw_upload_priv)
233                 return 0;
234
235         if (!fw_priv->size) {
236                 fw_free_paged_buf(fw_priv);
237                 fw_state_init(fw_sysfs->fw_priv);
238                 return 0;
239         }
240
241         fwlp = fw_sysfs->fw_upload_priv;
242         mutex_lock(&fwlp->lock);
243
244         /* Do not interfere with an on-going fw_upload */
245         if (fwlp->progress != FW_UPLOAD_PROG_IDLE) {
246                 mutex_unlock(&fwlp->lock);
247                 return -EBUSY;
248         }
249
250         get_device(fw_dev->parent); /* released in fw_upload_main */
251
252         fwlp->progress = FW_UPLOAD_PROG_RECEIVING;
253         fwlp->err_code = 0;
254         fwlp->remaining_size = fw_priv->size;
255         fwlp->data = fw_priv->data;
256
257         pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n",
258                  __func__, fw_priv->fw_name,
259                  fw_priv, fw_priv->data,
260                  (unsigned int)fw_priv->size);
261
262         queue_work(system_long_wq, &fwlp->work);
263         mutex_unlock(&fwlp->lock);
264
265         return 0;
266 }
267
268 void fw_upload_free(struct fw_sysfs *fw_sysfs)
269 {
270         struct fw_upload_priv *fw_upload_priv = fw_sysfs->fw_upload_priv;
271
272         free_fw_priv(fw_sysfs->fw_priv);
273         kfree(fw_upload_priv->fw_upload);
274         kfree(fw_upload_priv);
275 }
276
277 /**
278  * firmware_upload_register() - register for the firmware upload sysfs API
279  * @module: kernel module of this device
280  * @parent: parent device instantiating firmware upload
281  * @name: firmware name to be associated with this device
282  * @ops: pointer to structure of firmware upload ops
283  * @dd_handle: pointer to parent driver private data
284  *
285  *      @name must be unique among all users of firmware upload. The firmware
286  *      sysfs files for this device will be found at /sys/class/firmware/@name.
287  *
288  *      Return: struct fw_upload pointer or ERR_PTR()
289  *
290  **/
291 struct fw_upload *
292 firmware_upload_register(struct module *module, struct device *parent,
293                          const char *name, const struct fw_upload_ops *ops,
294                          void *dd_handle)
295 {
296         u32 opt_flags = FW_OPT_NOCACHE;
297         struct fw_upload *fw_upload;
298         struct fw_upload_priv *fw_upload_priv;
299         struct fw_sysfs *fw_sysfs;
300         struct fw_priv *fw_priv;
301         struct device *fw_dev;
302         int ret;
303
304         if (!name || name[0] == '\0')
305                 return ERR_PTR(-EINVAL);
306
307         if (!ops || !ops->cancel || !ops->prepare ||
308             !ops->write || !ops->poll_complete) {
309                 dev_err(parent, "Attempt to register without all required ops\n");
310                 return ERR_PTR(-EINVAL);
311         }
312
313         if (!try_module_get(module))
314                 return ERR_PTR(-EFAULT);
315
316         fw_upload = kzalloc(sizeof(*fw_upload), GFP_KERNEL);
317         if (!fw_upload) {
318                 ret = -ENOMEM;
319                 goto exit_module_put;
320         }
321
322         fw_upload_priv = kzalloc(sizeof(*fw_upload_priv), GFP_KERNEL);
323         if (!fw_upload_priv) {
324                 ret = -ENOMEM;
325                 goto free_fw_upload;
326         }
327
328         fw_upload_priv->fw_upload = fw_upload;
329         fw_upload_priv->ops = ops;
330         mutex_init(&fw_upload_priv->lock);
331         fw_upload_priv->module = module;
332         fw_upload_priv->name = name;
333         fw_upload_priv->err_code = 0;
334         fw_upload_priv->progress = FW_UPLOAD_PROG_IDLE;
335         INIT_WORK(&fw_upload_priv->work, fw_upload_main);
336         fw_upload->dd_handle = dd_handle;
337
338         fw_sysfs = fw_create_instance(NULL, name, parent, opt_flags);
339         if (IS_ERR(fw_sysfs)) {
340                 ret = PTR_ERR(fw_sysfs);
341                 goto free_fw_upload_priv;
342         }
343         fw_upload->priv = fw_sysfs;
344         fw_sysfs->fw_upload_priv = fw_upload_priv;
345         fw_dev = &fw_sysfs->dev;
346
347         ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv,  NULL, 0, 0,
348                                    FW_OPT_NOCACHE);
349         if (ret != 0) {
350                 if (ret > 0)
351                         ret = -EINVAL;
352                 goto free_fw_sysfs;
353         }
354         fw_priv->is_paged_buf = true;
355         fw_sysfs->fw_priv = fw_priv;
356
357         ret = device_add(fw_dev);
358         if (ret) {
359                 dev_err(fw_dev, "%s: device_register failed\n", __func__);
360                 put_device(fw_dev);
361                 goto exit_module_put;
362         }
363
364         return fw_upload;
365
366 free_fw_sysfs:
367         kfree(fw_sysfs);
368
369 free_fw_upload_priv:
370         kfree(fw_upload_priv);
371
372 free_fw_upload:
373         kfree(fw_upload);
374
375 exit_module_put:
376         module_put(module);
377
378         return ERR_PTR(ret);
379 }
380 EXPORT_SYMBOL_GPL(firmware_upload_register);
381
382 /**
383  * firmware_upload_unregister() - Unregister firmware upload interface
384  * @fw_upload: pointer to struct fw_upload
385  **/
386 void firmware_upload_unregister(struct fw_upload *fw_upload)
387 {
388         struct fw_sysfs *fw_sysfs = fw_upload->priv;
389         struct fw_upload_priv *fw_upload_priv = fw_sysfs->fw_upload_priv;
390         struct module *module = fw_upload_priv->module;
391
392         mutex_lock(&fw_upload_priv->lock);
393         if (fw_upload_priv->progress == FW_UPLOAD_PROG_IDLE) {
394                 mutex_unlock(&fw_upload_priv->lock);
395                 goto unregister;
396         }
397
398         fw_upload_priv->ops->cancel(fw_upload);
399         mutex_unlock(&fw_upload_priv->lock);
400
401         /* Ensure lower-level device-driver is finished */
402         flush_work(&fw_upload_priv->work);
403
404 unregister:
405         device_unregister(&fw_sysfs->dev);
406         module_put(module);
407 }
408 EXPORT_SYMBOL_GPL(firmware_upload_unregister);
This page took 0.058953 seconds and 4 git commands to generate.