16aec1278SMax Laier /*- 26aec1278SMax Laier * Copyright (c) 2005, Sam Leffler <sam@errno.com> 36aec1278SMax Laier * All rights reserved. 46aec1278SMax Laier * 56aec1278SMax Laier * Redistribution and use in source and binary forms, with or without 66aec1278SMax Laier * modification, are permitted provided that the following conditions 76aec1278SMax Laier * are met: 86aec1278SMax Laier * 1. Redistributions of source code must retain the above copyright 96aec1278SMax Laier * notice unmodified, this list of conditions, and the following 106aec1278SMax Laier * disclaimer. 116aec1278SMax Laier * 2. Redistributions in binary form must reproduce the above copyright 126aec1278SMax Laier * notice, this list of conditions and the following disclaimer in the 136aec1278SMax Laier * documentation and/or other materials provided with the distribution. 146aec1278SMax Laier * 156aec1278SMax Laier * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 166aec1278SMax Laier * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 176aec1278SMax Laier * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 186aec1278SMax Laier * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 196aec1278SMax Laier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 206aec1278SMax Laier * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 216aec1278SMax Laier * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 226aec1278SMax Laier * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 236aec1278SMax Laier * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 246aec1278SMax Laier * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 256aec1278SMax Laier */ 266aec1278SMax Laier 276aec1278SMax Laier #include <sys/cdefs.h> 286aec1278SMax Laier __FBSDID("$FreeBSD$"); 296aec1278SMax Laier 306aec1278SMax Laier #include <sys/param.h> 316aec1278SMax Laier #include <sys/kernel.h> 326aec1278SMax Laier #include <sys/malloc.h> 336aec1278SMax Laier #include <sys/queue.h> 346aec1278SMax Laier #include <sys/taskqueue.h> 356aec1278SMax Laier #include <sys/systm.h> 366aec1278SMax Laier #include <sys/lock.h> 376aec1278SMax Laier #include <sys/mutex.h> 386aec1278SMax Laier #include <sys/errno.h> 396aec1278SMax Laier #include <sys/linker.h> 406aec1278SMax Laier #include <sys/firmware.h> 41acd3428bSRobert Watson #include <sys/priv.h> 426aec1278SMax Laier #include <sys/proc.h> 436aec1278SMax Laier #include <sys/module.h> 446aec1278SMax Laier 456aec1278SMax Laier #define FIRMWARE_MAX 30 466aec1278SMax Laier static struct firmware firmware_table[FIRMWARE_MAX]; 476aec1278SMax Laier struct task firmware_task; 486aec1278SMax Laier struct mtx firmware_mtx; 496aec1278SMax Laier MTX_SYSINIT(firmware, &firmware_mtx, "firmware table", MTX_DEF); 506aec1278SMax Laier 516aec1278SMax Laier /* 526aec1278SMax Laier * Register a firmware image with the specified name. The 536aec1278SMax Laier * image name must not already be registered. If this is a 546aec1278SMax Laier * subimage then parent refers to a previously registered 556aec1278SMax Laier * image that this should be associated with. 566aec1278SMax Laier */ 576aec1278SMax Laier struct firmware * 586aec1278SMax Laier firmware_register(const char *imagename, const void *data, size_t datasize, 596aec1278SMax Laier unsigned int version, struct firmware *parent) 606aec1278SMax Laier { 616aec1278SMax Laier struct firmware *frp = NULL; 626aec1278SMax Laier int i; 636aec1278SMax Laier 646aec1278SMax Laier mtx_lock(&firmware_mtx); 656aec1278SMax Laier for (i = 0; i < FIRMWARE_MAX; i++) { 666aec1278SMax Laier struct firmware *fp = &firmware_table[i]; 676aec1278SMax Laier 686aec1278SMax Laier if (fp->name == NULL) { 696aec1278SMax Laier if (frp == NULL) 706aec1278SMax Laier frp = fp; 716aec1278SMax Laier continue; 726aec1278SMax Laier } 736aec1278SMax Laier if (strcasecmp(imagename, fp->name) == 0) { 746aec1278SMax Laier mtx_unlock(&firmware_mtx); 756aec1278SMax Laier printf("%s: image %s already registered!\n", 766aec1278SMax Laier __func__, imagename); 776aec1278SMax Laier return NULL; 786aec1278SMax Laier } 796aec1278SMax Laier } 806aec1278SMax Laier if (frp == NULL) { 816aec1278SMax Laier mtx_unlock(&firmware_mtx); 826aec1278SMax Laier printf("%s: cannot register image %s, firmware table full!\n", 836aec1278SMax Laier __func__, imagename); 846aec1278SMax Laier return NULL; 856aec1278SMax Laier } 866aec1278SMax Laier frp->name = imagename; 876aec1278SMax Laier frp->data = data; 886aec1278SMax Laier frp->datasize = datasize; 896aec1278SMax Laier frp->version = version; 906aec1278SMax Laier frp->refcnt = 0; 91eb1030c4SIan Dowse frp->flags = 0; 926aec1278SMax Laier if (parent != NULL) 936aec1278SMax Laier parent->refcnt++; 946aec1278SMax Laier frp->parent = parent; 956aec1278SMax Laier frp->file = NULL; 966aec1278SMax Laier mtx_unlock(&firmware_mtx); 976aec1278SMax Laier return frp; 986aec1278SMax Laier } 996aec1278SMax Laier 1006aec1278SMax Laier static void 101eb1030c4SIan Dowse clearentry(struct firmware *fp) 1026aec1278SMax Laier { 1036aec1278SMax Laier KASSERT(fp->refcnt == 0, ("image %s refcnt %u", fp->name, fp->refcnt)); 1046aec1278SMax Laier fp->name = NULL; 1056aec1278SMax Laier fp->file = NULL; 1066aec1278SMax Laier fp->data = NULL; 1076aec1278SMax Laier fp->datasize = 0; 1086aec1278SMax Laier fp->version = 0; 109eb1030c4SIan Dowse fp->flags = 0; 1106aec1278SMax Laier if (fp->parent != NULL) { /* release parent reference */ 1116aec1278SMax Laier fp->parent->refcnt--; 1126aec1278SMax Laier fp->parent = NULL; 1136aec1278SMax Laier } 1146aec1278SMax Laier } 1156aec1278SMax Laier 1166aec1278SMax Laier static struct firmware * 1176aec1278SMax Laier lookup(const char *name) 1186aec1278SMax Laier { 119b21c9288SJohn Baldwin struct firmware *fp; 1206aec1278SMax Laier int i; 1216aec1278SMax Laier 1226aec1278SMax Laier for (i = 0; i < FIRMWARE_MAX; i++) { 123b21c9288SJohn Baldwin fp = &firmware_table[i]; 1246aec1278SMax Laier if (fp->name != NULL && strcasecmp(name, fp->name) == 0) 1256aec1278SMax Laier return fp; 1266aec1278SMax Laier } 1276aec1278SMax Laier return NULL; 1286aec1278SMax Laier } 1296aec1278SMax Laier 1306aec1278SMax Laier /* 1316aec1278SMax Laier * Unregister/remove a firmware image. If there are outstanding 1326aec1278SMax Laier * references an error is returned and the image is not removed 1336aec1278SMax Laier * from the registry. 1346aec1278SMax Laier */ 1356aec1278SMax Laier int 1366aec1278SMax Laier firmware_unregister(const char *imagename) 1376aec1278SMax Laier { 1386aec1278SMax Laier struct firmware *fp; 1396aec1278SMax Laier int refcnt = 0; 1406aec1278SMax Laier 1416aec1278SMax Laier mtx_lock(&firmware_mtx); 1426aec1278SMax Laier /* 1436aec1278SMax Laier * NB: it is ok for the lookup to fail; this can happen 1446aec1278SMax Laier * when a module is unloaded on last reference and the 1456aec1278SMax Laier * module unload handler unregister's each of it's 1466aec1278SMax Laier * firmware images. 1476aec1278SMax Laier */ 1486aec1278SMax Laier fp = lookup(imagename); 1496aec1278SMax Laier if (fp != NULL) { 1506aec1278SMax Laier refcnt = fp->refcnt; 1516aec1278SMax Laier if (refcnt == 0) 152eb1030c4SIan Dowse clearentry(fp); 1536aec1278SMax Laier } 1546aec1278SMax Laier mtx_unlock(&firmware_mtx); 1556aec1278SMax Laier return (refcnt != 0 ? EBUSY : 0); 1566aec1278SMax Laier } 1576aec1278SMax Laier 1586aec1278SMax Laier /* 1596aec1278SMax Laier * Lookup and potentially load the specified firmware image. 1606aec1278SMax Laier * If the firmware is not found in the registry attempt to 1616aec1278SMax Laier * load a kernel module with the image name. If the firmware 1626aec1278SMax Laier * is located a reference is returned. The caller must release 1636aec1278SMax Laier * this reference for the image to be eligible for removal/unload. 1646aec1278SMax Laier */ 1656aec1278SMax Laier struct firmware * 1666aec1278SMax Laier firmware_get(const char *imagename) 1676aec1278SMax Laier { 1686aec1278SMax Laier struct thread *td; 1696aec1278SMax Laier struct firmware *fp; 1706aec1278SMax Laier linker_file_t result; 1716aec1278SMax Laier int requested_load = 0; 1726aec1278SMax Laier 1736aec1278SMax Laier again: 1746aec1278SMax Laier mtx_lock(&firmware_mtx); 1756aec1278SMax Laier fp = lookup(imagename); 1766aec1278SMax Laier if (fp != NULL) { 1776aec1278SMax Laier if (requested_load) 1786aec1278SMax Laier fp->file = result; 1796aec1278SMax Laier fp->refcnt++; 1806aec1278SMax Laier mtx_unlock(&firmware_mtx); 1816aec1278SMax Laier return fp; 1826aec1278SMax Laier } 1836aec1278SMax Laier /* 1846aec1278SMax Laier * Image not present, try to load the module holding it 1856aec1278SMax Laier * or if we already tried give up. 1866aec1278SMax Laier */ 1876aec1278SMax Laier mtx_unlock(&firmware_mtx); 1886aec1278SMax Laier if (requested_load) { 1896aec1278SMax Laier printf("%s: failed to load firmware image %s\n", 1906aec1278SMax Laier __func__, imagename); 1916aec1278SMax Laier return NULL; 1926aec1278SMax Laier } 1936aec1278SMax Laier td = curthread; 194acd3428bSRobert Watson if (priv_check(td, PRIV_FIRMWARE_LOAD) != 0 || 195acd3428bSRobert Watson securelevel_gt(td->td_ucred, 0) != 0) { 1966aec1278SMax Laier printf("%s: insufficient privileges to " 1976aec1278SMax Laier "load firmware image %s\n", __func__, imagename); 1986aec1278SMax Laier return NULL; 1996aec1278SMax Laier } 2006aec1278SMax Laier (void) linker_reference_module(imagename, NULL, &result); 2016aec1278SMax Laier requested_load = 1; 2026aec1278SMax Laier goto again; /* sort of an Algol-style for loop */ 2036aec1278SMax Laier } 2046aec1278SMax Laier 2056aec1278SMax Laier static void 2066aec1278SMax Laier unloadentry(void *unused1, int unused2) 2076aec1278SMax Laier { 2086aec1278SMax Laier struct firmware *fp; 209eb1030c4SIan Dowse linker_file_t file; 210450ec4edSIan Dowse int i, err; 2116aec1278SMax Laier 2126aec1278SMax Laier mtx_lock(&firmware_mtx); 213eb1030c4SIan Dowse for (;;) { 214eb1030c4SIan Dowse /* Look for an unwanted entry that we explicitly loaded. */ 215eb1030c4SIan Dowse for (i = 0; i < FIRMWARE_MAX; i++) { 216eb1030c4SIan Dowse fp = &firmware_table[i]; 217eb1030c4SIan Dowse if (fp->name != NULL && fp->file != NULL && 218eb1030c4SIan Dowse fp->refcnt == 0 && 219eb1030c4SIan Dowse (fp->flags & FIRMWAREFLAG_KEEPKLDREF) == 0) 220eb1030c4SIan Dowse break; 221eb1030c4SIan Dowse fp = NULL; 222eb1030c4SIan Dowse } 223eb1030c4SIan Dowse if (fp == NULL) 224eb1030c4SIan Dowse break; 225eb1030c4SIan Dowse file = fp->file; 226eb1030c4SIan Dowse /* No longer explicitly loaded. */ 227eb1030c4SIan Dowse fp->file = NULL; 2286aec1278SMax Laier mtx_unlock(&firmware_mtx); 2296aec1278SMax Laier 230450ec4edSIan Dowse err = linker_release_module(NULL, NULL, file); 2316aec1278SMax Laier 2326aec1278SMax Laier mtx_lock(&firmware_mtx); 233450ec4edSIan Dowse if (err) { 234450ec4edSIan Dowse /* 235450ec4edSIan Dowse * If linker_release_module() failed then we still 236450ec4edSIan Dowse * hold a reference on the module so it should not be 237450ec4edSIan Dowse * possible for it to go away or be re-registered. 238450ec4edSIan Dowse */ 239450ec4edSIan Dowse KASSERT(fp->file == NULL, 240450ec4edSIan Dowse ("firmware entry reused while referenced!")); 241450ec4edSIan Dowse fp->file = file; 242450ec4edSIan Dowse } 2436aec1278SMax Laier } 2446aec1278SMax Laier mtx_unlock(&firmware_mtx); 2456aec1278SMax Laier } 2466aec1278SMax Laier 2476aec1278SMax Laier /* 2486aec1278SMax Laier * Release a reference to a firmware image returned by 2496aec1278SMax Laier * firmware_get. The reference is released and if this is 2506aec1278SMax Laier * the last reference to the firmware image the associated 2516aec1278SMax Laier * module may be released/unloaded. 2526aec1278SMax Laier */ 2536aec1278SMax Laier void 2546aec1278SMax Laier firmware_put(struct firmware *fp, int flags) 2556aec1278SMax Laier { 2566aec1278SMax Laier mtx_lock(&firmware_mtx); 2576aec1278SMax Laier fp->refcnt--; 258eb1030c4SIan Dowse if (fp->refcnt == 0) { 259eb1030c4SIan Dowse if ((flags & FIRMWARE_UNLOAD) == 0) 260eb1030c4SIan Dowse fp->flags |= FIRMWAREFLAG_KEEPKLDREF; 261eb1030c4SIan Dowse } 2626aec1278SMax Laier if (fp->file) 2636aec1278SMax Laier taskqueue_enqueue(taskqueue_thread, &firmware_task); 2646aec1278SMax Laier mtx_unlock(&firmware_mtx); 2656aec1278SMax Laier } 2666aec1278SMax Laier 2676aec1278SMax Laier /* 2686aec1278SMax Laier * Module glue. 2696aec1278SMax Laier */ 2706aec1278SMax Laier static int 2716aec1278SMax Laier firmware_modevent(module_t mod, int type, void *unused) 2726aec1278SMax Laier { 273b21c9288SJohn Baldwin struct firmware *fp; 274eb1030c4SIan Dowse int i; 275eb1030c4SIan Dowse 2766aec1278SMax Laier switch (type) { 2776aec1278SMax Laier case MOD_LOAD: 2786aec1278SMax Laier TASK_INIT(&firmware_task, 0, unloadentry, NULL); 2796aec1278SMax Laier return 0; 2806aec1278SMax Laier case MOD_UNLOAD: 281eb1030c4SIan Dowse for (i = 0; i < FIRMWARE_MAX; i++) { 282b21c9288SJohn Baldwin fp = &firmware_table[i]; 283eb1030c4SIan Dowse fp->flags &= ~FIRMWAREFLAG_KEEPKLDREF; 284eb1030c4SIan Dowse } 285eb1030c4SIan Dowse taskqueue_enqueue(taskqueue_thread, &firmware_task); 2866aec1278SMax Laier taskqueue_drain(taskqueue_thread, &firmware_task); 2876aec1278SMax Laier return 0; 2886aec1278SMax Laier } 2896aec1278SMax Laier return EINVAL; 2906aec1278SMax Laier } 2916aec1278SMax Laier 2926aec1278SMax Laier static moduledata_t firmware_mod = { 2936aec1278SMax Laier "firmware", 2946aec1278SMax Laier firmware_modevent, 2956aec1278SMax Laier 0 2966aec1278SMax Laier }; 2976aec1278SMax Laier DECLARE_MODULE(firmware, firmware_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 2986aec1278SMax Laier MODULE_VERSION(firmware, 1); 299