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/dev/firmload.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 /*      $NetBSD: firmload.c,v 1.6.2.1 2007/02/08 22:32:36 tron Exp $    */
    2 
    3 /*-
    4  * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Jason R. Thorpe.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  * 3. All advertising materials mentioning features or use of this software
   19  *    must display the following acknowledgement:
   20  *      This product includes software developed by the NetBSD
   21  *      Foundation, Inc. and its contributors.
   22  * 4. Neither the name of The NetBSD Foundation nor the names of its
   23  *    contributors may be used to endorse or promote products derived
   24  *    from this software without specific prior written permission.
   25  *
   26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   36  * POSSIBILITY OF SUCH DAMAGE.
   37  */
   38 
   39 #include <sys/cdefs.h>
   40 __KERNEL_RCSID(0, "$NetBSD: firmload.c,v 1.6.2.1 2007/02/08 22:32:36 tron Exp $");
   41 
   42 /*
   43  * The firmload API provides an interface for device drivers to access
   44  * firmware images that must be loaded onto their devices.
   45  */
   46 
   47 #include <sys/param.h>
   48 #include <sys/fcntl.h>
   49 #include <sys/filedesc.h>
   50 #include <sys/malloc.h>
   51 #include <sys/namei.h>
   52 #include <sys/systm.h>
   53 #include <sys/sysctl.h>
   54 #include <sys/vnode.h>
   55 #include <sys/kauth.h>
   56 
   57 #include <dev/firmload.h>
   58 
   59 static MALLOC_DEFINE(M_DEVFIRM, "devfirm", "device firmware buffers");
   60 
   61 struct firmware_handle {
   62         struct vnode    *fh_vp;
   63         off_t            fh_size;
   64 };
   65 
   66 static firmware_handle_t
   67 firmware_handle_alloc(void)
   68 {
   69 
   70         return (malloc(sizeof(struct firmware_handle), M_DEVFIRM, M_WAITOK));
   71 }
   72 
   73 static void
   74 firmware_handle_free(firmware_handle_t fh)
   75 {
   76 
   77         free(fh, M_DEVFIRM);
   78 }
   79 
   80 #if !defined(FIRMWARE_PATHS)
   81 #define FIRMWARE_PATHS          \
   82         "/libdata/firmware:/usr/libdata/firmware:/usr/pkg/libdata/firmware:/usr/pkg/libdata"
   83 #endif
   84 
   85 static char firmware_paths[PATH_MAX+1] = FIRMWARE_PATHS;
   86 
   87 static int
   88 sysctl_hw_firmware_path(SYSCTLFN_ARGS)
   89 {
   90         int error, i;
   91         char newpath[PATH_MAX+1];
   92         struct sysctlnode node;
   93         char expected_char;
   94 
   95         node = *rnode;
   96         node.sysctl_data = &newpath[0];
   97         memcpy(node.sysctl_data, rnode->sysctl_data, PATH_MAX+1);
   98         error = sysctl_lookup(SYSCTLFN_CALL(&node));
   99         if (error || newp == NULL)
  100                 return (error);
  101         
  102         /*
  103          * Make sure that all of the paths in the new path list are
  104          * absolute.
  105          *
  106          * When sysctl_lookup() deals with a string, it's guaranteed
  107          * to come back nul-terminated.
  108          */
  109         expected_char = '/';
  110         for (i = 0; i < PATH_MAX+1; i++) {
  111                 if (expected_char != 0 && newpath[i] != expected_char)
  112                         return (EINVAL);
  113                 if (newpath[i] == '\0')
  114                         break;
  115                 else if (newpath[i] == ':')
  116                         expected_char = '/';
  117                 else
  118                         expected_char = 0;
  119         }
  120 
  121         memcpy(rnode->sysctl_data, node.sysctl_data, PATH_MAX+1);
  122 
  123         return (0);
  124 }
  125 
  126 SYSCTL_SETUP_PROTO(sysctl_hw_firmware_setup);
  127 
  128 SYSCTL_SETUP(sysctl_hw_firmware_setup, "sysctl hw.firmware subtree setup")
  129 {
  130         const struct sysctlnode *firmware_node;
  131 
  132         if (sysctl_createv(clog, 0, NULL, NULL,
  133             CTLFLAG_PERMANENT,
  134             CTLTYPE_NODE, "hw", NULL,
  135             NULL, 0, NULL, 0,
  136             CTL_HW, CTL_EOL) != 0)
  137                 return;
  138         
  139         if (sysctl_createv(clog, 0, NULL, &firmware_node,
  140             CTLFLAG_PERMANENT,
  141             CTLTYPE_NODE, "firmware", NULL,
  142             NULL, 0, NULL, 0,
  143             CTL_HW, CTL_CREATE, CTL_EOL) != 0)
  144                 return;
  145 
  146         sysctl_createv(clog, 0, NULL, NULL,
  147             CTLFLAG_READWRITE,
  148             CTLTYPE_STRING, "path",
  149             SYSCTL_DESCR("Device firmware loading path list"),
  150             sysctl_hw_firmware_path, 0, firmware_paths, PATH_MAX+1,
  151             CTL_HW, firmware_node->sysctl_num, CTL_CREATE, CTL_EOL);
  152 }
  153 
  154 static char *
  155 firmware_path_next(const char *drvname, const char *imgname, char *pnbuf,
  156     char **prefixp)
  157 {
  158         char *prefix = *prefixp;
  159         size_t maxprefix, i;
  160 
  161         if (prefix == NULL              /* terminated early */
  162             || *prefix == '\0'          /* no more left */
  163             || *prefix != '/') {        /* not absolute */
  164                 *prefixp = NULL;
  165                 return (NULL);
  166         }
  167 
  168         /*
  169          * Compute the max path prefix based on the length of the provided
  170          * names.
  171          */
  172         maxprefix = MAXPATHLEN -
  173                 (1 /* / */
  174                  + strlen(drvname)
  175                  + 1 /* / */
  176                  + strlen(imgname)
  177                  + 1 /* terminating NUL */);
  178 
  179         /* Check for underflow (size_t is unsigned). */
  180         if (maxprefix > MAXPATHLEN) {
  181                 *prefixp = NULL;
  182                 return (NULL);
  183         }
  184 
  185         for (i = 0; i < maxprefix; i++) {
  186                 if (*prefix == ':' || *prefix == '\0')
  187                         break;
  188                 pnbuf[i] = *prefix++;
  189         }
  190 
  191         if (*prefix != ':' && *prefix != '\0') {
  192                 /* Path prefix was too long. */
  193                 *prefixp = NULL;
  194                 return (NULL);
  195         }
  196 
  197         if (*prefix != '\0')
  198                 prefix++;
  199         *prefixp = prefix;
  200 
  201         /*
  202          * This sprintf() is safe because of the maxprefix calculation
  203          * performed above.
  204          */
  205         sprintf(&pnbuf[i], "/%s/%s", drvname, imgname);
  206 
  207         return (pnbuf);
  208 }
  209 
  210 static char *
  211 firmware_path_first(const char *drvname, const char *imgname, char *pnbuf,
  212     char **prefixp)
  213 {
  214 
  215         *prefixp = firmware_paths;
  216         return (firmware_path_next(drvname, imgname, pnbuf, prefixp));
  217 }
  218 
  219 /*
  220  * firmware_open:
  221  *
  222  *      Open a firmware image and return its handle.
  223  */
  224 int
  225 firmware_open(const char *drvname, const char *imgname, firmware_handle_t *fhp)
  226 {
  227         struct nameidata nd;
  228         struct vattr va;
  229         char *pnbuf, *path, *prefix;
  230         firmware_handle_t fh;
  231         struct vnode *vp;
  232         int error;
  233         extern struct cwdinfo cwdi0;
  234 
  235         if (drvname == NULL || imgname == NULL)
  236                 return (EINVAL);
  237 
  238         if (cwdi0.cwdi_cdir == NULL) {
  239                 printf("firmware_open(%s/%s) called too early.\n",
  240                         drvname, imgname);
  241                 return ENOENT;
  242         }
  243 
  244         pnbuf = PNBUF_GET();
  245         KASSERT(pnbuf != NULL);
  246 
  247         fh = firmware_handle_alloc();
  248         KASSERT(fh != NULL);
  249 
  250         error = 0;
  251         for (path = firmware_path_first(drvname, imgname, pnbuf, &prefix);
  252              path != NULL;
  253              path = firmware_path_next(drvname, imgname, pnbuf, &prefix)) {
  254                 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, curlwp);
  255                 error = vn_open(&nd, FREAD, 0);
  256                 if (error == ENOENT)
  257                         continue;
  258                 break;
  259         }
  260 
  261         PNBUF_PUT(pnbuf);
  262         if (error) {
  263                 firmware_handle_free(fh);
  264                 return (error);
  265         }
  266 
  267         vp = nd.ni_vp;
  268 
  269         error = VOP_GETATTR(vp, &va, kauth_cred_get(), curlwp);
  270         if (error) {
  271                 VOP_UNLOCK(vp, 0);
  272                 (void)vn_close(vp, FREAD, kauth_cred_get(), curlwp);
  273                 firmware_handle_free(fh);
  274                 return (error);
  275         }
  276 
  277         if (va.va_type != VREG) {
  278                 VOP_UNLOCK(vp, 0);
  279                 (void)vn_close(vp, FREAD, kauth_cred_get(), curlwp);
  280                 firmware_handle_free(fh);
  281                 return (EINVAL);
  282         }
  283 
  284         /* XXX Mark as busy text file. */
  285 
  286         fh->fh_vp = vp;
  287         fh->fh_size = va.va_size;
  288 
  289         VOP_UNLOCK(vp, 0);
  290 
  291         *fhp = fh;
  292         return (0);
  293 }
  294 
  295 /*
  296  * firmware_close:
  297  *
  298  *      Close a firmware image.
  299  */
  300 int
  301 firmware_close(firmware_handle_t fh)
  302 {
  303         int error;
  304 
  305         error = vn_close(fh->fh_vp, FREAD, kauth_cred_get(), curlwp);
  306         firmware_handle_free(fh);
  307         return (error);
  308 }
  309 
  310 /*
  311  * firmware_get_size:
  312  *
  313  *      Return the total size of a firmware image.
  314  */
  315 off_t
  316 firmware_get_size(firmware_handle_t fh)
  317 {
  318 
  319         return (fh->fh_size);
  320 }
  321 
  322 /*
  323  * firmware_read:
  324  *
  325  *      Read data from a firmware image at the specified offset into
  326  *      the provided buffer.
  327  */
  328 int
  329 firmware_read(firmware_handle_t fh, off_t offset, void *buf, size_t len)
  330 {
  331 
  332         return (vn_rdwr(UIO_READ, fh->fh_vp, buf, len, offset,
  333                         UIO_SYSSPACE, 0, kauth_cred_get(), NULL, curlwp));
  334 }
  335 
  336 /*
  337  * firmware_malloc:
  338  *
  339  *      Allocate a firmware buffer of the specified size.
  340  *
  341  *      NOTE: This routine may block.
  342  */
  343 void *
  344 firmware_malloc(size_t size)
  345 {
  346 
  347         return (malloc(size, M_DEVFIRM, M_WAITOK));
  348 }
  349 
  350 /*
  351  * firmware_free:
  352  *
  353  *      Free a previously allocated firmware buffer.
  354  */
  355 /*ARGSUSED*/
  356 void
  357 firmware_free(void *v, size_t size)
  358 {
  359 
  360         free(v, M_DEVFIRM);
  361 }

Cache object: 954df5fa9d8b3c2e6cc948afa61bba04


[ 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.