132e913d7SJoe Talbott /*-
232e913d7SJoe Talbott * Copyright (c) 2005-2008, Sam Leffler <sam@errno.com>
332e913d7SJoe Talbott * All rights reserved.
432e913d7SJoe Talbott *
532e913d7SJoe Talbott * Redistribution and use in source and binary forms, with or without
632e913d7SJoe Talbott * modification, are permitted provided that the following conditions
732e913d7SJoe Talbott * are met:
832e913d7SJoe Talbott * 1. Redistributions of source code must retain the above copyright
932e913d7SJoe Talbott * notice unmodified, this list of conditions, and the following
1032e913d7SJoe Talbott * disclaimer.
1132e913d7SJoe Talbott * 2. Redistributions in binary form must reproduce the above copyright
1232e913d7SJoe Talbott * notice, this list of conditions and the following disclaimer in the
1332e913d7SJoe Talbott * documentation and/or other materials provided with the distribution.
1432e913d7SJoe Talbott *
1532e913d7SJoe Talbott * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1632e913d7SJoe Talbott * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1732e913d7SJoe Talbott * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1832e913d7SJoe Talbott * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1932e913d7SJoe Talbott * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2032e913d7SJoe Talbott * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2132e913d7SJoe Talbott * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2232e913d7SJoe Talbott * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2332e913d7SJoe Talbott * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2432e913d7SJoe Talbott * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2589e93e6fSRui Paulo *
2689e93e6fSRui Paulo * $FreeBSD: src/sys/kern/subr_firmware.c,v 1.13.2.2 2010/02/11 18:34:06 mjacob Exp $
2732e913d7SJoe Talbott */
2832e913d7SJoe Talbott
2932e913d7SJoe Talbott #include <sys/param.h>
3032e913d7SJoe Talbott #include <sys/kernel.h>
3132e913d7SJoe Talbott #include <sys/malloc.h>
3232e913d7SJoe Talbott #include <sys/queue.h>
3332e913d7SJoe Talbott #include <sys/taskqueue.h>
3432e913d7SJoe Talbott #include <sys/systm.h>
3532e913d7SJoe Talbott #include <sys/lock.h>
3632e913d7SJoe Talbott #include <sys/spinlock.h>
3732e913d7SJoe Talbott #include <sys/spinlock2.h>
3832e913d7SJoe Talbott #include <sys/errno.h>
3932e913d7SJoe Talbott #include <sys/linker.h>
40d83c779aSSascha Wildner #include <sys/firmware.h>
41*2b3f93eaSMatthew Dillon #include <sys/caps.h>
4232e913d7SJoe Talbott #include <sys/proc.h>
4332e913d7SJoe Talbott #include <sys/module.h>
4432e913d7SJoe Talbott #include <sys/eventhandler.h>
4532e913d7SJoe Talbott
4632e913d7SJoe Talbott #include <sys/filedesc.h>
4732e913d7SJoe Talbott #include <sys/vnode.h>
4832e913d7SJoe Talbott
4932e913d7SJoe Talbott /*
5032e913d7SJoe Talbott * Loadable firmware support. See sys/sys/firmware.h and firmware(9)
5132e913d7SJoe Talbott * form more details on the subsystem.
5232e913d7SJoe Talbott *
5332e913d7SJoe Talbott * 'struct firmware' is the user-visible part of the firmware table.
5432e913d7SJoe Talbott * Additional internal information is stored in a 'struct priv_fw'
5532e913d7SJoe Talbott * (currently a static array). A slot is in use if FW_INUSE is true:
5632e913d7SJoe Talbott */
5732e913d7SJoe Talbott
5832e913d7SJoe Talbott #define FW_INUSE(p) ((p)->file != NULL || (p)->fw.name != NULL)
5932e913d7SJoe Talbott
6032e913d7SJoe Talbott /*
6132e913d7SJoe Talbott * fw.name != NULL when an image is registered; file != NULL for
6232e913d7SJoe Talbott * autoloaded images whose handling has not been completed.
6332e913d7SJoe Talbott *
6432e913d7SJoe Talbott * The state of a slot evolves as follows:
6532e913d7SJoe Talbott * firmware_register --> fw.name = image_name
6632e913d7SJoe Talbott * (autoloaded image) --> file = module reference
6732e913d7SJoe Talbott * firmware_unregister --> fw.name = NULL
6832e913d7SJoe Talbott * (unloadentry complete) --> file = NULL
6932e913d7SJoe Talbott *
7032e913d7SJoe Talbott * In order for the above to work, the 'file' field must remain
7132e913d7SJoe Talbott * unchanged in firmware_unregister().
7232e913d7SJoe Talbott *
7332e913d7SJoe Talbott * Images residing in the same module are linked to each other
7432e913d7SJoe Talbott * through the 'parent' argument of firmware_register().
7532e913d7SJoe Talbott * One image (typically, one with the same name as the module to let
7632e913d7SJoe Talbott * the autoloading mechanism work) is considered the parent image for
7732e913d7SJoe Talbott * all other images in the same module. Children affect the refcount
7832e913d7SJoe Talbott * on the parent image preventing improper unloading of the image itself.
7932e913d7SJoe Talbott */
8032e913d7SJoe Talbott
8132e913d7SJoe Talbott struct priv_fw {
8232e913d7SJoe Talbott int refcnt; /* reference count */
8332e913d7SJoe Talbott
8432e913d7SJoe Talbott /*
8532e913d7SJoe Talbott * parent entry, see above. Set on firmware_register(),
8632e913d7SJoe Talbott * cleared on firmware_unregister().
8732e913d7SJoe Talbott */
8832e913d7SJoe Talbott struct priv_fw *parent;
8932e913d7SJoe Talbott
9032e913d7SJoe Talbott int flags; /* record FIRMWARE_UNLOAD requests */
9132e913d7SJoe Talbott #define FW_UNLOAD 0x100
9232e913d7SJoe Talbott
9332e913d7SJoe Talbott /*
9432e913d7SJoe Talbott * 'file' is private info managed by the autoload/unload code.
9532e913d7SJoe Talbott * Set at the end of firmware_get(), cleared only in the
9632e913d7SJoe Talbott * firmware_unload_task, so the latter can depend on its value even
9732e913d7SJoe Talbott * while the lock is not held.
9832e913d7SJoe Talbott */
9932e913d7SJoe Talbott linker_file_t file; /* module file, if autoloaded */
10032e913d7SJoe Talbott
10132e913d7SJoe Talbott /*
10232e913d7SJoe Talbott * 'fw' is the externally visible image information.
10332e913d7SJoe Talbott * We do not make it the first field in priv_fw, to avoid the
10432e913d7SJoe Talbott * temptation of casting pointers to each other.
10532e913d7SJoe Talbott * Use PRIV_FW(fw) to get a pointer to the cointainer of fw.
10632e913d7SJoe Talbott * Beware, PRIV_FW does not work for a NULL pointer.
10732e913d7SJoe Talbott */
10832e913d7SJoe Talbott struct firmware fw; /* externally visible information */
10932e913d7SJoe Talbott };
11032e913d7SJoe Talbott
11132e913d7SJoe Talbott /*
11232e913d7SJoe Talbott * PRIV_FW returns the pointer to the container of struct firmware *x.
11332e913d7SJoe Talbott * Cast to intptr_t to override the 'const' attribute of x
11432e913d7SJoe Talbott */
11532e913d7SJoe Talbott #define PRIV_FW(x) ((struct priv_fw *) \
11632e913d7SJoe Talbott ((intptr_t)(x) - offsetof(struct priv_fw, fw)) )
11732e913d7SJoe Talbott
11832e913d7SJoe Talbott /*
11932e913d7SJoe Talbott * At the moment we use a static array as backing store for the registry.
12032e913d7SJoe Talbott * Should we move to a dynamic structure, keep in mind that we cannot
12132e913d7SJoe Talbott * reallocate the array because pointers are held externally.
12232e913d7SJoe Talbott * A list may work, though.
12332e913d7SJoe Talbott */
12432e913d7SJoe Talbott #define FIRMWARE_MAX 30
12532e913d7SJoe Talbott static struct priv_fw firmware_table[FIRMWARE_MAX];
12632e913d7SJoe Talbott
12732e913d7SJoe Talbott /*
12832e913d7SJoe Talbott * Firmware module operations are handled in a separate task as they
12932e913d7SJoe Talbott * might sleep and they require directory context to do i/o.
13032e913d7SJoe Talbott */
13132e913d7SJoe Talbott static struct taskqueue *firmware_tq;
13232e913d7SJoe Talbott static struct task firmware_unload_task;
13332e913d7SJoe Talbott
13432e913d7SJoe Talbott /*
13563bae95bSSascha Wildner * This lock protects accesses to the firmware table.
13632e913d7SJoe Talbott */
13732e913d7SJoe Talbott static struct lock firmware_lock;
13832e913d7SJoe Talbott
13932e913d7SJoe Talbott /*
14032e913d7SJoe Talbott * Helper function to lookup a name.
14132e913d7SJoe Talbott * As a side effect, it sets the pointer to a free slot, if any.
14232e913d7SJoe Talbott * This way we can concentrate most of the registry scanning in
14332e913d7SJoe Talbott * this function, which makes it easier to replace the registry
14432e913d7SJoe Talbott * with some other data structure.
14532e913d7SJoe Talbott */
14632e913d7SJoe Talbott static struct priv_fw *
lookup(const char * name,struct priv_fw ** empty_slot)14732e913d7SJoe Talbott lookup(const char *name, struct priv_fw **empty_slot)
14832e913d7SJoe Talbott {
14932e913d7SJoe Talbott struct priv_fw *fp = NULL;
15032e913d7SJoe Talbott struct priv_fw *dummy;
15132e913d7SJoe Talbott int i;
15232e913d7SJoe Talbott
15332e913d7SJoe Talbott if (empty_slot == NULL)
15432e913d7SJoe Talbott empty_slot = &dummy;
15532e913d7SJoe Talbott *empty_slot = NULL;
15632e913d7SJoe Talbott for (i = 0; i < FIRMWARE_MAX; i++) {
15732e913d7SJoe Talbott fp = &firmware_table[i];
15832e913d7SJoe Talbott if (fp->fw.name != NULL && strcasecmp(name, fp->fw.name) == 0)
15932e913d7SJoe Talbott break;
16032e913d7SJoe Talbott else if (!FW_INUSE(fp))
16132e913d7SJoe Talbott *empty_slot = fp;
16232e913d7SJoe Talbott }
16332e913d7SJoe Talbott return (i < FIRMWARE_MAX ) ? fp : NULL;
16432e913d7SJoe Talbott }
16532e913d7SJoe Talbott
16632e913d7SJoe Talbott /*
16732e913d7SJoe Talbott * Register a firmware image with the specified name. The
16832e913d7SJoe Talbott * image name must not already be registered. If this is a
16932e913d7SJoe Talbott * subimage then parent refers to a previously registered
17032e913d7SJoe Talbott * image that this should be associated with.
17132e913d7SJoe Talbott */
17232e913d7SJoe Talbott const struct firmware *
firmware_register(const char * imagename,const void * data,size_t datasize,unsigned int version,const struct firmware * parent)17332e913d7SJoe Talbott firmware_register(const char *imagename, const void *data, size_t datasize,
17432e913d7SJoe Talbott unsigned int version, const struct firmware *parent)
17532e913d7SJoe Talbott {
17632e913d7SJoe Talbott struct priv_fw *match, *frp;
17732e913d7SJoe Talbott
17832e913d7SJoe Talbott lockmgr(&firmware_lock, LK_EXCLUSIVE);
17932e913d7SJoe Talbott /*
18032e913d7SJoe Talbott * Do a lookup to make sure the name is unique or find a free slot.
18132e913d7SJoe Talbott */
18232e913d7SJoe Talbott match = lookup(imagename, &frp);
18332e913d7SJoe Talbott if (match != NULL) {
18432e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
18532e913d7SJoe Talbott kprintf("%s: image %s already registered!\n",
18632e913d7SJoe Talbott __func__, imagename);
18732e913d7SJoe Talbott return NULL;
18832e913d7SJoe Talbott }
18932e913d7SJoe Talbott if (frp == NULL) {
19032e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
19132e913d7SJoe Talbott kprintf("%s: cannot register image %s, firmware table full!\n",
19232e913d7SJoe Talbott __func__, imagename);
19332e913d7SJoe Talbott return NULL;
19432e913d7SJoe Talbott }
1958075c3b8SSascha Wildner bzero(frp, sizeof(*frp)); /* start from a clean record */
19632e913d7SJoe Talbott frp->fw.name = imagename;
19732e913d7SJoe Talbott frp->fw.data = data;
19832e913d7SJoe Talbott frp->fw.datasize = datasize;
19932e913d7SJoe Talbott frp->fw.version = version;
20032e913d7SJoe Talbott if (parent != NULL) {
20132e913d7SJoe Talbott frp->parent = PRIV_FW(parent);
20232e913d7SJoe Talbott frp->parent->refcnt++;
20332e913d7SJoe Talbott }
20432e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
20532e913d7SJoe Talbott if (bootverbose)
20632e913d7SJoe Talbott kprintf("firmware: '%s' version %u: %zu bytes loaded at %p\n",
20732e913d7SJoe Talbott imagename, version, datasize, data);
20832e913d7SJoe Talbott return &frp->fw;
20932e913d7SJoe Talbott }
21032e913d7SJoe Talbott
21132e913d7SJoe Talbott /*
21232e913d7SJoe Talbott * Unregister/remove a firmware image. If there are outstanding
21332e913d7SJoe Talbott * references an error is returned and the image is not removed
21432e913d7SJoe Talbott * from the registry.
21532e913d7SJoe Talbott */
21632e913d7SJoe Talbott int
firmware_unregister(const char * imagename)21732e913d7SJoe Talbott firmware_unregister(const char *imagename)
21832e913d7SJoe Talbott {
21932e913d7SJoe Talbott struct priv_fw *fp;
22032e913d7SJoe Talbott int err;
22132e913d7SJoe Talbott
22232e913d7SJoe Talbott lockmgr(&firmware_lock, LK_EXCLUSIVE);
22332e913d7SJoe Talbott fp = lookup(imagename, NULL);
22432e913d7SJoe Talbott if (fp == NULL) {
22532e913d7SJoe Talbott /*
22632e913d7SJoe Talbott * It is ok for the lookup to fail; this can happen
22732e913d7SJoe Talbott * when a module is unloaded on last reference and the
22832e913d7SJoe Talbott * module unload handler unregister's each of it's
22932e913d7SJoe Talbott * firmware images.
23032e913d7SJoe Talbott */
23132e913d7SJoe Talbott err = 0;
23232e913d7SJoe Talbott } else if (fp->refcnt != 0) { /* cannot unregister */
23332e913d7SJoe Talbott err = EBUSY;
23432e913d7SJoe Talbott } else {
23532e913d7SJoe Talbott linker_file_t x = fp->file; /* save value */
23632e913d7SJoe Talbott
23732e913d7SJoe Talbott if (fp->parent != NULL) /* release parent reference */
23832e913d7SJoe Talbott fp->parent->refcnt--;
23932e913d7SJoe Talbott /*
24032e913d7SJoe Talbott * Clear the whole entry with bzero to make sure we
24132e913d7SJoe Talbott * do not forget anything. Then restore 'file' which is
24232e913d7SJoe Talbott * non-null for autoloaded images.
24332e913d7SJoe Talbott */
24432e913d7SJoe Talbott bzero(fp, sizeof(struct priv_fw));
24532e913d7SJoe Talbott fp->file = x;
24632e913d7SJoe Talbott err = 0;
24732e913d7SJoe Talbott }
24832e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
24932e913d7SJoe Talbott return err;
25032e913d7SJoe Talbott }
25132e913d7SJoe Talbott
25232e913d7SJoe Talbott static void
loadimage(void * arg,int npending)25332e913d7SJoe Talbott loadimage(void *arg, int npending)
25432e913d7SJoe Talbott {
25589e93e6fSRui Paulo #ifdef notyet
25632e913d7SJoe Talbott struct thread *td = curthread;
25789e93e6fSRui Paulo #endif
25832e913d7SJoe Talbott char *imagename = arg;
25932e913d7SJoe Talbott struct priv_fw *fp;
26032e913d7SJoe Talbott linker_file_t result;
26132e913d7SJoe Talbott int error;
26232e913d7SJoe Talbott
26332e913d7SJoe Talbott /* synchronize with the thread that dispatched us */
26432e913d7SJoe Talbott lockmgr(&firmware_lock, LK_EXCLUSIVE);
26532e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
26632e913d7SJoe Talbott
26732e913d7SJoe Talbott /* JAT
26832e913d7SJoe Talbott if (td->td_proc->p_fd->fd_rdir == NULL) {
26932e913d7SJoe Talbott kprintf("%s: root not mounted yet, no way to load image\n",
27032e913d7SJoe Talbott imagename);
27132e913d7SJoe Talbott goto done;
27232e913d7SJoe Talbott }
27332e913d7SJoe Talbott */
27432e913d7SJoe Talbott error = linker_reference_module(imagename, NULL, &result);
27532e913d7SJoe Talbott if (error != 0) {
27632e913d7SJoe Talbott kprintf("%s: could not load firmware image, error %d\n",
27732e913d7SJoe Talbott imagename, error);
27832e913d7SJoe Talbott goto done;
27932e913d7SJoe Talbott }
28032e913d7SJoe Talbott
28132e913d7SJoe Talbott lockmgr(&firmware_lock, LK_EXCLUSIVE);
28232e913d7SJoe Talbott fp = lookup(imagename, NULL);
28332e913d7SJoe Talbott if (fp == NULL || fp->file != NULL) {
28432e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
28532e913d7SJoe Talbott if (fp == NULL)
28632e913d7SJoe Talbott kprintf("%s: firmware image loaded, "
28732e913d7SJoe Talbott "but did not register\n", imagename);
28832e913d7SJoe Talbott (void) linker_release_module(imagename, NULL, NULL);
28932e913d7SJoe Talbott goto done;
29032e913d7SJoe Talbott }
29132e913d7SJoe Talbott fp->file = result; /* record the module identity */
29232e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
29332e913d7SJoe Talbott done:
29432e913d7SJoe Talbott wakeup_one(imagename); /* we're done */
29532e913d7SJoe Talbott }
29632e913d7SJoe Talbott
29732e913d7SJoe Talbott /*
29832e913d7SJoe Talbott * Lookup and potentially load the specified firmware image.
29932e913d7SJoe Talbott * If the firmware is not found in the registry, try to load a kernel
30032e913d7SJoe Talbott * module named as the image name.
30132e913d7SJoe Talbott * If the firmware is located, a reference is returned. The caller must
30232e913d7SJoe Talbott * release this reference for the image to be eligible for removal/unload.
30332e913d7SJoe Talbott */
30432e913d7SJoe Talbott const struct firmware *
firmware_get(const char * imagename)30532e913d7SJoe Talbott firmware_get(const char *imagename)
30632e913d7SJoe Talbott {
30732e913d7SJoe Talbott struct task fwload_task;
30832e913d7SJoe Talbott struct priv_fw *fp;
30932e913d7SJoe Talbott
31032e913d7SJoe Talbott lockmgr(&firmware_lock, LK_EXCLUSIVE);
31132e913d7SJoe Talbott fp = lookup(imagename, NULL);
31232e913d7SJoe Talbott if (fp != NULL)
31332e913d7SJoe Talbott goto found;
31432e913d7SJoe Talbott /*
31532e913d7SJoe Talbott * Image not present, try to load the module holding it.
31632e913d7SJoe Talbott */
317*2b3f93eaSMatthew Dillon if (caps_priv_check_self(SYSCAP_NOKLD) != 0 || securelevel > 0) {
31832e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
31932e913d7SJoe Talbott kprintf("%s: insufficient privileges to "
32032e913d7SJoe Talbott "load firmware image %s\n", __func__, imagename);
32132e913d7SJoe Talbott return NULL;
32232e913d7SJoe Talbott }
32332e913d7SJoe Talbott /*
32432e913d7SJoe Talbott * Defer load to a thread with known context. linker_reference_module
32532e913d7SJoe Talbott * may do filesystem i/o which requires root & current dirs, etc.
32632e913d7SJoe Talbott * Also we must not hold any lock's over this call which is problematic.
32732e913d7SJoe Talbott */
32832e913d7SJoe Talbott if (!cold) {
32932e913d7SJoe Talbott TASK_INIT(&fwload_task, 0, loadimage, __DECONST(void *,
33032e913d7SJoe Talbott imagename));
33132e913d7SJoe Talbott taskqueue_enqueue(firmware_tq, &fwload_task);
33232e913d7SJoe Talbott lksleep(__DECONST(void *, imagename), &firmware_lock, 0,
33332e913d7SJoe Talbott "fwload", 0);
33432e913d7SJoe Talbott }
33532e913d7SJoe Talbott /*
33632e913d7SJoe Talbott * After attempting to load the module, see if the image is registered.
33732e913d7SJoe Talbott */
33832e913d7SJoe Talbott fp = lookup(imagename, NULL);
33932e913d7SJoe Talbott if (fp == NULL) {
34032e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
34132e913d7SJoe Talbott return NULL;
34232e913d7SJoe Talbott }
34332e913d7SJoe Talbott found: /* common exit point on success */
34432e913d7SJoe Talbott fp->refcnt++;
34532e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
34632e913d7SJoe Talbott return &fp->fw;
34732e913d7SJoe Talbott }
34832e913d7SJoe Talbott
34932e913d7SJoe Talbott /*
35032e913d7SJoe Talbott * Release a reference to a firmware image returned by firmware_get.
35132e913d7SJoe Talbott * The caller may specify, with the FIRMWARE_UNLOAD flag, its desire
35232e913d7SJoe Talbott * to release the resource, but the flag is only advisory.
35332e913d7SJoe Talbott *
35432e913d7SJoe Talbott * If this is the last reference to the firmware image, and this is an
35532e913d7SJoe Talbott * autoloaded module, wake up the firmware_unload_task to figure out
35632e913d7SJoe Talbott * what to do with the associated module.
35732e913d7SJoe Talbott */
35832e913d7SJoe Talbott void
firmware_put(const struct firmware * p,int flags)35932e913d7SJoe Talbott firmware_put(const struct firmware *p, int flags)
36032e913d7SJoe Talbott {
36132e913d7SJoe Talbott struct priv_fw *fp = PRIV_FW(p);
36232e913d7SJoe Talbott
36332e913d7SJoe Talbott lockmgr(&firmware_lock, LK_EXCLUSIVE);
36432e913d7SJoe Talbott fp->refcnt--;
36532e913d7SJoe Talbott if (fp->refcnt == 0) {
36632e913d7SJoe Talbott if (flags & FIRMWARE_UNLOAD)
36732e913d7SJoe Talbott fp->flags |= FW_UNLOAD;
36832e913d7SJoe Talbott if (fp->file)
36932e913d7SJoe Talbott taskqueue_enqueue(firmware_tq, &firmware_unload_task);
37032e913d7SJoe Talbott }
37132e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
37232e913d7SJoe Talbott }
37332e913d7SJoe Talbott
37489e93e6fSRui Paulo #ifdef notyet
37532e913d7SJoe Talbott /*
37632e913d7SJoe Talbott * Setup directory state for the firmware_tq thread so we can do i/o.
37732e913d7SJoe Talbott */
37832e913d7SJoe Talbott static void
set_rootvnode(void * arg,int npending)37932e913d7SJoe Talbott set_rootvnode(void *arg, int npending)
38032e913d7SJoe Talbott {
38132e913d7SJoe Talbott struct thread *td = curthread;
38232e913d7SJoe Talbott struct proc *p = td->td_proc;
38332e913d7SJoe Talbott
38432e913d7SJoe Talbott
38532e913d7SJoe Talbott #if 0
38632e913d7SJoe Talbott spin_lock_wr(&p->p_fd->fd_spin);
38732e913d7SJoe Talbott if (p->p_fd->fd_cdir == NULL) {
38832e913d7SJoe Talbott p->p_fd->fd_cdir = rootvnode;
38932e913d7SJoe Talbott vref(rootvnode);
39032e913d7SJoe Talbott }
39132e913d7SJoe Talbott if (p->p_fd->fd_rdir == NULL) {
39232e913d7SJoe Talbott p->p_fd->fd_rdir = rootvnode;
39332e913d7SJoe Talbott vref(rootvnode);
39432e913d7SJoe Talbott }
39532e913d7SJoe Talbott spin_unlock_wr(&p->p_fd->fd_spin);
39632e913d7SJoe Talbott
39732e913d7SJoe Talbott kfree(arg, M_TEMP);
39832e913d7SJoe Talbott #endif
39932e913d7SJoe Talbott }
40032e913d7SJoe Talbott
40132e913d7SJoe Talbott /*
40232e913d7SJoe Talbott * Event handler called on mounting of /; bounce a task
40332e913d7SJoe Talbott * into the task queue thread to setup it's directories.
40432e913d7SJoe Talbott */
40532e913d7SJoe Talbott static void
firmware_mountroot(void * arg)40632e913d7SJoe Talbott firmware_mountroot(void *arg)
40732e913d7SJoe Talbott {
40832e913d7SJoe Talbott struct task *setroot_task;
40932e913d7SJoe Talbott
41032e913d7SJoe Talbott setroot_task = kmalloc(sizeof(struct task), M_TEMP, M_NOWAIT);
41132e913d7SJoe Talbott if (setroot_task != NULL) {
41232e913d7SJoe Talbott TASK_INIT(setroot_task, 0, set_rootvnode, setroot_task);
41332e913d7SJoe Talbott taskqueue_enqueue(firmware_tq, setroot_task);
41432e913d7SJoe Talbott } else
41532e913d7SJoe Talbott kprintf("%s: no memory for task!\n", __func__);
41632e913d7SJoe Talbott }
41732e913d7SJoe Talbott EVENTHANDLER_DECLARE(mountroot, firmware_mountroot);
41832e913d7SJoe Talbott #endif
41932e913d7SJoe Talbott
42032e913d7SJoe Talbott /*
42132e913d7SJoe Talbott * The body of the task in charge of unloading autoloaded modules
42232e913d7SJoe Talbott * that are not needed anymore.
42332e913d7SJoe Talbott * Images can be cross-linked so we may need to make multiple passes,
42432e913d7SJoe Talbott * but the time we spend in the loop is bounded because we clear entries
42532e913d7SJoe Talbott * as we touch them.
42632e913d7SJoe Talbott */
42732e913d7SJoe Talbott static void
unloadentry(void * unused1,int unused2)42832e913d7SJoe Talbott unloadentry(void *unused1, int unused2)
42932e913d7SJoe Talbott {
43032e913d7SJoe Talbott int limit = FIRMWARE_MAX;
43132e913d7SJoe Talbott int i; /* current cycle */
43232e913d7SJoe Talbott
43332e913d7SJoe Talbott lockmgr(&firmware_lock, LK_EXCLUSIVE);
43432e913d7SJoe Talbott /*
43532e913d7SJoe Talbott * Scan the table. limit is set to make sure we make another
43632e913d7SJoe Talbott * full sweep after matching an entry that requires unloading.
43732e913d7SJoe Talbott */
43832e913d7SJoe Talbott for (i = 0; i < limit; i++) {
43932e913d7SJoe Talbott struct priv_fw *fp;
44032e913d7SJoe Talbott int err;
44132e913d7SJoe Talbott
44232e913d7SJoe Talbott fp = &firmware_table[i % FIRMWARE_MAX];
44332e913d7SJoe Talbott if (fp->fw.name == NULL || fp->file == NULL ||
44432e913d7SJoe Talbott fp->refcnt != 0 || (fp->flags & FW_UNLOAD) == 0)
44532e913d7SJoe Talbott continue;
44632e913d7SJoe Talbott
44732e913d7SJoe Talbott /*
44832e913d7SJoe Talbott * Found an entry. Now:
44932e913d7SJoe Talbott * 1. bump up limit to make sure we make another full round;
45032e913d7SJoe Talbott * 2. clear FW_UNLOAD so we don't try this entry again.
45132e913d7SJoe Talbott * 3. release the lock while trying to unload the module.
45232e913d7SJoe Talbott * 'file' remains set so that the entry cannot be reused
45332e913d7SJoe Talbott * in the meantime (it also means that fp->file will
45432e913d7SJoe Talbott * not change while we release the lock).
45532e913d7SJoe Talbott */
45632e913d7SJoe Talbott limit = i + FIRMWARE_MAX; /* make another full round */
45732e913d7SJoe Talbott fp->flags &= ~FW_UNLOAD; /* do not try again */
45832e913d7SJoe Talbott
45932e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
46032e913d7SJoe Talbott err = linker_release_module(NULL, NULL, fp->file);
46132e913d7SJoe Talbott lockmgr(&firmware_lock, LK_EXCLUSIVE);
46232e913d7SJoe Talbott
46332e913d7SJoe Talbott /*
46432e913d7SJoe Talbott * We rely on the module to call firmware_unregister()
46532e913d7SJoe Talbott * on unload to actually release the entry.
46632e913d7SJoe Talbott * If err = 0 we can drop our reference as the system
46732e913d7SJoe Talbott * accepted it. Otherwise unloading failed (e.g. the
46832e913d7SJoe Talbott * module itself gave an error) so our reference is
46932e913d7SJoe Talbott * still valid.
47032e913d7SJoe Talbott */
47132e913d7SJoe Talbott if (err == 0)
47232e913d7SJoe Talbott fp->file = NULL;
47332e913d7SJoe Talbott }
47432e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
47532e913d7SJoe Talbott }
47632e913d7SJoe Talbott
47732e913d7SJoe Talbott /*
47832e913d7SJoe Talbott * Module glue.
47932e913d7SJoe Talbott */
48032e913d7SJoe Talbott static int
firmware_modevent(module_t mod,int type,void * unused)48132e913d7SJoe Talbott firmware_modevent(module_t mod, int type, void *unused)
48232e913d7SJoe Talbott {
48332e913d7SJoe Talbott struct priv_fw *fp;
48432e913d7SJoe Talbott int i, err;
48532e913d7SJoe Talbott
48632e913d7SJoe Talbott switch (type) {
48732e913d7SJoe Talbott case MOD_LOAD:
48832e913d7SJoe Talbott TASK_INIT(&firmware_unload_task, 0, unloadentry, NULL);
48932e913d7SJoe Talbott lockinit(&firmware_lock, "firmware table", 0, LK_CANRECURSE);
49032e913d7SJoe Talbott firmware_tq = taskqueue_create("taskqueue_firmware", M_WAITOK,
49132e913d7SJoe Talbott taskqueue_thread_enqueue, &firmware_tq);
49232e913d7SJoe Talbott /* NB: use our own loop routine that sets up context */
49332e913d7SJoe Talbott (void) taskqueue_start_threads(&firmware_tq, 1, TDPRI_KERN_DAEMON,
49432e913d7SJoe Talbott -1, "firmware taskq");
49532e913d7SJoe Talbott if (rootvnode != NULL) {
49632e913d7SJoe Talbott /*
49732e913d7SJoe Talbott * Root is already mounted so we won't get an event;
49832e913d7SJoe Talbott * simulate one here.
49932e913d7SJoe Talbott */
50089e93e6fSRui Paulo #ifdef notyet
50132e913d7SJoe Talbott firmware_mountroot(NULL);
50232e913d7SJoe Talbott #endif
50332e913d7SJoe Talbott }
50432e913d7SJoe Talbott return 0;
50532e913d7SJoe Talbott
50632e913d7SJoe Talbott case MOD_UNLOAD:
50732e913d7SJoe Talbott /* request all autoloaded modules to be released */
50832e913d7SJoe Talbott lockmgr(&firmware_lock, LK_EXCLUSIVE);
50932e913d7SJoe Talbott for (i = 0; i < FIRMWARE_MAX; i++) {
51032e913d7SJoe Talbott fp = &firmware_table[i];
51132e913d7SJoe Talbott fp->flags |= FW_UNLOAD;
51232e913d7SJoe Talbott }
51332e913d7SJoe Talbott lockmgr(&firmware_lock, LK_RELEASE);
51432e913d7SJoe Talbott taskqueue_enqueue(firmware_tq, &firmware_unload_task);
51532e913d7SJoe Talbott taskqueue_drain(firmware_tq, &firmware_unload_task);
51632e913d7SJoe Talbott err = 0;
51732e913d7SJoe Talbott for (i = 0; i < FIRMWARE_MAX; i++) {
51832e913d7SJoe Talbott fp = &firmware_table[i];
51932e913d7SJoe Talbott if (fp->fw.name != NULL) {
52032e913d7SJoe Talbott kprintf("%s: image %p ref %d still active slot %d\n",
52132e913d7SJoe Talbott __func__, fp->fw.name,
52232e913d7SJoe Talbott fp->refcnt, i);
52332e913d7SJoe Talbott err = EINVAL;
52432e913d7SJoe Talbott }
52532e913d7SJoe Talbott }
52632e913d7SJoe Talbott if (err == 0)
52732e913d7SJoe Talbott taskqueue_free(firmware_tq);
52832e913d7SJoe Talbott return err;
52932e913d7SJoe Talbott }
53032e913d7SJoe Talbott return EINVAL;
53132e913d7SJoe Talbott }
53232e913d7SJoe Talbott
533d83c779aSSascha Wildner static moduledata_t firmware_mod = {
534d83c779aSSascha Wildner "firmware",
53532e913d7SJoe Talbott firmware_modevent,
53632e913d7SJoe Talbott NULL
53732e913d7SJoe Talbott };
538d83c779aSSascha Wildner DECLARE_MODULE(firmware, firmware_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
539d83c779aSSascha Wildner MODULE_VERSION(firmware, 1);
540