xref: /dflybsd-src/sys/kern/subr_firmware.c (revision 2b3f93ea6d1f70880f3e87f3c2cbe0dc0bfc9332)
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