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

Cache object: 5ff344b6c28e1bb261d46c351a88a358


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