The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/subr_firmware.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2005, Sam Leffler <sam@errno.com>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice unmodified, this list of conditions, and the following
   10  *    disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD: releng/6.1/sys/kern/subr_firmware.c 158179 2006-04-30 16:44:43Z cvs2svn $");
   29 
   30 #include <sys/param.h>
   31 #include <sys/kernel.h>
   32 #include <sys/malloc.h>
   33 #include <sys/queue.h>
   34 #include <sys/taskqueue.h>
   35 #include <sys/systm.h>
   36 #include <sys/lock.h>
   37 #include <sys/mutex.h>
   38 #include <sys/errno.h>
   39 #include <sys/linker.h>
   40 #include <sys/firmware.h>
   41 #include <sys/proc.h>
   42 #include <sys/module.h>
   43 
   44 #define FIRMWARE_MAX    30
   45 static char *name_unload = "UNLOADING";
   46 static struct firmware firmware_table[FIRMWARE_MAX];
   47 struct task firmware_task;
   48 struct mtx firmware_mtx;
   49 MTX_SYSINIT(firmware, &firmware_mtx, "firmware table", MTX_DEF);
   50 
   51 /*
   52  * Register a firmware image with the specified name.  The
   53  * image name must not already be registered.  If this is a
   54  * subimage then parent refers to a previously registered
   55  * image that this should be associated with.
   56  */
   57 struct firmware *
   58 firmware_register(const char *imagename, const void *data, size_t datasize,
   59     unsigned int version, struct firmware *parent)
   60 {
   61         struct firmware *frp = NULL;
   62         int i;
   63 
   64         mtx_lock(&firmware_mtx);
   65         for (i = 0; i < FIRMWARE_MAX; i++) {
   66                 struct firmware *fp = &firmware_table[i];
   67 
   68                 if (fp->name == NULL) {
   69                         if (frp == NULL)
   70                                 frp = fp;
   71                         continue;
   72                 }
   73                 if (strcasecmp(imagename, fp->name) == 0) {
   74                         mtx_unlock(&firmware_mtx);
   75                         printf("%s: image %s already registered!\n",
   76                                 __func__, imagename);
   77                         return NULL;
   78                 }
   79         }
   80         if (frp == NULL) {
   81                 mtx_unlock(&firmware_mtx);
   82                 printf("%s: cannot register image %s, firmware table full!\n",
   83                     __func__, imagename);
   84                 return NULL;
   85         }
   86         frp->name = imagename;
   87         frp->data = data;
   88         frp->datasize = datasize;
   89         frp->version = version;
   90         frp->refcnt = 0;
   91         if (parent != NULL)
   92                 parent->refcnt++;
   93         frp->parent = parent;
   94         frp->file = NULL;
   95         mtx_unlock(&firmware_mtx);
   96         return frp;
   97 }
   98 
   99 static void
  100 clearentry(struct firmware *fp, int keep_file)
  101 {
  102         KASSERT(fp->refcnt == 0, ("image %s refcnt %u", fp->name, fp->refcnt));
  103         if (keep_file && (fp->file != NULL))
  104                 fp->name = name_unload;
  105         else {
  106                 fp->name = NULL;
  107                 fp->file = NULL;
  108         }
  109         fp->data = NULL;
  110         fp->datasize = 0;
  111         fp->version = 0;
  112         if (fp->parent != NULL) {       /* release parent reference */
  113                 fp->parent->refcnt--;
  114                 fp->parent = NULL;
  115         }
  116 }
  117 
  118 static struct firmware *
  119 lookup(const char *name)
  120 {
  121         int i;
  122 
  123         for (i = 0; i < FIRMWARE_MAX; i++) {
  124                 struct firmware * fp = &firmware_table[i];
  125                 if (fp->name != NULL && strcasecmp(name, fp->name) == 0)
  126                         return fp;
  127         }
  128         return NULL;
  129 }
  130 
  131 /*
  132  * Unregister/remove a firmware image.  If there are outstanding
  133  * references an error is returned and the image is not removed
  134  * from the registry.
  135  */
  136 int
  137 firmware_unregister(const char *imagename)
  138 {
  139         struct firmware *fp;
  140         int refcnt = 0;
  141 
  142         mtx_lock(&firmware_mtx);
  143         /*
  144          * NB: it is ok for the lookup to fail; this can happen
  145          * when a module is unloaded on last reference and the
  146          * module unload handler unregister's each of it's
  147          * firmware images.
  148          */
  149         fp = lookup(imagename);
  150         if (fp != NULL) {
  151                 refcnt = fp->refcnt;
  152                 if (refcnt == 0)
  153                         clearentry(fp, 0);
  154         }
  155         mtx_unlock(&firmware_mtx);
  156         return (refcnt != 0 ? EBUSY : 0);
  157 }
  158 
  159 /*
  160  * Lookup and potentially load the specified firmware image.
  161  * If the firmware is not found in the registry attempt to
  162  * load a kernel module with the image name.  If the firmware
  163  * is located a reference is returned.  The caller must release
  164  * this reference for the image to be eligible for removal/unload.
  165  */
  166 struct firmware *
  167 firmware_get(const char *imagename)
  168 {
  169         struct thread *td;
  170         struct firmware *fp;
  171         linker_file_t result;
  172         int requested_load = 0;
  173 
  174 again:
  175         mtx_lock(&firmware_mtx);
  176         fp = lookup(imagename);
  177         if (fp != NULL) {
  178                 if (requested_load)
  179                         fp->file = result;
  180                 fp->refcnt++;
  181                 mtx_unlock(&firmware_mtx);
  182                 return fp;
  183         }
  184         /*
  185          * Image not present, try to load the module holding it
  186          * or if we already tried give up.
  187          */
  188         mtx_unlock(&firmware_mtx);
  189         if (requested_load) {
  190                 printf("%s: failed to load firmware image %s\n",
  191                     __func__, imagename);
  192                 return NULL;
  193         }
  194         td = curthread;
  195         if (suser(td) != 0 || securelevel_gt(td->td_ucred, 0) != 0) {
  196                 printf("%s: insufficient privileges to "
  197                     "load firmware image %s\n", __func__, imagename);
  198                 return NULL;
  199         }
  200         mtx_lock(&Giant);               /* XXX */
  201         (void) linker_reference_module(imagename, NULL, &result);
  202         mtx_unlock(&Giant);             /* XXX */
  203         requested_load = 1;
  204         goto again;             /* sort of an Algol-style for loop */
  205 }
  206 
  207 static void
  208 unloadentry(void *unused1, int unused2)
  209 {
  210         struct firmware *fp;
  211 
  212         mtx_lock(&firmware_mtx);
  213         while ((fp = lookup(name_unload))) {
  214                 /*
  215                  * XXX: ugly, we should be able to lookup unlocked here if
  216                  * we properly lock around clearentry below to avoid double
  217                  * unload.  Play it safe for now.
  218                  */
  219                 mtx_unlock(&firmware_mtx);
  220 
  221                 linker_file_unload(fp->file, LINKER_UNLOAD_NORMAL);
  222 
  223                 mtx_lock(&firmware_mtx);
  224                 clearentry(fp, 0);
  225         }
  226         mtx_unlock(&firmware_mtx);
  227 }
  228 
  229 /*
  230  * Release a reference to a firmware image returned by
  231  * firmware_get.  The reference is released and if this is
  232  * the last reference to the firmware image the associated
  233  * module may be released/unloaded.
  234  */
  235 void
  236 firmware_put(struct firmware *fp, int flags)
  237 {
  238         mtx_lock(&firmware_mtx);
  239         fp->refcnt--;
  240         if (fp->refcnt == 0 && (flags & FIRMWARE_UNLOAD))
  241                 clearentry(fp, 1);
  242         if (fp->file)
  243                 taskqueue_enqueue(taskqueue_thread, &firmware_task);
  244         mtx_unlock(&firmware_mtx);
  245 }
  246 
  247 /*
  248  * Module glue.
  249  */
  250 static int
  251 firmware_modevent(module_t mod, int type, void *unused)
  252 {
  253         switch (type) {
  254         case MOD_LOAD:
  255                 TASK_INIT(&firmware_task, 0, unloadentry, NULL);
  256                 return 0;
  257         case MOD_UNLOAD:
  258                 taskqueue_drain(taskqueue_thread, &firmware_task);
  259                 return 0;
  260         }
  261         return EINVAL;
  262 }
  263 
  264 static moduledata_t firmware_mod = {
  265         "firmware",
  266         firmware_modevent,
  267         0
  268 };
  269 DECLARE_MODULE(firmware, firmware_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
  270 MODULE_VERSION(firmware, 1);

Cache object: 7ce7224d0bad7e748ab65352f1727d96


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.