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/mips/broadcom/bcm_nvram_cfe.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) 2016 Landon Fuller <landonf@FreeBSD.org>
    3  * Copyright (c) 2017 The FreeBSD Foundation
    4  * All rights reserved.
    5  *
    6  * Portions of this software were developed by Landon Fuller
    7  * under sponsorship from the FreeBSD Foundation.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer,
   14  *    without modification.
   15  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
   16  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
   17  *    redistribution must be conditioned upon including a substantially
   18  *    similar Disclaimer requirement for further binary redistribution.
   19  *
   20  * NO WARRANTY
   21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
   24  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
   25  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
   26  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
   29  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   31  * THE POSSIBILITY OF SUCH DAMAGES.
   32  */
   33 
   34 #include <sys/cdefs.h>
   35 __FBSDID("$FreeBSD: releng/12.0/sys/mips/broadcom/bcm_nvram_cfe.c 324070 2017-09-27 19:44:23Z landonf $");
   36 
   37 /*
   38  * BHND CFE NVRAM driver.
   39  * 
   40  * Provides access to device NVRAM via CFE.
   41  */
   42 
   43 #include <sys/param.h>
   44 #include <sys/kernel.h>
   45 #include <sys/bus.h>
   46 #include <sys/limits.h>
   47 #include <sys/malloc.h>
   48 #include <sys/module.h>
   49 #include <sys/systm.h>
   50 
   51 #include <machine/bus.h>
   52 #include <sys/rman.h>
   53 #include <machine/resource.h>
   54 
   55 #include <dev/bhnd/bhnd.h>
   56 
   57 #include <dev/cfe/cfe_api.h>
   58 #include <dev/cfe/cfe_error.h>
   59 #include <dev/cfe/cfe_ioctl.h>
   60 
   61 #include "bhnd_nvram_if.h"
   62 
   63 #include "bcm_machdep.h"
   64 #include "bcm_nvram_cfevar.h"
   65 
   66 BHND_NVRAM_IOPS_DEFN(iocfe)
   67 
   68 #define IOCFE_LOG(_io, _fmt, ...)       \
   69         printf("%s/%s: " _fmt, __FUNCTION__, (_io)->dname, ##__VA_ARGS__)
   70 
   71 static int      bcm_nvram_iocfe_init(struct bcm_nvram_iocfe *iocfe,
   72                     char *dname);
   73 
   74 /** Known CFE NVRAM device names, in probe order. */
   75 static char *nvram_cfe_devs[] = {
   76         "nflash0.nvram",        /* NAND */
   77         "nflash1.nvram",
   78         "flash0.nvram",
   79         "flash1.nvram",
   80 };
   81 
   82 /** Supported CFE NVRAM formats, in probe order. */
   83 static bhnd_nvram_data_class * const nvram_cfe_fmts[] = {
   84         &bhnd_nvram_bcm_class,
   85         &bhnd_nvram_tlv_class
   86 };
   87 
   88 static int
   89 bhnd_nvram_cfe_probe(device_t dev)
   90 {
   91         struct bcm_platform *bp;
   92 
   93         /* Fetch platform NVRAM I/O context */
   94         bp = bcm_get_platform();
   95         if (bp->nvram_io == NULL)
   96                 return (ENXIO);
   97 
   98         KASSERT(bp->nvram_cls != NULL, ("missing NVRAM class"));
   99 
  100         /* Set the device description */
  101         device_set_desc(dev, bhnd_nvram_data_class_desc(bp->nvram_cls));
  102 
  103         /* Refuse wildcard attachments */
  104         return (BUS_PROBE_NOWILDCARD);
  105 }
  106 
  107 
  108 static int
  109 bhnd_nvram_cfe_attach(device_t dev)
  110 {
  111         struct bcm_platform             *bp;
  112         struct bhnd_nvram_cfe_softc     *sc;
  113         int                              error;
  114 
  115         bp = bcm_get_platform();
  116         KASSERT(bp->nvram_io != NULL, ("missing NVRAM I/O context"));
  117         KASSERT(bp->nvram_cls != NULL, ("missing NVRAM class"));
  118 
  119         sc = device_get_softc(dev);
  120         sc->dev = dev;
  121 
  122         error = bhnd_nvram_store_parse_new(&sc->store, bp->nvram_io,
  123             bp->nvram_cls);
  124         if (error)
  125                 return (error);
  126 
  127         error = bhnd_service_registry_add(&bp->services, dev,
  128             BHND_SERVICE_NVRAM, 0);
  129         if (error) {
  130                 bhnd_nvram_store_free(sc->store);
  131                 return (error);
  132         }
  133 
  134         return (error);
  135 }
  136 
  137 static int
  138 bhnd_nvram_cfe_resume(device_t dev)
  139 {
  140         return (0);
  141 }
  142 
  143 static int
  144 bhnd_nvram_cfe_suspend(device_t dev)
  145 {
  146         return (0);
  147 }
  148 
  149 static int
  150 bhnd_nvram_cfe_detach(device_t dev)
  151 {
  152         struct bcm_platform             *bp;
  153         struct bhnd_nvram_cfe_softc     *sc;
  154         int                              error;
  155 
  156         bp = bcm_get_platform();
  157         sc = device_get_softc(dev);
  158 
  159         error = bhnd_service_registry_remove(&bp->services, dev,
  160             BHND_SERVICE_ANY);
  161         if (error)
  162                 return (error);
  163 
  164         bhnd_nvram_store_free(sc->store);
  165 
  166         return (0);
  167 }
  168 
  169 static int
  170 bhnd_nvram_cfe_getvar(device_t dev, const char *name, void *buf, size_t *len,
  171     bhnd_nvram_type type)
  172 {
  173         struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev);
  174 
  175         return (bhnd_nvram_store_getvar(sc->store, name, buf, len, type));
  176 }
  177 
  178 static int
  179 bhnd_nvram_cfe_setvar(device_t dev, const char *name, const void *buf,
  180     size_t len, bhnd_nvram_type type)
  181 {
  182         struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev);
  183 
  184         return (bhnd_nvram_store_setvar(sc->store, name, buf, len, type));
  185 }
  186 
  187 /**
  188  * Find, open, identify, and initialize an I/O context mapping the CFE NVRAM
  189  * device.
  190  * 
  191  * @param[out]  iocfe           On success, an I/O context mapping the CFE NVRAM
  192  *                              device.
  193  * @param[out]  cls             On success, the identified NVRAM data format
  194  *                              class.
  195  *
  196  * @retval 0            success. the caller inherits ownership of @p iocfe.
  197  * @retval non-zero     if no usable CFE NVRAM device can be found, a standard
  198  *                      unix error will be returned.
  199  */
  200 int
  201 bcm_nvram_find_cfedev(struct bcm_nvram_iocfe *iocfe,
  202     bhnd_nvram_data_class **cls)
  203 {
  204         char    *dname;
  205         int      devinfo;
  206         int      error, result;
  207 
  208         for (u_int i = 0; i < nitems(nvram_cfe_fmts); i++) {
  209                 *cls = nvram_cfe_fmts[i];
  210 
  211                 for (u_int j = 0; j < nitems(nvram_cfe_devs); j++) {
  212                         dname = nvram_cfe_devs[j];
  213 
  214                         /* Does the device exist? */
  215                         if ((devinfo = cfe_getdevinfo(dname)) < 0) {
  216                                 if (devinfo != CFE_ERR_DEVNOTFOUND) {
  217                                         BCM_ERR("cfe_getdevinfo(%s) failed: "
  218                                             "%d\n", dname, devinfo);
  219                                 }
  220 
  221                                 continue;
  222                         }
  223 
  224                         /* Open for reading */
  225                         if ((error = bcm_nvram_iocfe_init(iocfe, dname)))
  226                                 continue;
  227 
  228                         /* Probe */
  229                         result = bhnd_nvram_data_probe(*cls, &iocfe->io);
  230                         if (result <= 0) {
  231                                 /* Found a supporting NVRAM data class */
  232                                 return (0);
  233                         }
  234 
  235                         /* Keep searching */
  236                         bhnd_nvram_io_free(&iocfe->io);
  237                 }
  238         }
  239 
  240         return (ENODEV);
  241 }
  242 
  243 
  244 /**
  245  * Initialize a new CFE device-backed I/O context.
  246  *
  247  * The caller is responsible for releasing all resources held by the returned
  248  * I/O context via bhnd_nvram_io_free().
  249  * 
  250  * @param[out]  io      On success, will be initialized as an I/O context for
  251  *                      CFE device @p dname.
  252  * @param       dname   The name of the CFE device to be opened for reading.
  253  *
  254  * @retval 0            success.
  255  * @retval non-zero     if opening @p dname otherwise fails, a standard unix
  256  *                      error will be returned.
  257  */
  258 static int
  259 bcm_nvram_iocfe_init(struct bcm_nvram_iocfe *iocfe, char *dname)
  260 {
  261         nvram_info_t             nvram_info;
  262         int                      cerr, devinfo, dtype, rlen;
  263         int64_t                  nv_offset;
  264         u_int                    nv_size;
  265         bool                     req_blk_erase;
  266         int                      error;
  267 
  268         iocfe->io.iops = &bhnd_nvram_iocfe_ops;
  269         iocfe->dname = dname;
  270 
  271         /* Try to open the device */
  272         iocfe->fd = cfe_open(dname);
  273         if (iocfe->fd <= 0) {
  274                 IOCFE_LOG(iocfe, "cfe_open() failed: %d\n", iocfe->fd);
  275 
  276                 return (ENXIO);
  277         }
  278 
  279         /* Try to fetch device info */
  280         if ((devinfo = cfe_getdevinfo(iocfe->dname)) < 0) {
  281                 IOCFE_LOG(iocfe, "cfe_getdevinfo() failed: %d\n", devinfo);
  282                 error = ENXIO;
  283                 goto failed;
  284         }
  285 
  286         /* Verify device type */
  287         dtype = devinfo & CFE_DEV_MASK;
  288         switch (dtype) {
  289         case CFE_DEV_FLASH:
  290         case CFE_DEV_NVRAM:
  291                 /* Valid device type */
  292                 break;
  293         default:
  294                 IOCFE_LOG(iocfe, "unknown device type: %d\n", dtype);
  295                 error = ENXIO;
  296                 goto failed;
  297         }
  298 
  299         /* Try to fetch nvram info from CFE */
  300         cerr = cfe_ioctl(iocfe->fd, IOCTL_NVRAM_GETINFO,
  301             (unsigned char *)&nvram_info, sizeof(nvram_info), &rlen, 0);
  302         if (cerr == CFE_OK) {
  303                 /* Sanity check the result; must not be a negative integer */
  304                 if (nvram_info.nvram_size < 0 ||
  305                     nvram_info.nvram_offset < 0)
  306                 {
  307                         IOCFE_LOG(iocfe, "invalid NVRAM layout (%d/%d)\n",
  308                             nvram_info.nvram_size, nvram_info.nvram_offset);
  309                         error = ENXIO;
  310                         goto failed;
  311                 }
  312 
  313                 nv_offset       = nvram_info.nvram_offset;
  314                 nv_size         = nvram_info.nvram_size;
  315                 req_blk_erase   = (nvram_info.nvram_eraseflg != 0);
  316         } else if (cerr != CFE_OK && cerr != CFE_ERR_INV_COMMAND) {
  317                 IOCFE_LOG(iocfe, "IOCTL_NVRAM_GETINFO failed: %d\n", cerr);
  318                 error = ENXIO;
  319                 goto failed;
  320         }
  321 
  322         /* Fall back on flash info.
  323          * 
  324          * This is known to be required on the Asus RT-N53 (CFE 5.70.55.33, 
  325          * BBP 1.0.37, BCM5358UB0), where IOCTL_NVRAM_GETINFO returns
  326          * CFE_ERR_INV_COMMAND.
  327          */
  328         if (cerr == CFE_ERR_INV_COMMAND) {
  329                 flash_info_t fi;
  330 
  331                 cerr = cfe_ioctl(iocfe->fd, IOCTL_FLASH_GETINFO,
  332                     (unsigned char *)&fi, sizeof(fi), &rlen, 0);
  333 
  334                 if (cerr != CFE_OK) {
  335                         IOCFE_LOG(iocfe, "IOCTL_FLASH_GETINFO failed %d\n",
  336                             cerr);
  337                         error = ENXIO;
  338                         goto failed;
  339                 }
  340 
  341                 nv_offset       = 0x0;
  342                 nv_size         = fi.flash_size;
  343                 req_blk_erase   = !(fi.flash_flags & FLASH_FLAG_NOERASE);
  344         }
  345 
  346         
  347         /* Verify that the full NVRAM layout can be represented via size_t */
  348         if (nv_size > SIZE_MAX || SIZE_MAX - nv_size < nv_offset) {
  349                 IOCFE_LOG(iocfe, "invalid NVRAM layout (%#x/%#jx)\n",
  350                     nv_size, (intmax_t)nv_offset);
  351                 error = ENXIO;
  352                 goto failed;
  353         }
  354 
  355         iocfe->offset = nv_offset;
  356         iocfe->size = nv_size;
  357         iocfe->req_blk_erase = req_blk_erase;
  358 
  359         return (CFE_OK);
  360 
  361 failed:
  362         if (iocfe->fd >= 0)
  363                 cfe_close(iocfe->fd);
  364 
  365         return (error);
  366 }
  367 
  368 static void
  369 bhnd_nvram_iocfe_free(struct bhnd_nvram_io *io)
  370 {
  371         struct bcm_nvram_iocfe  *iocfe = (struct bcm_nvram_iocfe *)io;
  372 
  373         /* CFE I/O instances are statically allocated; we do not need to free
  374          * the instance itself */
  375         cfe_close(iocfe->fd);
  376 }
  377 
  378 static size_t
  379 bhnd_nvram_iocfe_getsize(struct bhnd_nvram_io *io)
  380 {
  381         struct bcm_nvram_iocfe  *iocfe = (struct bcm_nvram_iocfe *)io;
  382         return (iocfe->size);
  383 }
  384 
  385 static int
  386 bhnd_nvram_iocfe_setsize(struct bhnd_nvram_io *io, size_t size)
  387 {
  388         /* unsupported */
  389         return (ENODEV);
  390 }
  391 
  392 static int
  393 bhnd_nvram_iocfe_read_ptr(struct bhnd_nvram_io *io, size_t offset,
  394     const void **ptr, size_t nbytes, size_t *navail)
  395 {
  396         /* unsupported */
  397         return (ENODEV);
  398 }
  399 
  400 static int
  401 bhnd_nvram_iocfe_write_ptr(struct bhnd_nvram_io *io, size_t offset,
  402     void **ptr, size_t nbytes, size_t *navail)
  403 {
  404         /* unsupported */
  405         return (ENODEV);
  406 }
  407 
  408 static int
  409 bhnd_nvram_iocfe_write(struct bhnd_nvram_io *io, size_t offset, void *buffer,
  410     size_t nbytes)
  411 {
  412         /* unsupported */
  413         return (ENODEV);
  414 }
  415 
  416 static int
  417 bhnd_nvram_iocfe_read(struct bhnd_nvram_io *io, size_t offset, void *buffer,
  418     size_t nbytes)
  419 {
  420         struct bcm_nvram_iocfe  *iocfe;
  421         size_t                   remain;
  422         int64_t                  cfe_offset;
  423         int                      nr, nreq;
  424 
  425         iocfe = (struct bcm_nvram_iocfe *)io;
  426 
  427         /* Determine (and validate) the base CFE offset */
  428 #if (SIZE_MAX > INT64_MAX)
  429         if (iocfe->offset > INT64_MAX || offset > INT64_MAX)
  430                 return (ENXIO);
  431 #endif
  432 
  433         if (INT64_MAX - offset < iocfe->offset)
  434                 return (ENXIO);
  435 
  436         cfe_offset = iocfe->offset + offset;
  437 
  438         /* Verify that cfe_offset + nbytes is representable */
  439         if (INT64_MAX - cfe_offset < nbytes)
  440                 return (ENXIO);
  441 
  442         /* Perform the read */
  443         for (remain = nbytes; remain > 0;) {
  444                 void    *p;
  445                 size_t   nread;
  446                 int64_t  cfe_noff;
  447 
  448                 nread = (nbytes - remain);
  449                 cfe_noff = cfe_offset + nread;
  450                 p = ((uint8_t *)buffer + nread);
  451                 nreq = ummin(INT_MAX, remain);
  452         
  453                 nr = cfe_readblk(iocfe->fd, cfe_noff, p, nreq);
  454                 if (nr < 0) {
  455                         IOCFE_LOG(iocfe, "cfe_readblk() failed: %d\n", nr);
  456                         return (ENXIO);
  457                 }
  458 
  459                 /* Check for unexpected short read */
  460                 if (nr == 0 && remain > 0) {
  461                         /* If the request fits entirely within the CFE
  462                          * device range, we shouldn't hit EOF */
  463                         if (remain < iocfe->size &&
  464                             iocfe->size - remain > offset)
  465                         {
  466                                 IOCFE_LOG(iocfe, "cfe_readblk() returned "
  467                                     "unexpected short read (%d/%d)\n", nr,
  468                                     nreq);
  469                                 return (ENXIO);
  470                         }
  471                 }
  472 
  473                 if (nr == 0)
  474                         break;
  475 
  476                 remain -= nr;
  477         }
  478 
  479         /* Check for short read */
  480         if (remain > 0)
  481                 return (ENXIO);
  482 
  483         return (0);
  484 }
  485 
  486 static device_method_t bhnd_nvram_cfe_methods[] = {
  487         /* Device interface */
  488         DEVMETHOD(device_probe,         bhnd_nvram_cfe_probe),
  489         DEVMETHOD(device_attach,        bhnd_nvram_cfe_attach),
  490         DEVMETHOD(device_resume,        bhnd_nvram_cfe_resume),
  491         DEVMETHOD(device_suspend,       bhnd_nvram_cfe_suspend),
  492         DEVMETHOD(device_detach,        bhnd_nvram_cfe_detach),
  493 
  494         /* NVRAM interface */
  495         DEVMETHOD(bhnd_nvram_getvar,    bhnd_nvram_cfe_getvar),
  496         DEVMETHOD(bhnd_nvram_setvar,    bhnd_nvram_cfe_setvar),
  497 
  498         DEVMETHOD_END
  499 };
  500 
  501 DEFINE_CLASS_0(bhnd_nvram, bhnd_nvram_cfe, bhnd_nvram_cfe_methods,
  502     sizeof(struct bhnd_nvram_cfe_softc));
  503 EARLY_DRIVER_MODULE(bhnd_nvram_cfe, nexus, bhnd_nvram_cfe,
  504     bhnd_nvram_devclass, NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);

Cache object: 6844977f54ef6ca5a5e343ef38e73308


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