source: rtems-libbsd/freebsd/sys/kern/subr_firmware.c @ 66eb143

55-freebsd-126-freebsd-12
Last change on this file since 66eb143 was 66eb143, checked in by Christian Mauderer <Christian.Mauderer@…>, on 12/12/16 at 12:09:09

firmware: Port to RTEMS.

  • Property mode set to 100644
File size: 15.2 KB
Line 
1#include <machine/rtems-bsd-kernel-space.h>
2
3/*-
4 * Copyright (c) 2005-2008, Sam Leffler <sam@errno.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice unmodified, this list of conditions, and the following
12 *    disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <rtems/bsd/sys/param.h>
33#include <sys/kernel.h>
34#include <sys/malloc.h>
35#include <sys/queue.h>
36#include <sys/taskqueue.h>
37#include <sys/systm.h>
38#include <rtems/bsd/sys/lock.h>
39#include <sys/mutex.h>
40#include <rtems/bsd/sys/errno.h>
41#include <sys/linker.h>
42#include <sys/firmware.h>
43#include <sys/priv.h>
44#include <sys/proc.h>
45#include <sys/module.h>
46#include <sys/eventhandler.h>
47
48#include <sys/filedesc.h>
49#include <sys/vnode.h>
50
51/*
52 * Loadable firmware support. See sys/sys/firmware.h and firmware(9)
53 * form more details on the subsystem.
54 *
55 * 'struct firmware' is the user-visible part of the firmware table.
56 * Additional internal information is stored in a 'struct priv_fw'
57 * (currently a static array). A slot is in use if FW_INUSE is true:
58 */
59
60#define FW_INUSE(p)     ((p)->file != NULL || (p)->fw.name != NULL)
61
62/*
63 * fw.name != NULL when an image is registered; file != NULL for
64 * autoloaded images whose handling has not been completed.
65 *
66 * The state of a slot evolves as follows:
67 *      firmware_register       -->  fw.name = image_name
68 *      (autoloaded image)      -->  file = module reference
69 *      firmware_unregister     -->  fw.name = NULL
70 *      (unloadentry complete)  -->  file = NULL
71 *
72 * In order for the above to work, the 'file' field must remain
73 * unchanged in firmware_unregister().
74 *
75 * Images residing in the same module are linked to each other
76 * through the 'parent' argument of firmware_register().
77 * One image (typically, one with the same name as the module to let
78 * the autoloading mechanism work) is considered the parent image for
79 * all other images in the same module. Children affect the refcount
80 * on the parent image preventing improper unloading of the image itself.
81 */
82
83struct priv_fw {
84        int             refcnt;         /* reference count */
85
86        /*
87         * parent entry, see above. Set on firmware_register(),
88         * cleared on firmware_unregister().
89         */
90        struct priv_fw  *parent;
91
92        int             flags;  /* record FIRMWARE_UNLOAD requests */
93#define FW_UNLOAD       0x100
94
95        /*
96         * 'file' is private info managed by the autoload/unload code.
97         * Set at the end of firmware_get(), cleared only in the
98         * firmware_unload_task, so the latter can depend on its value even
99         * while the lock is not held.
100         */
101        linker_file_t   file;   /* module file, if autoloaded */
102
103        /*
104         * 'fw' is the externally visible image information.
105         * We do not make it the first field in priv_fw, to avoid the
106         * temptation of casting pointers to each other.
107         * Use PRIV_FW(fw) to get a pointer to the cointainer of fw.
108         * Beware, PRIV_FW does not work for a NULL pointer.
109         */
110        struct firmware fw;     /* externally visible information */
111};
112
113/*
114 * PRIV_FW returns the pointer to the container of struct firmware *x.
115 * Cast to intptr_t to override the 'const' attribute of x
116 */
117#define PRIV_FW(x)      ((struct priv_fw *)             \
118        ((intptr_t)(x) - offsetof(struct priv_fw, fw)) )
119
120/*
121 * At the moment we use a static array as backing store for the registry.
122 * Should we move to a dynamic structure, keep in mind that we cannot
123 * reallocate the array because pointers are held externally.
124 * A list may work, though.
125 */
126#define FIRMWARE_MAX    50
127static struct priv_fw firmware_table[FIRMWARE_MAX];
128
129/*
130 * Firmware module operations are handled in a separate task as they
131 * might sleep and they require directory context to do i/o.
132 */
133static struct taskqueue *firmware_tq;
134static struct task firmware_unload_task;
135
136/*
137 * This mutex protects accesses to the firmware table.
138 */
139static struct mtx firmware_mtx;
140MTX_SYSINIT(firmware, &firmware_mtx, "firmware table", MTX_DEF);
141
142/*
143 * Helper function to lookup a name.
144 * As a side effect, it sets the pointer to a free slot, if any.
145 * This way we can concentrate most of the registry scanning in
146 * this function, which makes it easier to replace the registry
147 * with some other data structure.
148 */
149static struct priv_fw *
150lookup(const char *name, struct priv_fw **empty_slot)
151{
152        struct priv_fw *fp = NULL;
153        struct priv_fw *dummy;
154        int i;
155
156        if (empty_slot == NULL)
157                empty_slot = &dummy;
158        *empty_slot = NULL;
159        for (i = 0; i < FIRMWARE_MAX; i++) {
160                fp = &firmware_table[i];
161                if (fp->fw.name != NULL && strcasecmp(name, fp->fw.name) == 0)
162                        break;
163                else if (!FW_INUSE(fp))
164                        *empty_slot = fp;
165        }
166        return (i < FIRMWARE_MAX ) ? fp : NULL;
167}
168
169/*
170 * Register a firmware image with the specified name.  The
171 * image name must not already be registered.  If this is a
172 * subimage then parent refers to a previously registered
173 * image that this should be associated with.
174 */
175const struct firmware *
176firmware_register(const char *imagename, const void *data, size_t datasize,
177    unsigned int version, const struct firmware *parent)
178{
179        struct priv_fw *match, *frp;
180        char *str;
181
182        str = strdup(imagename, M_TEMP);
183
184        mtx_lock(&firmware_mtx);
185        /*
186         * Do a lookup to make sure the name is unique or find a free slot.
187         */
188        match = lookup(imagename, &frp);
189        if (match != NULL) {
190                mtx_unlock(&firmware_mtx);
191                printf("%s: image %s already registered!\n",
192                        __func__, imagename);
193                free(str, M_TEMP);
194                return NULL;
195        }
196        if (frp == NULL) {
197                mtx_unlock(&firmware_mtx);
198                printf("%s: cannot register image %s, firmware table full!\n",
199                    __func__, imagename);
200                free(str, M_TEMP);
201                return NULL;
202        }
203        bzero(frp, sizeof(*frp));       /* start from a clean record */
204        frp->fw.name = str;
205        frp->fw.data = data;
206        frp->fw.datasize = datasize;
207        frp->fw.version = version;
208        if (parent != NULL)
209                frp->parent = PRIV_FW(parent);
210        mtx_unlock(&firmware_mtx);
211        if (bootverbose)
212                printf("firmware: '%s' version %u: %zu bytes loaded at %p\n",
213                    imagename, version, datasize, data);
214        return &frp->fw;
215}
216
217/*
218 * Unregister/remove a firmware image.  If there are outstanding
219 * references an error is returned and the image is not removed
220 * from the registry.
221 */
222int
223firmware_unregister(const char *imagename)
224{
225        struct priv_fw *fp;
226        int err;
227
228        mtx_lock(&firmware_mtx);
229        fp = lookup(imagename, NULL);
230        if (fp == NULL) {
231                /*
232                 * It is ok for the lookup to fail; this can happen
233                 * when a module is unloaded on last reference and the
234                 * module unload handler unregister's each of it's
235                 * firmware images.
236                 */
237                err = 0;
238        } else if (fp->refcnt != 0) {   /* cannot unregister */
239                err = EBUSY;
240        } else {
241                linker_file_t x = fp->file;     /* save value */
242
243                /*
244                 * Clear the whole entry with bzero to make sure we
245                 * do not forget anything. Then restore 'file' which is
246                 * non-null for autoloaded images.
247                 */
248                free((void *) (uintptr_t) fp->fw.name, M_TEMP);
249                bzero(fp, sizeof(struct priv_fw));
250                fp->file = x;
251                err = 0;
252        }
253        mtx_unlock(&firmware_mtx);
254        return err;
255}
256
257#ifndef __rtems__
258static void
259loadimage(void *arg, int npending)
260{
261        struct thread *td = curthread;
262        char *imagename = arg;
263        struct priv_fw *fp;
264        linker_file_t result;
265        int error;
266
267        /* synchronize with the thread that dispatched us */
268        mtx_lock(&firmware_mtx);
269        mtx_unlock(&firmware_mtx);
270
271        if (td->td_proc->p_fd->fd_rdir == NULL) {
272                printf("%s: root not mounted yet, no way to load image\n",
273                    imagename);
274                goto done;
275        }
276        error = linker_reference_module(imagename, NULL, &result);
277        if (error != 0) {
278                printf("%s: could not load firmware image, error %d\n",
279                    imagename, error);
280                goto done;
281        }
282
283        mtx_lock(&firmware_mtx);
284        fp = lookup(imagename, NULL);
285        if (fp == NULL || fp->file != NULL) {
286                mtx_unlock(&firmware_mtx);
287                if (fp == NULL)
288                        printf("%s: firmware image loaded, "
289                            "but did not register\n", imagename);
290                (void) linker_release_module(imagename, NULL, NULL);
291                goto done;
292        }
293        fp->file = result;      /* record the module identity */
294        mtx_unlock(&firmware_mtx);
295done:
296        wakeup_one(imagename);          /* we're done */
297}
298#endif /* __rtems__ */
299
300/*
301 * Lookup and potentially load the specified firmware image.
302 * If the firmware is not found in the registry, try to load a kernel
303 * module named as the image name.
304 * If the firmware is located, a reference is returned. The caller must
305 * release this reference for the image to be eligible for removal/unload.
306 */
307const struct firmware *
308firmware_get(const char *imagename)
309{
310#ifndef __rtems__
311        struct task fwload_task;
312        struct thread *td;
313#endif /* __rtems__ */
314        struct priv_fw *fp;
315
316        mtx_lock(&firmware_mtx);
317        fp = lookup(imagename, NULL);
318        if (fp != NULL)
319                goto found;
320#ifndef __rtems__
321        /*
322         * Image not present, try to load the module holding it.
323         */
324        td = curthread;
325        if (priv_check(td, PRIV_FIRMWARE_LOAD) != 0 ||
326            securelevel_gt(td->td_ucred, 0) != 0) {
327                mtx_unlock(&firmware_mtx);
328                printf("%s: insufficient privileges to "
329                    "load firmware image %s\n", __func__, imagename);
330                return NULL;
331        }
332        /*
333         * Defer load to a thread with known context.  linker_reference_module
334         * may do filesystem i/o which requires root & current dirs, etc.
335         * Also we must not hold any mtx's over this call which is problematic.
336         */
337        if (!cold) {
338                TASK_INIT(&fwload_task, 0, loadimage, __DECONST(void *,
339                    imagename));
340                taskqueue_enqueue(firmware_tq, &fwload_task);
341                msleep(__DECONST(void *, imagename), &firmware_mtx, 0,
342                    "fwload", 0);
343        }
344        /*
345         * After attempting to load the module, see if the image is registered.
346         */
347        fp = lookup(imagename, NULL);
348        if (fp == NULL) {
349                mtx_unlock(&firmware_mtx);
350                return NULL;
351        }
352#else /* __rtems__ */
353        return NULL;
354#endif /* __rtems__ */
355found:                          /* common exit point on success */
356        if (fp->refcnt == 0 && fp->parent != NULL)
357                fp->parent->refcnt++;
358        fp->refcnt++;
359        mtx_unlock(&firmware_mtx);
360        return &fp->fw;
361}
362
363/*
364 * Release a reference to a firmware image returned by firmware_get.
365 * The caller may specify, with the FIRMWARE_UNLOAD flag, its desire
366 * to release the resource, but the flag is only advisory.
367 *
368 * If this is the last reference to the firmware image, and this is an
369 * autoloaded module, wake up the firmware_unload_task to figure out
370 * what to do with the associated module.
371 */
372void
373firmware_put(const struct firmware *p, int flags)
374{
375        struct priv_fw *fp = PRIV_FW(p);
376
377        mtx_lock(&firmware_mtx);
378        fp->refcnt--;
379        if (fp->refcnt == 0) {
380                if (fp->parent != NULL)
381                        fp->parent->refcnt--;
382                if (flags & FIRMWARE_UNLOAD)
383                        fp->flags |= FW_UNLOAD;
384                if (fp->file)
385                        taskqueue_enqueue(firmware_tq, &firmware_unload_task);
386        }
387        mtx_unlock(&firmware_mtx);
388}
389
390#ifndef __rtems__
391/*
392 * Setup directory state for the firmware_tq thread so we can do i/o.
393 */
394static void
395set_rootvnode(void *arg, int npending)
396{
397
398        pwd_ensure_dirs();
399
400        free(arg, M_TEMP);
401}
402
403/*
404 * Event handler called on mounting of /; bounce a task
405 * into the task queue thread to setup it's directories.
406 */
407static void
408firmware_mountroot(void *arg)
409{
410        struct task *setroot_task;
411
412        setroot_task = malloc(sizeof(struct task), M_TEMP, M_NOWAIT);
413        if (setroot_task != NULL) {
414                TASK_INIT(setroot_task, 0, set_rootvnode, setroot_task);
415                taskqueue_enqueue(firmware_tq, setroot_task);
416        } else
417                printf("%s: no memory for task!\n", __func__);
418}
419EVENTHANDLER_DEFINE(mountroot, firmware_mountroot, NULL, 0);
420
421/*
422 * The body of the task in charge of unloading autoloaded modules
423 * that are not needed anymore.
424 * Images can be cross-linked so we may need to make multiple passes,
425 * but the time we spend in the loop is bounded because we clear entries
426 * as we touch them.
427 */
428static void
429unloadentry(void *unused1, int unused2)
430{
431        int limit = FIRMWARE_MAX;
432        int i;  /* current cycle */
433
434        mtx_lock(&firmware_mtx);
435        /*
436         * Scan the table. limit is set to make sure we make another
437         * full sweep after matching an entry that requires unloading.
438         */
439        for (i = 0; i < limit; i++) {
440                struct priv_fw *fp;
441                int err;
442
443                fp = &firmware_table[i % FIRMWARE_MAX];
444                if (fp->fw.name == NULL || fp->file == NULL ||
445                    fp->refcnt != 0 || (fp->flags & FW_UNLOAD) == 0)
446                        continue;
447
448                /*
449                 * Found an entry. Now:
450                 * 1. bump up limit to make sure we make another full round;
451                 * 2. clear FW_UNLOAD so we don't try this entry again.
452                 * 3. release the lock while trying to unload the module.
453                 * 'file' remains set so that the entry cannot be reused
454                 * in the meantime (it also means that fp->file will
455                 * not change while we release the lock).
456                 */
457                limit = i + FIRMWARE_MAX;       /* make another full round */
458                fp->flags &= ~FW_UNLOAD;        /* do not try again */
459
460                mtx_unlock(&firmware_mtx);
461                err = linker_release_module(NULL, NULL, fp->file);
462                mtx_lock(&firmware_mtx);
463
464                /*
465                 * We rely on the module to call firmware_unregister()
466                 * on unload to actually release the entry.
467                 * If err = 0 we can drop our reference as the system
468                 * accepted it. Otherwise unloading failed (e.g. the
469                 * module itself gave an error) so our reference is
470                 * still valid.
471                 */
472                if (err == 0)
473                        fp->file = NULL;
474        }
475        mtx_unlock(&firmware_mtx);
476}
477
478/*
479 * Module glue.
480 */
481static int
482firmware_modevent(module_t mod, int type, void *unused)
483{
484        struct priv_fw *fp;
485        int i, err;
486
487        switch (type) {
488        case MOD_LOAD:
489                TASK_INIT(&firmware_unload_task, 0, unloadentry, NULL);
490                firmware_tq = taskqueue_create("taskqueue_firmware", M_WAITOK,
491                    taskqueue_thread_enqueue, &firmware_tq);
492                /* NB: use our own loop routine that sets up context */
493                (void) taskqueue_start_threads(&firmware_tq, 1, PWAIT,
494                    "firmware taskq");
495                if (rootvnode != NULL) {
496                        /*
497                         * Root is already mounted so we won't get an event;
498                         * simulate one here.
499                         */
500                        firmware_mountroot(NULL);
501                }
502                return 0;
503
504        case MOD_UNLOAD:
505                /* request all autoloaded modules to be released */
506                mtx_lock(&firmware_mtx);
507                for (i = 0; i < FIRMWARE_MAX; i++) {
508                        fp = &firmware_table[i];
509                        fp->flags |= FW_UNLOAD;
510                }
511                mtx_unlock(&firmware_mtx);
512                taskqueue_enqueue(firmware_tq, &firmware_unload_task);
513                taskqueue_drain(firmware_tq, &firmware_unload_task);
514                err = 0;
515                for (i = 0; i < FIRMWARE_MAX; i++) {
516                        fp = &firmware_table[i];
517                        if (fp->fw.name != NULL) {
518                                printf("%s: image %p ref %d still active slot %d\n",
519                                        __func__, fp->fw.name,
520                                        fp->refcnt,  i);
521                                err = EINVAL;
522                        }
523                }
524                if (err == 0)
525                        taskqueue_free(firmware_tq);
526                return err;
527        }
528        return EINVAL;
529}
530
531static moduledata_t firmware_mod = {
532        "firmware",
533        firmware_modevent,
534        NULL
535};
536DECLARE_MODULE(firmware, firmware_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
537MODULE_VERSION(firmware, 1);
538#endif /* __rtems__ */
Note: See TracBrowser for help on using the repository browser.