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.24 2022/10/26 23:39:10 riastradh 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.24 2022/10/26 23:39:10 riastradh 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/kmem.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 #include <sys/lwp.h>
   50 
   51 #include <dev/firmload.h>
   52 
   53 struct firmware_handle {
   54         struct vnode    *fh_vp;
   55         off_t            fh_size;
   56 };
   57 
   58 static firmware_handle_t
   59 firmware_handle_alloc(void)
   60 {
   61 
   62         return (kmem_alloc(sizeof(struct firmware_handle), KM_SLEEP));
   63 }
   64 
   65 static void
   66 firmware_handle_free(firmware_handle_t fh)
   67 {
   68 
   69         kmem_free(fh, sizeof(*fh));
   70 }
   71 
   72 #if !defined(FIRMWARE_PATHS)
   73 #define FIRMWARE_PATHS          \
   74         "/libdata/firmware:/usr/libdata/firmware:/usr/pkg/libdata/firmware:/usr/pkg/libdata"
   75 #endif
   76 
   77 static char firmware_paths[PATH_MAX+1] = FIRMWARE_PATHS;
   78 
   79 static int
   80 sysctl_hw_firmware_path(SYSCTLFN_ARGS)
   81 {
   82         int error, i;
   83         char newpath[PATH_MAX+1];
   84         struct sysctlnode node;
   85         char expected_char;
   86 
   87         node = *rnode;
   88         node.sysctl_data = &newpath[0];
   89         memcpy(node.sysctl_data, rnode->sysctl_data, PATH_MAX+1);
   90         error = sysctl_lookup(SYSCTLFN_CALL(&node));
   91         if (error || newp == NULL)
   92                 return (error);
   93         
   94         /*
   95          * Make sure that all of the paths in the new path list are
   96          * absolute.
   97          *
   98          * When sysctl_lookup() deals with a string, it's guaranteed
   99          * to come back nul-terminated.
  100          */
  101         expected_char = '/';
  102         for (i = 0; i < PATH_MAX+1; i++) {
  103                 if (expected_char != 0 && newpath[i] != expected_char)
  104                         return (EINVAL);
  105                 if (newpath[i] == '\0')
  106                         break;
  107                 else if (newpath[i] == ':')
  108                         expected_char = '/';
  109                 else
  110                         expected_char = 0;
  111         }
  112 
  113         memcpy(rnode->sysctl_data, node.sysctl_data, PATH_MAX+1);
  114 
  115         return (0);
  116 }
  117 
  118 SYSCTL_SETUP(sysctl_hw_firmware_setup, "sysctl hw.firmware subtree setup")
  119 {
  120         const struct sysctlnode *firmware_node;
  121         
  122         if (sysctl_createv(clog, 0, NULL, &firmware_node,
  123             CTLFLAG_PERMANENT,
  124             CTLTYPE_NODE, "firmware", NULL,
  125             NULL, 0, NULL, 0,
  126             CTL_HW, CTL_CREATE, CTL_EOL) != 0)
  127                 return;
  128 
  129         sysctl_createv(clog, 0, NULL, NULL,
  130             CTLFLAG_READWRITE,
  131             CTLTYPE_STRING, "path",
  132             SYSCTL_DESCR("Device firmware loading path list"),
  133             sysctl_hw_firmware_path, 0, firmware_paths, PATH_MAX+1,
  134             CTL_HW, firmware_node->sysctl_num, CTL_CREATE, CTL_EOL);
  135 }
  136 
  137 static char *
  138 firmware_path_next(const char *drvname, const char *imgname, char *pnbuf,
  139     char **prefixp)
  140 {
  141         char *prefix = *prefixp;
  142         size_t maxprefix, i;
  143 
  144         if (prefix == NULL              /* terminated early */
  145             || *prefix != '/') {        /* empty or not absolute */
  146                 *prefixp = NULL;
  147                 return (NULL);
  148         }
  149 
  150         /*
  151          * Compute the max path prefix based on the length of the provided
  152          * names.
  153          */
  154         maxprefix = MAXPATHLEN -
  155                 (1 /* / */
  156                  + strlen(drvname)
  157                  + 1 /* / */
  158                  + strlen(imgname)
  159                  + 1 /* terminating NUL */);
  160 
  161         /* Check for underflow (size_t is unsigned). */
  162         if (maxprefix > MAXPATHLEN) {
  163                 *prefixp = NULL;
  164                 return (NULL);
  165         }
  166 
  167         for (i = 0; i < maxprefix; i++) {
  168                 if (*prefix == ':' || *prefix == '\0')
  169                         break;
  170                 pnbuf[i] = *prefix++;
  171         }
  172 
  173         if (*prefix != ':' && *prefix != '\0') {
  174                 /* Path prefix was too long. */
  175                 *prefixp = NULL;
  176                 return (NULL);
  177         }
  178 
  179         if (*prefix != '\0')
  180                 prefix++;
  181         *prefixp = prefix;
  182 
  183         KASSERT(MAXPATHLEN >= i);
  184         snprintf(pnbuf + i, MAXPATHLEN - i, "/%s/%s", drvname, imgname);
  185 
  186         return (pnbuf);
  187 }
  188 
  189 static char *
  190 firmware_path_first(const char *drvname, const char *imgname, char *pnbuf,
  191     char **prefixp)
  192 {
  193 
  194         *prefixp = firmware_paths;
  195         return (firmware_path_next(drvname, imgname, pnbuf, prefixp));
  196 }
  197 
  198 /*
  199  * firmware_open:
  200  *
  201  *      Open a firmware image and return its handle.
  202  */
  203 int
  204 firmware_open(const char *drvname, const char *imgname, firmware_handle_t *fhp)
  205 {
  206         struct pathbuf *pb;
  207         struct vattr va;
  208         char *pnbuf, *path, *prefix;
  209         firmware_handle_t fh;
  210         struct vnode *vp;
  211         int error;
  212 
  213         if (drvname == NULL || imgname == NULL)
  214                 return (EINVAL);
  215 
  216         if (cwdi0.cwdi_cdir == NULL) {
  217                 printf("firmware_open(%s/%s) called too early.\n",
  218                         drvname, imgname);
  219                 return ENOENT;
  220         }
  221 
  222         pnbuf = PNBUF_GET();
  223         KASSERT(pnbuf != NULL);
  224 
  225         fh = firmware_handle_alloc();
  226         KASSERT(fh != NULL);
  227 
  228         error = 0;
  229         for (path = firmware_path_first(drvname, imgname, pnbuf, &prefix);
  230              path != NULL;
  231              path = firmware_path_next(drvname, imgname, pnbuf, &prefix)) {
  232                 pb = pathbuf_create(path);
  233                 if (pb == NULL) {
  234                         error = ENOMEM;
  235                         break;
  236                 }
  237                 error = vn_open(NULL, pb, NOCHROOT, FREAD, 0, &vp, NULL, NULL);
  238                 pathbuf_destroy(pb);
  239                 if (error == ENOENT) {
  240                         continue;
  241                 }
  242                 break;
  243         }
  244 
  245         PNBUF_PUT(pnbuf);
  246         if (error) {
  247                 firmware_handle_free(fh);
  248                 return (error);
  249         }
  250 
  251         error = VOP_GETATTR(vp, &va, kauth_cred_get());
  252         if (error) {
  253                 VOP_UNLOCK(vp);
  254                 (void)vn_close(vp, FREAD, kauth_cred_get());
  255                 firmware_handle_free(fh);
  256                 return (error);
  257         }
  258 
  259         if (va.va_type != VREG) {
  260                 VOP_UNLOCK(vp);
  261                 (void)vn_close(vp, FREAD, kauth_cred_get());
  262                 firmware_handle_free(fh);
  263                 return (EINVAL);
  264         }
  265 
  266         /* XXX Mark as busy text file. */
  267 
  268         fh->fh_vp = vp;
  269         fh->fh_size = va.va_size;
  270 
  271         VOP_UNLOCK(vp);
  272 
  273         *fhp = fh;
  274         return (0);
  275 }
  276 
  277 /*
  278  * firmware_close:
  279  *
  280  *      Close a firmware image.
  281  */
  282 int
  283 firmware_close(firmware_handle_t fh)
  284 {
  285         int error;
  286 
  287         error = vn_close(fh->fh_vp, FREAD, kauth_cred_get());
  288         firmware_handle_free(fh);
  289         return (error);
  290 }
  291 
  292 /*
  293  * firmware_get_size:
  294  *
  295  *      Return the total size of a firmware image.
  296  */
  297 off_t
  298 firmware_get_size(firmware_handle_t fh)
  299 {
  300 
  301         return (fh->fh_size);
  302 }
  303 
  304 /*
  305  * firmware_read:
  306  *
  307  *      Read data from a firmware image at the specified offset into
  308  *      the provided buffer.
  309  */
  310 int
  311 firmware_read(firmware_handle_t fh, off_t offset, void *buf, size_t len)
  312 {
  313 
  314         return (vn_rdwr(UIO_READ, fh->fh_vp, buf, len, offset,
  315                         UIO_SYSSPACE, 0, kauth_cred_get(), NULL, curlwp));
  316 }
  317 
  318 /*
  319  * firmware_malloc:
  320  *
  321  *      Allocate a firmware buffer of the specified size.
  322  *
  323  *      NOTE: This routine may block.
  324  */
  325 void *
  326 firmware_malloc(size_t size)
  327 {
  328 
  329         return (kmem_alloc(size, KM_SLEEP));
  330 }
  331 
  332 /*
  333  * firmware_free:
  334  *
  335  *      Free a previously allocated firmware buffer.
  336  */
  337 /*ARGSUSED*/
  338 void
  339 firmware_free(void *v, size_t size)
  340 {
  341 
  342         kmem_free(v, size);
  343 }

Cache object: 4904f33fd4e14e9820672c31cf7e6f5a


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