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$");
   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 static int
  108 bhnd_nvram_cfe_attach(device_t dev)
  109 {
  110         struct bcm_platform             *bp;
  111         struct bhnd_nvram_cfe_softc     *sc;
  112         int                              error;
  113 
  114         bp = bcm_get_platform();
  115         KASSERT(bp->nvram_io != NULL, ("missing NVRAM I/O context"));
  116         KASSERT(bp->nvram_cls != NULL, ("missing NVRAM class"));
  117 
  118         sc = device_get_softc(dev);
  119         sc->dev = dev;
  120 
  121         error = bhnd_nvram_store_parse_new(&sc->store, bp->nvram_io,
  122             bp->nvram_cls);
  123         if (error)
  124                 return (error);
  125 
  126         error = bhnd_service_registry_add(&bp->services, dev,
  127             BHND_SERVICE_NVRAM, 0);
  128         if (error) {
  129                 bhnd_nvram_store_free(sc->store);
  130                 return (error);
  131         }
  132 
  133         return (error);
  134 }
  135 
  136 static int
  137 bhnd_nvram_cfe_resume(device_t dev)
  138 {
  139         return (0);
  140 }
  141 
  142 static int
  143 bhnd_nvram_cfe_suspend(device_t dev)
  144 {
  145         return (0);
  146 }
  147 
  148 static int
  149 bhnd_nvram_cfe_detach(device_t dev)
  150 {
  151         struct bcm_platform             *bp;
  152         struct bhnd_nvram_cfe_softc     *sc;
  153         int                              error;
  154 
  155         bp = bcm_get_platform();
  156         sc = device_get_softc(dev);
  157 
  158         error = bhnd_service_registry_remove(&bp->services, dev,
  159             BHND_SERVICE_ANY);
  160         if (error)
  161                 return (error);
  162 
  163         bhnd_nvram_store_free(sc->store);
  164 
  165         return (0);
  166 }
  167 
  168 static int
  169 bhnd_nvram_cfe_getvar(device_t dev, const char *name, void *buf, size_t *len,
  170     bhnd_nvram_type type)
  171 {
  172         struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev);
  173 
  174         return (bhnd_nvram_store_getvar(sc->store, name, buf, len, type));
  175 }
  176 
  177 static int
  178 bhnd_nvram_cfe_setvar(device_t dev, const char *name, const void *buf,
  179     size_t len, bhnd_nvram_type type)
  180 {
  181         struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev);
  182 
  183         return (bhnd_nvram_store_setvar(sc->store, name, buf, len, type));
  184 }
  185 
  186 /**
  187  * Find, open, identify, and initialize an I/O context mapping the CFE NVRAM
  188  * device.
  189  * 
  190  * @param[out]  iocfe           On success, an I/O context mapping the CFE NVRAM
  191  *                              device.
  192  * @param[out]  cls             On success, the identified NVRAM data format
  193  *                              class.
  194  *
  195  * @retval 0            success. the caller inherits ownership of @p iocfe.
  196  * @retval non-zero     if no usable CFE NVRAM device can be found, a standard
  197  *                      unix error will be returned.
  198  */
  199 int
  200 bcm_nvram_find_cfedev(struct bcm_nvram_iocfe *iocfe,
  201     bhnd_nvram_data_class **cls)
  202 {
  203         char    *dname;
  204         int      devinfo;
  205         int      error, result;
  206 
  207         for (u_int i = 0; i < nitems(nvram_cfe_fmts); i++) {
  208                 *cls = nvram_cfe_fmts[i];
  209 
  210                 for (u_int j = 0; j < nitems(nvram_cfe_devs); j++) {
  211                         dname = nvram_cfe_devs[j];
  212 
  213                         /* Does the device exist? */
  214                         if ((devinfo = cfe_getdevinfo(dname)) < 0) {
  215                                 if (devinfo != CFE_ERR_DEVNOTFOUND) {
  216                                         BCM_ERR("cfe_getdevinfo(%s) failed: "
  217                                             "%d\n", dname, devinfo);
  218                                 }
  219 
  220                                 continue;
  221                         }
  222 
  223                         /* Open for reading */
  224                         if ((error = bcm_nvram_iocfe_init(iocfe, dname)))
  225                                 continue;
  226 
  227                         /* Probe */
  228                         result = bhnd_nvram_data_probe(*cls, &iocfe->io);
  229                         if (result <= 0) {
  230                                 /* Found a supporting NVRAM data class */
  231                                 return (0);
  232                         }
  233 
  234                         /* Keep searching */
  235                         bhnd_nvram_io_free(&iocfe->io);
  236                 }
  237         }
  238 
  239         return (ENODEV);
  240 }
  241 
  242 /**
  243  * Initialize a new CFE device-backed I/O context.
  244  *
  245  * The caller is responsible for releasing all resources held by the returned
  246  * I/O context via bhnd_nvram_io_free().
  247  * 
  248  * @param[out]  io      On success, will be initialized as an I/O context for
  249  *                      CFE device @p dname.
  250  * @param       dname   The name of the CFE device to be opened for reading.
  251  *
  252  * @retval 0            success.
  253  * @retval non-zero     if opening @p dname otherwise fails, a standard unix
  254  *                      error will be returned.
  255  */
  256 static int
  257 bcm_nvram_iocfe_init(struct bcm_nvram_iocfe *iocfe, char *dname)
  258 {
  259         nvram_info_t             nvram_info;
  260         int                      cerr, devinfo, dtype, rlen;
  261         int64_t                  nv_offset;
  262         u_int                    nv_size;
  263         bool                     req_blk_erase;
  264         int                      error;
  265 
  266         iocfe->io.iops = &bhnd_nvram_iocfe_ops;
  267         iocfe->dname = dname;
  268 
  269         /* Try to open the device */
  270         iocfe->fd = cfe_open(dname);
  271         if (iocfe->fd <= 0) {
  272                 IOCFE_LOG(iocfe, "cfe_open() failed: %d\n", iocfe->fd);
  273 
  274                 return (ENXIO);
  275         }
  276 
  277         /* Try to fetch device info */
  278         if ((devinfo = cfe_getdevinfo(iocfe->dname)) < 0) {
  279                 IOCFE_LOG(iocfe, "cfe_getdevinfo() failed: %d\n", devinfo);
  280                 error = ENXIO;
  281                 goto failed;
  282         }
  283 
  284         /* Verify device type */
  285         dtype = devinfo & CFE_DEV_MASK;
  286         switch (dtype) {
  287         case CFE_DEV_FLASH:
  288         case CFE_DEV_NVRAM:
  289                 /* Valid device type */
  290                 break;
  291         default:
  292                 IOCFE_LOG(iocfe, "unknown device type: %d\n", dtype);
  293                 error = ENXIO;
  294                 goto failed;
  295         }
  296 
  297         /* Try to fetch nvram info from CFE */
  298         cerr = cfe_ioctl(iocfe->fd, IOCTL_NVRAM_GETINFO,
  299             (unsigned char *)&nvram_info, sizeof(nvram_info), &rlen, 0);
  300         if (cerr == CFE_OK) {
  301                 /* Sanity check the result; must not be a negative integer */
  302                 if (nvram_info.nvram_size < 0 ||
  303                     nvram_info.nvram_offset < 0)
  304                 {
  305                         IOCFE_LOG(iocfe, "invalid NVRAM layout (%d/%d)\n",
  306                             nvram_info.nvram_size, nvram_info.nvram_offset);
  307                         error = ENXIO;
  308                         goto failed;
  309                 }
  310 
  311                 nv_offset       = nvram_info.nvram_offset;
  312                 nv_size         = nvram_info.nvram_size;
  313                 req_blk_erase   = (nvram_info.nvram_eraseflg != 0);
  314         } else if (cerr != CFE_OK && cerr != CFE_ERR_INV_COMMAND) {
  315                 IOCFE_LOG(iocfe, "IOCTL_NVRAM_GETINFO failed: %d\n", cerr);
  316                 error = ENXIO;
  317                 goto failed;
  318         }
  319 
  320         /* Fall back on flash info.
  321          * 
  322          * This is known to be required on the Asus RT-N53 (CFE 5.70.55.33, 
  323          * BBP 1.0.37, BCM5358UB0), where IOCTL_NVRAM_GETINFO returns
  324          * CFE_ERR_INV_COMMAND.
  325          */
  326         if (cerr == CFE_ERR_INV_COMMAND) {
  327                 flash_info_t fi;
  328 
  329                 cerr = cfe_ioctl(iocfe->fd, IOCTL_FLASH_GETINFO,
  330                     (unsigned char *)&fi, sizeof(fi), &rlen, 0);
  331 
  332                 if (cerr != CFE_OK) {
  333                         IOCFE_LOG(iocfe, "IOCTL_FLASH_GETINFO failed %d\n",
  334                             cerr);
  335                         error = ENXIO;
  336                         goto failed;
  337                 }
  338 
  339                 nv_offset       = 0x0;
  340                 nv_size         = fi.flash_size;
  341                 req_blk_erase   = !(fi.flash_flags & FLASH_FLAG_NOERASE);
  342         }
  343 
  344         /* Verify that the full NVRAM layout can be represented via size_t */
  345         if (nv_size > SIZE_MAX || SIZE_MAX - nv_size < nv_offset) {
  346                 IOCFE_LOG(iocfe, "invalid NVRAM layout (%#x/%#jx)\n",
  347                     nv_size, (intmax_t)nv_offset);
  348                 error = ENXIO;
  349                 goto failed;
  350         }
  351 
  352         iocfe->offset = nv_offset;
  353         iocfe->size = nv_size;
  354         iocfe->req_blk_erase = req_blk_erase;
  355 
  356         return (CFE_OK);
  357 
  358 failed:
  359         if (iocfe->fd >= 0)
  360                 cfe_close(iocfe->fd);
  361 
  362         return (error);
  363 }
  364 
  365 static void
  366 bhnd_nvram_iocfe_free(struct bhnd_nvram_io *io)
  367 {
  368         struct bcm_nvram_iocfe  *iocfe = (struct bcm_nvram_iocfe *)io;
  369 
  370         /* CFE I/O instances are statically allocated; we do not need to free
  371          * the instance itself */
  372         cfe_close(iocfe->fd);
  373 }
  374 
  375 static size_t
  376 bhnd_nvram_iocfe_getsize(struct bhnd_nvram_io *io)
  377 {
  378         struct bcm_nvram_iocfe  *iocfe = (struct bcm_nvram_iocfe *)io;
  379         return (iocfe->size);
  380 }
  381 
  382 static int
  383 bhnd_nvram_iocfe_setsize(struct bhnd_nvram_io *io, size_t size)
  384 {
  385         /* unsupported */
  386         return (ENODEV);
  387 }
  388 
  389 static int
  390 bhnd_nvram_iocfe_read_ptr(struct bhnd_nvram_io *io, size_t offset,
  391     const void **ptr, size_t nbytes, size_t *navail)
  392 {
  393         /* unsupported */
  394         return (ENODEV);
  395 }
  396 
  397 static int
  398 bhnd_nvram_iocfe_write_ptr(struct bhnd_nvram_io *io, size_t offset,
  399     void **ptr, size_t nbytes, size_t *navail)
  400 {
  401         /* unsupported */
  402         return (ENODEV);
  403 }
  404 
  405 static int
  406 bhnd_nvram_iocfe_write(struct bhnd_nvram_io *io, size_t offset, void *buffer,
  407     size_t nbytes)
  408 {
  409         /* unsupported */
  410         return (ENODEV);
  411 }
  412 
  413 static int
  414 bhnd_nvram_iocfe_read(struct bhnd_nvram_io *io, size_t offset, void *buffer,
  415     size_t nbytes)
  416 {
  417         struct bcm_nvram_iocfe  *iocfe;
  418         size_t                   remain;
  419         int64_t                  cfe_offset;
  420         int                      nr, nreq;
  421 
  422         iocfe = (struct bcm_nvram_iocfe *)io;
  423 
  424         /* Determine (and validate) the base CFE offset */
  425 #if (SIZE_MAX > INT64_MAX)
  426         if (iocfe->offset > INT64_MAX || offset > INT64_MAX)
  427                 return (ENXIO);
  428 #endif
  429 
  430         if (INT64_MAX - offset < iocfe->offset)
  431                 return (ENXIO);
  432 
  433         cfe_offset = iocfe->offset + offset;
  434 
  435         /* Verify that cfe_offset + nbytes is representable */
  436         if (INT64_MAX - cfe_offset < nbytes)
  437                 return (ENXIO);
  438 
  439         /* Perform the read */
  440         for (remain = nbytes; remain > 0;) {
  441                 void    *p;
  442                 size_t   nread;
  443                 int64_t  cfe_noff;
  444 
  445                 nread = (nbytes - remain);
  446                 cfe_noff = cfe_offset + nread;
  447                 p = ((uint8_t *)buffer + nread);
  448                 nreq = ummin(INT_MAX, remain);
  449 
  450                 nr = cfe_readblk(iocfe->fd, cfe_noff, p, nreq);
  451                 if (nr < 0) {
  452                         IOCFE_LOG(iocfe, "cfe_readblk() failed: %d\n", nr);
  453                         return (ENXIO);
  454                 }
  455 
  456                 /* Check for unexpected short read */
  457                 if (nr == 0 && remain > 0) {
  458                         /* If the request fits entirely within the CFE
  459                          * device range, we shouldn't hit EOF */
  460                         if (remain < iocfe->size &&
  461                             iocfe->size - remain > offset)
  462                         {
  463                                 IOCFE_LOG(iocfe, "cfe_readblk() returned "
  464                                     "unexpected short read (%d/%d)\n", nr,
  465                                     nreq);
  466                                 return (ENXIO);
  467                         }
  468                 }
  469 
  470                 if (nr == 0)
  471                         break;
  472 
  473                 remain -= nr;
  474         }
  475 
  476         /* Check for short read */
  477         if (remain > 0)
  478                 return (ENXIO);
  479 
  480         return (0);
  481 }
  482 
  483 static device_method_t bhnd_nvram_cfe_methods[] = {
  484         /* Device interface */
  485         DEVMETHOD(device_probe,         bhnd_nvram_cfe_probe),
  486         DEVMETHOD(device_attach,        bhnd_nvram_cfe_attach),
  487         DEVMETHOD(device_resume,        bhnd_nvram_cfe_resume),
  488         DEVMETHOD(device_suspend,       bhnd_nvram_cfe_suspend),
  489         DEVMETHOD(device_detach,        bhnd_nvram_cfe_detach),
  490 
  491         /* NVRAM interface */
  492         DEVMETHOD(bhnd_nvram_getvar,    bhnd_nvram_cfe_getvar),
  493         DEVMETHOD(bhnd_nvram_setvar,    bhnd_nvram_cfe_setvar),
  494 
  495         DEVMETHOD_END
  496 };
  497 
  498 DEFINE_CLASS_0(bhnd_nvram, bhnd_nvram_cfe, bhnd_nvram_cfe_methods,
  499     sizeof(struct bhnd_nvram_cfe_softc));
  500 EARLY_DRIVER_MODULE(bhnd_nvram_cfe, nexus, bhnd_nvram_cfe,
  501     bhnd_nvram_devclass, NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);

Cache object: 36ba75e79acff129e6cf2f2f5848818c


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