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/bhnd/nvram/bhnd_nvram_data_bcm.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  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer,
   10  *    without modification.
   11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
   12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
   13  *    redistribution must be conditioned upon including a substantially
   14  *    similar Disclaimer requirement for further binary redistribution.
   15  *
   16  * NO WARRANTY
   17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
   20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
   21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
   22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
   25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   27  * THE POSSIBILITY OF SUCH DAMAGES.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 #include <sys/param.h>
   34 #include <sys/endian.h>
   35 
   36 #ifdef _KERNEL
   37 
   38 #include <sys/bus.h>
   39 #include <sys/ctype.h>
   40 #include <sys/malloc.h>
   41 #include <sys/systm.h>
   42 
   43 #else /* !_KERNEL */
   44 
   45 #include <ctype.h>
   46 #include <stdint.h>
   47 #include <stdio.h>
   48 #include <stdlib.h>
   49 #include <string.h>
   50 
   51 #endif /* _KERNEL */
   52 
   53 #include "bhnd_nvram_private.h"
   54 
   55 #include "bhnd_nvram_datavar.h"
   56 
   57 #include "bhnd_nvram_data_bcmreg.h"
   58 #include "bhnd_nvram_data_bcmvar.h"
   59 
   60 /*
   61  * Broadcom NVRAM data class.
   62  * 
   63  * The Broadcom NVRAM NUL-delimited ASCII format is used by most
   64  * Broadcom SoCs.
   65  * 
   66  * The NVRAM data is encoded as a standard header, followed by series of
   67  * NUL-terminated 'key=value' strings; the end of the stream is denoted
   68  * by a single extra NUL character.
   69  */
   70 
   71 struct bhnd_nvram_bcm;
   72 
   73 static struct bhnd_nvram_bcm_hvar       *bhnd_nvram_bcm_gethdrvar(
   74                                              struct bhnd_nvram_bcm *bcm,
   75                                              const char *name);
   76 static struct bhnd_nvram_bcm_hvar       *bhnd_nvram_bcm_to_hdrvar(
   77                                              struct bhnd_nvram_bcm *bcm,
   78                                              void *cookiep);
   79 static size_t                            bhnd_nvram_bcm_hdrvar_index(
   80                                              struct bhnd_nvram_bcm *bcm,
   81                                              struct bhnd_nvram_bcm_hvar *hvar);
   82 /*
   83  * Set of BCM NVRAM header values that are required to be mirrored in the
   84  * NVRAM data itself.
   85  *
   86  * If they're not included in the parsed NVRAM data, we need to vend the
   87  * header-parsed values with their appropriate keys, and add them in any
   88  * updates to the NVRAM data.
   89  *
   90  * If they're modified in NVRAM, we need to sync the changes with the
   91  * the NVRAM header values.
   92  */
   93 static const struct bhnd_nvram_bcm_hvar bhnd_nvram_bcm_hvars[] = {
   94         {
   95                 .name   = BCM_NVRAM_CFG0_SDRAM_INIT_VAR,
   96                 .type   = BHND_NVRAM_TYPE_UINT16,
   97                 .len    = sizeof(uint16_t),
   98                 .nelem  = 1,
   99         },
  100         {
  101                 .name   = BCM_NVRAM_CFG1_SDRAM_CFG_VAR,
  102                 .type   = BHND_NVRAM_TYPE_UINT16,
  103                 .len    = sizeof(uint16_t),
  104                 .nelem  = 1,
  105         },
  106         {
  107                 .name   = BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR,
  108                 .type   = BHND_NVRAM_TYPE_UINT16,
  109                 .len    = sizeof(uint16_t),
  110                 .nelem  = 1,
  111         },
  112         {
  113                 .name   = BCM_NVRAM_SDRAM_NCDL_VAR,
  114                 .type   = BHND_NVRAM_TYPE_UINT32,
  115                 .len    = sizeof(uint32_t),
  116                 .nelem  = 1,
  117         },
  118 };
  119 
  120 /** BCM NVRAM data class instance */
  121 struct bhnd_nvram_bcm {
  122         struct bhnd_nvram_data           nv;    /**< common instance state */
  123         struct bhnd_nvram_io            *data;  /**< backing buffer */
  124         bhnd_nvram_plist                *opts;  /**< serialization options */
  125 
  126         /** BCM header values */
  127         struct bhnd_nvram_bcm_hvar       hvars[nitems(bhnd_nvram_bcm_hvars)];
  128 
  129         size_t                           count; /**< total variable count */
  130 };
  131 
  132 BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", BHND_NVRAM_DATA_CAP_DEVPATHS,
  133     sizeof(struct bhnd_nvram_bcm))
  134 
  135 static int
  136 bhnd_nvram_bcm_probe(struct bhnd_nvram_io *io)
  137 {
  138         struct bhnd_nvram_bcmhdr        hdr;
  139         int                             error;
  140 
  141         if ((error = bhnd_nvram_io_read(io, 0x0, &hdr, sizeof(hdr))))
  142                 return (error);
  143 
  144         if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
  145                 return (ENXIO);
  146 
  147         if (le32toh(hdr.size) > bhnd_nvram_io_getsize(io))
  148                 return (ENXIO);
  149 
  150         return (BHND_NVRAM_DATA_PROBE_DEFAULT);
  151 }
  152 
  153 /**
  154  * Parser states for bhnd_nvram_bcm_getvar_direct_common().
  155  */
  156 typedef enum {
  157         BCM_PARSE_KEY_START,
  158         BCM_PARSE_KEY_CONT,
  159         BCM_PARSE_KEY,
  160         BCM_PARSE_NEXT_KEY,
  161         BCM_PARSE_VALUE_START,
  162         BCM_PARSE_VALUE
  163 } bcm_parse_state;
  164 
  165 static int
  166 bhnd_nvram_bcm_getvar_direct(struct bhnd_nvram_io *io, const char *name,
  167     void *outp, size_t *olen, bhnd_nvram_type otype)
  168 {
  169         return (bhnd_nvram_bcm_getvar_direct_common(io, name, outp, olen, otype,
  170             true));
  171 }
  172 
  173 /**
  174  * Common BCM/BCMRAW implementation of bhnd_nvram_getvar_direct().
  175  */
  176 int
  177 bhnd_nvram_bcm_getvar_direct_common(struct bhnd_nvram_io *io, const char *name,
  178     void *outp, size_t *olen, bhnd_nvram_type otype, bool have_header)
  179 {
  180         struct bhnd_nvram_bcmhdr         hdr;
  181         char                             buf[512];
  182         bcm_parse_state                  pstate;
  183         size_t                           limit, offset;
  184         size_t                           buflen, bufpos;
  185         size_t                           namelen, namepos;
  186         size_t                           vlen;
  187         int                              error;
  188 
  189         limit = bhnd_nvram_io_getsize(io);
  190         offset = 0;
  191 
  192         /* Fetch and validate the header */
  193         if (have_header) {
  194                 if ((error = bhnd_nvram_io_read(io, offset, &hdr, sizeof(hdr))))
  195                         return (error);
  196 
  197                 if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
  198                         return (ENXIO);
  199 
  200                 offset += sizeof(hdr);
  201                 limit = bhnd_nv_ummin(le32toh(hdr.size), limit);
  202         }
  203 
  204         /* Loop our parser until we find the requested variable, or hit EOF */
  205         pstate = BCM_PARSE_KEY_START;
  206         buflen = 0;
  207         bufpos = 0;
  208         namelen = strlen(name);
  209         namepos = 0;
  210         vlen = 0;
  211 
  212         while ((offset - bufpos) < limit) {
  213                 BHND_NV_ASSERT(bufpos <= buflen,
  214                     ("buf position invalid (%zu > %zu)", bufpos, buflen));
  215                 BHND_NV_ASSERT(buflen <= sizeof(buf),
  216                     ("buf length invalid (%zu > %zu", buflen, sizeof(buf)));
  217 
  218                 /* Repopulate our parse buffer? */
  219                 if (buflen - bufpos == 0) {
  220                         BHND_NV_ASSERT(offset < limit, ("offset overrun"));
  221 
  222                         buflen = bhnd_nv_ummin(sizeof(buf), limit - offset);
  223                         bufpos = 0;
  224 
  225                         error = bhnd_nvram_io_read(io, offset, buf, buflen);
  226                         if (error)
  227                                 return (error);
  228 
  229                         offset += buflen;
  230                 }
  231 
  232                 switch (pstate) {
  233                 case BCM_PARSE_KEY_START:
  234                         BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!"));
  235 
  236                         /* An extra '\0' denotes NVRAM EOF */
  237                         if (buf[bufpos] == '\0')
  238                                 return (ENOENT);
  239 
  240                         /* Reset name matching position */
  241                         namepos = 0;
  242 
  243                         /* Start name matching */
  244                         pstate = BCM_PARSE_KEY_CONT;
  245                         break;
  246 
  247                 case BCM_PARSE_KEY_CONT: {
  248                         size_t navail, nleft;
  249 
  250                         nleft = namelen - namepos;
  251                         navail = bhnd_nv_ummin(buflen - bufpos, nleft);
  252 
  253                         if (strncmp(name+namepos, buf+bufpos, navail) == 0) {
  254                                 /* Matched */
  255                                 namepos += navail;
  256                                 bufpos += navail;
  257 
  258                                 /* If we've matched the full variable name,
  259                                  * look for its trailing delimiter */
  260                                 if (namepos == namelen)
  261                                         pstate = BCM_PARSE_KEY;
  262                         } else {
  263                                 /* No match; advance to next entry and restart
  264                                  * name matching */
  265                                 pstate = BCM_PARSE_NEXT_KEY;
  266                         }
  267 
  268                         break;
  269                 }
  270 
  271                 case BCM_PARSE_KEY:
  272                         BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!"));
  273 
  274                         if (buf[bufpos] == '=') {
  275                                 /* Key fully matched; advance past '=' and
  276                                  * parse the value */
  277                                 bufpos++;
  278                                 pstate = BCM_PARSE_VALUE_START;
  279                         } else {
  280                                 /* No match; advance to next entry and restart
  281                                  * name matching */
  282                                 pstate = BCM_PARSE_NEXT_KEY;
  283                         }
  284 
  285                         break;
  286 
  287                 case BCM_PARSE_NEXT_KEY: {
  288                         const char *p;
  289 
  290                         /* Scan for a '\0' terminator */
  291                         p = memchr(buf+bufpos, '\0', buflen - bufpos);
  292 
  293                         if (p != NULL) {
  294                                 /* Found entry terminator; restart name
  295                                  * matching at next entry */
  296                                 pstate = BCM_PARSE_KEY_START;
  297                                 bufpos = (p - buf) + 1 /* skip '\0' */;
  298                         } else {
  299                                 /* Consumed full buffer looking for '\0'; 
  300                                  * force repopulation of the buffer and
  301                                  * retry */
  302                                 bufpos = buflen;
  303                         }
  304 
  305                         break;
  306                 }
  307 
  308                 case BCM_PARSE_VALUE_START: {
  309                         const char *p;
  310 
  311                         /* Scan for a '\0' terminator */
  312                         p = memchr(buf+bufpos, '\0', buflen - bufpos);
  313 
  314                         if (p != NULL) {
  315                                 /* Found entry terminator; parse the value */
  316                                 vlen = p - &buf[bufpos];
  317                                 pstate = BCM_PARSE_VALUE;
  318 
  319                         } else if (p == NULL && offset == limit) {
  320                                 /* Hit EOF without a terminating '\0';
  321                                  * treat the entry as implicitly terminated */
  322                                 vlen = buflen - bufpos;
  323                                 pstate = BCM_PARSE_VALUE;
  324 
  325                         } else if (p == NULL && bufpos > 0) {
  326                                 size_t  nread;
  327 
  328                                 /* Move existing value data to start of
  329                                  * buffer */
  330                                 memmove(buf, buf+bufpos, buflen - bufpos);
  331                                 buflen = bufpos;
  332                                 bufpos = 0;
  333 
  334                                 /* Populate full buffer to allow retry of
  335                                  * value parsing */
  336                                 nread = bhnd_nv_ummin(sizeof(buf) - buflen,
  337                                     limit - offset);
  338 
  339                                 error = bhnd_nvram_io_read(io, offset,
  340                                     buf+buflen, nread);
  341                                 if (error)
  342                                         return (error);
  343 
  344                                 offset += nread;
  345                                 buflen += nread;
  346                         } else {
  347                                 /* Value exceeds our buffer capacity */
  348                                 BHND_NV_LOG("cannot parse value for '%s' "
  349                                     "(exceeds %zu byte limit)\n", name,
  350                                     sizeof(buf));
  351 
  352                                 return (ENXIO);
  353                         }
  354 
  355                         break;
  356                 }
  357 
  358                 case BCM_PARSE_VALUE:
  359                         BHND_NV_ASSERT(vlen <= buflen, ("value buf overrun"));
  360 
  361                         return (bhnd_nvram_value_coerce(buf+bufpos, vlen,
  362                             BHND_NVRAM_TYPE_STRING, outp, olen, otype));
  363                 }
  364         }
  365 
  366         /* Variable not found */
  367         return (ENOENT);
  368 }
  369 
  370 static int
  371 bhnd_nvram_bcm_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
  372     bhnd_nvram_plist *options, void *outp, size_t *olen)
  373 {
  374         struct bhnd_nvram_bcmhdr         hdr;
  375         bhnd_nvram_prop                 *prop;
  376         size_t                           limit, nbytes;
  377         uint32_t                         sdram_ncdl;
  378         uint16_t                         sdram_init, sdram_cfg, sdram_refresh;
  379         uint8_t                          bcm_ver, crc8;
  380         int                              error;
  381 
  382         /* Determine output byte limit */
  383         if (outp != NULL)
  384                 limit = *olen;
  385         else
  386                 limit = 0;
  387 
  388         /* Fetch required header variables */
  389 #define PROPS_GET_HDRVAR(_name, _dest, _type)   do {                    \
  390                 const char *name = BCM_NVRAM_ ## _name ## _VAR; \
  391                 if (!bhnd_nvram_plist_contains(props, name)) {          \
  392                         BHND_NV_LOG("missing required property: %s\n",  \
  393                             name);                                      \
  394                         return (EFTYPE);                                \
  395                 }                                                       \
  396                                                                         \
  397                 error = bhnd_nvram_plist_get_encoded(props, name,       \
  398                     (_dest), sizeof(*(_dest)),                          \
  399                     BHND_NVRAM_TYPE_ ##_type);                          \
  400                 if (error) {                                            \
  401                         BHND_NV_LOG("error reading required header "    \
  402                             "%s property: %d\n", name, error);          \
  403                         return (EFTYPE);                                \
  404                 }                                                       \
  405 } while (0)
  406 
  407         PROPS_GET_HDRVAR(SDRAM_NCDL,            &sdram_ncdl,    UINT32);
  408         PROPS_GET_HDRVAR(CFG0_SDRAM_INIT,       &sdram_init,    UINT16);
  409         PROPS_GET_HDRVAR(CFG1_SDRAM_CFG,        &sdram_cfg,     UINT16);
  410         PROPS_GET_HDRVAR(CFG1_SDRAM_REFRESH,    &sdram_refresh, UINT16);
  411 
  412 #undef  PROPS_GET_HDRVAR
  413 
  414         /* Fetch BCM nvram version from options */
  415         if (options != NULL &&
  416             bhnd_nvram_plist_contains(options, BCM_NVRAM_ENCODE_OPT_VERSION))
  417         {
  418                 error = bhnd_nvram_plist_get_uint8(options,
  419                     BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver);
  420                 if (error) {
  421                         BHND_NV_LOG("error reading %s uint8 option value: %d\n",
  422                             BCM_NVRAM_ENCODE_OPT_VERSION, error);
  423                         return (EINVAL);
  424                 }
  425         } else {
  426                 bcm_ver = BCM_NVRAM_CFG0_VER_DEFAULT;
  427         }
  428 
  429         /* Construct our header */
  430         hdr = (struct bhnd_nvram_bcmhdr) {
  431                 .magic = htole32(BCM_NVRAM_MAGIC),
  432                 .size = 0,
  433                 .cfg0 = 0,
  434                 .cfg1 = 0,
  435                 .sdram_ncdl = htole32(sdram_ncdl)
  436         };
  437 
  438         hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, 0x0);
  439         hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER, bcm_ver);
  440         hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_SDRAM_INIT,
  441             htole16(sdram_init));
  442 
  443         hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_CFG,
  444             htole16(sdram_cfg));
  445         hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_REFRESH,
  446             htole16(sdram_refresh));
  447 
  448         /* Write the header */
  449         nbytes = sizeof(hdr);
  450         if (limit >= nbytes)
  451                 memcpy(outp, &hdr, sizeof(hdr));
  452 
  453         /* Write all properties */
  454         prop = NULL;
  455         while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
  456                 const char      *name;
  457                 char            *p;
  458                 size_t           prop_limit;
  459                 size_t           name_len, value_len;
  460 
  461                 if (outp == NULL || limit < nbytes) {
  462                         p = NULL;
  463                         prop_limit = 0;
  464                 } else {
  465                         p = ((char *)outp) + nbytes;
  466                         prop_limit = limit - nbytes;
  467                 }
  468 
  469                 /* Fetch and write name + '=' to output */
  470                 name = bhnd_nvram_prop_name(prop);
  471                 name_len = strlen(name) + 1;
  472 
  473                 if (prop_limit > name_len) {
  474                         memcpy(p, name, name_len - 1);
  475                         p[name_len - 1] = '=';
  476 
  477                         prop_limit -= name_len;
  478                         p += name_len;
  479                 } else {
  480                         prop_limit = 0;
  481                         p = NULL;
  482                 }
  483 
  484                 /* Advance byte count */
  485                 if (SIZE_MAX - nbytes < name_len)
  486                         return (EFTYPE); /* would overflow size_t */
  487 
  488                 nbytes += name_len;
  489 
  490                 /* Attempt to write NUL-terminated value to output */
  491                 value_len = prop_limit;
  492                 error = bhnd_nvram_prop_encode(prop, p, &value_len,
  493                     BHND_NVRAM_TYPE_STRING);
  494 
  495                 /* If encoding failed for any reason other than ENOMEM (which
  496                  * we'll detect and report after encoding all properties),
  497                  * return immediately */
  498                 if (error && error != ENOMEM) {
  499                         BHND_NV_LOG("error serializing %s to required type "
  500                             "%s: %d\n", name,
  501                             bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
  502                             error);
  503                         return (error);
  504                 }
  505 
  506                 /* Advance byte count */
  507                 if (SIZE_MAX - nbytes < value_len)
  508                         return (EFTYPE); /* would overflow size_t */
  509 
  510                 nbytes += value_len;
  511         }
  512 
  513         /* Write terminating '\0' */
  514         if (limit > nbytes)
  515                 *((char *)outp + nbytes) = '\0';
  516 
  517         if (nbytes == SIZE_MAX)
  518                 return (EFTYPE); /* would overflow size_t */
  519         else
  520                 nbytes++;
  521 
  522         /* Update header length; this must fit within the header's 32-bit size
  523          * field */
  524         if (nbytes <= UINT32_MAX) {
  525                 hdr.size = (uint32_t)nbytes;
  526         } else {
  527                 BHND_NV_LOG("size %zu exceeds maximum supported size of %u "
  528                     "bytes\n", nbytes, UINT32_MAX);
  529                 return (EFTYPE);
  530         }
  531 
  532         /* Provide required length */
  533         *olen = nbytes;
  534         if (limit < *olen) {
  535                 if (outp == NULL)
  536                         return (0);
  537 
  538                 return (ENOMEM);
  539         }
  540 
  541         /* Calculate the CRC value */
  542         BHND_NV_ASSERT(nbytes >= BCM_NVRAM_CRC_SKIP, ("invalid output size"));
  543         crc8 = bhnd_nvram_crc8((uint8_t *)outp + BCM_NVRAM_CRC_SKIP,
  544             nbytes - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
  545 
  546         /* Update CRC and write the finalized header */
  547         BHND_NV_ASSERT(nbytes >= sizeof(hdr), ("invalid output size"));
  548         hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, crc8);
  549         memcpy(outp, &hdr, sizeof(hdr));
  550 
  551         return (0);
  552 }
  553 
  554 /**
  555  * Initialize @p bcm with the provided NVRAM data mapped by @p src.
  556  * 
  557  * @param bcm A newly allocated data instance.
  558  */
  559 static int
  560 bhnd_nvram_bcm_init(struct bhnd_nvram_bcm *bcm, struct bhnd_nvram_io *src)
  561 {
  562         struct bhnd_nvram_bcmhdr         hdr;
  563         uint8_t                         *p;
  564         void                            *ptr;
  565         size_t                           io_offset, io_size;
  566         uint8_t                          crc, valid, bcm_ver;
  567         int                              error;
  568 
  569         if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr))))
  570                 return (error);
  571 
  572         if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
  573                 return (ENXIO);
  574 
  575         /* Fetch the actual NVRAM image size */
  576         io_size = le32toh(hdr.size);
  577         if (io_size < sizeof(hdr)) {
  578                 /* The header size must include the header itself */
  579                 BHND_NV_LOG("corrupt header size: %zu\n", io_size);
  580                 return (EINVAL);
  581         }
  582 
  583         if (io_size > bhnd_nvram_io_getsize(src)) {
  584                 BHND_NV_LOG("header size %zu exceeds input size %zu\n",
  585                     io_size, bhnd_nvram_io_getsize(src));
  586                 return (EINVAL);
  587         }
  588 
  589         /* Allocate a buffer large enough to hold the NVRAM image, and
  590          * an extra EOF-signaling NUL (on the chance it's missing from the
  591          * source data) */
  592         if (io_size == SIZE_MAX)
  593                 return (ENOMEM);
  594 
  595         bcm->data = bhnd_nvram_iobuf_empty(io_size, io_size + 1);
  596         if (bcm->data == NULL)
  597                 return (ENOMEM);
  598 
  599         /* Fetch a pointer into our backing buffer and copy in the
  600          * NVRAM image. */
  601         error = bhnd_nvram_io_write_ptr(bcm->data, 0x0, &ptr, io_size, NULL);
  602         if (error)
  603                 return (error);
  604 
  605         p = ptr;
  606         if ((error = bhnd_nvram_io_read(src, 0x0, p, io_size)))
  607                 return (error);
  608 
  609         /* Verify the CRC */
  610         valid = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC);
  611         crc = bhnd_nvram_crc8(p + BCM_NVRAM_CRC_SKIP,
  612             io_size - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
  613 
  614         if (crc != valid) {
  615                 BHND_NV_LOG("warning: NVRAM CRC error (crc=%#hhx, "
  616                     "expected=%hhx)\n", crc, valid);
  617         }
  618 
  619         /* Populate header variable definitions */
  620 #define BCM_READ_HDR_VAR(_name, _dest, _swap) do {              \
  621         struct bhnd_nvram_bcm_hvar *data;                               \
  622         data = bhnd_nvram_bcm_gethdrvar(bcm, _name ##_VAR);             \
  623         BHND_NV_ASSERT(data != NULL,                                            \
  624             ("no such header variable: " __STRING(_name)));             \
  625                                                                         \
  626                                                                         \
  627         data->value. _dest = _swap(BCM_NVRAM_GET_BITS(                  \
  628             hdr. _name ## _FIELD, _name));                              \
  629 } while(0)
  630 
  631         BCM_READ_HDR_VAR(BCM_NVRAM_CFG0_SDRAM_INIT,     u16, le16toh);
  632         BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_CFG,      u16, le16toh);
  633         BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_REFRESH,  u16, le16toh);
  634         BCM_READ_HDR_VAR(BCM_NVRAM_SDRAM_NCDL,          u32, le32toh);
  635 
  636         _Static_assert(nitems(bcm->hvars) == 4, "missing initialization for"
  637             "NVRAM header variable(s)");
  638 
  639 #undef BCM_READ_HDR_VAR
  640 
  641         /* Process the buffer */
  642         bcm->count = 0;
  643         io_offset = sizeof(hdr);
  644         while (io_offset < io_size) {
  645                 char            *envp;
  646                 const char      *name, *value;
  647                 size_t           envp_len;
  648                 size_t           name_len, value_len;
  649 
  650                 /* Parse the key=value string */
  651                 envp = (char *) (p + io_offset);
  652                 envp_len = strnlen(envp, io_size - io_offset);
  653                 error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,
  654                                              &name_len, &value, &value_len);
  655                 if (error) {
  656                         BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",
  657                             io_offset, error);
  658                         return (error);
  659                 }
  660 
  661                 /* Insert a '\0' character, replacing the '=' delimiter and
  662                  * allowing us to vend references directly to the variable
  663                  * name */
  664                 *(envp + name_len) = '\0';
  665 
  666                 /* Record any NVRAM variables that mirror our header variables.
  667                  * This is a brute-force search -- for the amount of data we're
  668                  * operating on, it shouldn't be an issue. */
  669                 for (size_t i = 0; i < nitems(bcm->hvars); i++) {
  670                         struct bhnd_nvram_bcm_hvar      *hvar;
  671                         union bhnd_nvram_bcm_hvar_value  hval;
  672                         size_t                           hval_len;
  673 
  674                         hvar = &bcm->hvars[i];
  675 
  676                         /* Already matched? */
  677                         if (hvar->envp != NULL)
  678                                 continue;
  679 
  680                         /* Name matches? */
  681                         if ((strcmp(name, hvar->name)) != 0)
  682                                 continue;
  683 
  684                         /* Save pointer to mirrored envp */
  685                         hvar->envp = envp;
  686 
  687                         /* Check for stale value */
  688                         hval_len = sizeof(hval);
  689                         error = bhnd_nvram_value_coerce(value, value_len,
  690                             BHND_NVRAM_TYPE_STRING, &hval, &hval_len,
  691                             hvar->type);
  692                         if (error) {
  693                                 /* If parsing fails, we can likely only make
  694                                  * things worse by trying to synchronize the
  695                                  * variables */
  696                                 BHND_NV_LOG("error parsing header variable "
  697                                     "'%s=%s': %d\n", name, value, error);
  698                         } else if (hval_len != hvar->len) {
  699                                 hvar->stale = true;
  700                         } else if (memcmp(&hval, &hvar->value, hval_len) != 0) {
  701                                 hvar->stale = true;
  702                         }
  703                 }
  704 
  705                 /* Seek past the value's terminating '\0' */
  706                 io_offset += envp_len;
  707                 if (io_offset == io_size) {
  708                         BHND_NV_LOG("missing terminating NUL at offset %#zx\n",
  709                             io_offset);
  710                         return (EINVAL);
  711                 }
  712 
  713                 if (*(p + io_offset) != '\0') {
  714                         BHND_NV_LOG("invalid terminator '%#hhx' at offset "
  715                             "%#zx\n", *(p + io_offset), io_offset);
  716                         return (EINVAL);
  717                 }
  718 
  719                 /* Update variable count */
  720                 bcm->count++;
  721 
  722                 /* Seek to the next record */
  723                 if (++io_offset == io_size) {
  724                         char ch;
  725 
  726                         /* Hit EOF without finding a terminating NUL
  727                          * byte; we need to grow our buffer and append
  728                          * it */
  729                         io_size++;
  730                         if ((error = bhnd_nvram_io_setsize(bcm->data, io_size)))
  731                                 return (error);
  732 
  733                         /* Write NUL byte */
  734                         ch = '\0';
  735                         error = bhnd_nvram_io_write(bcm->data, io_size-1, &ch,
  736                             sizeof(ch));
  737                         if (error)
  738                                 return (error);
  739                 }
  740 
  741                 /* Check for explicit EOF (encoded as a single empty NUL
  742                  * terminated string) */
  743                 if (*(p + io_offset) == '\0')
  744                         break;
  745         }
  746 
  747         /* Add non-mirrored header variables to total count variable */
  748         for (size_t i = 0; i < nitems(bcm->hvars); i++) {
  749                 if (bcm->hvars[i].envp == NULL)
  750                         bcm->count++;
  751         }
  752 
  753         /* Populate serialization options from our header */
  754         bcm_ver = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER);
  755         error = bhnd_nvram_plist_append_bytes(bcm->opts,
  756             BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver, sizeof(bcm_ver),
  757             BHND_NVRAM_TYPE_UINT8);
  758         if (error)
  759                 return (error);
  760 
  761         return (0);
  762 }
  763 
  764 static int
  765 bhnd_nvram_bcm_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
  766 {
  767         struct bhnd_nvram_bcm   *bcm;
  768         int                      error;
  769 
  770         bcm = (struct bhnd_nvram_bcm *)nv;
  771 
  772         /* Populate default BCM mirrored header variable set */
  773         _Static_assert(sizeof(bcm->hvars) == sizeof(bhnd_nvram_bcm_hvars),
  774             "hvar declarations must match bhnd_nvram_bcm_hvars template");
  775         memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars));
  776 
  777         /* Allocate (empty) option list, to be populated by
  778          * bhnd_nvram_bcm_init() */
  779         bcm->opts = bhnd_nvram_plist_new();
  780         if (bcm->opts == NULL)
  781                 return (ENOMEM);
  782 
  783         /* Parse the BCM input data and initialize our backing
  784          * data representation */
  785         if ((error = bhnd_nvram_bcm_init(bcm, io))) {
  786                 bhnd_nvram_bcm_free(nv);
  787                 return (error);
  788         }
  789 
  790         return (0);
  791 }
  792 
  793 static void
  794 bhnd_nvram_bcm_free(struct bhnd_nvram_data *nv)
  795 {
  796         struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
  797 
  798         if (bcm->data != NULL)
  799                 bhnd_nvram_io_free(bcm->data);
  800 
  801         if (bcm->opts != NULL)
  802                 bhnd_nvram_plist_release(bcm->opts);
  803 }
  804 
  805 size_t
  806 bhnd_nvram_bcm_count(struct bhnd_nvram_data *nv)
  807 {
  808         struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
  809         return (bcm->count);
  810 }
  811 
  812 static bhnd_nvram_plist *
  813 bhnd_nvram_bcm_options(struct bhnd_nvram_data *nv)
  814 {
  815         struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
  816         return (bcm->opts);
  817 }
  818 
  819 static uint32_t
  820 bhnd_nvram_bcm_caps(struct bhnd_nvram_data *nv)
  821 {
  822         return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
  823 }
  824 
  825 static const char *
  826 bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep)
  827 {
  828         struct bhnd_nvram_bcm           *bcm;
  829         struct bhnd_nvram_bcm_hvar      *hvar, *hvar_next;
  830         const void                      *ptr;
  831         const char                      *envp, *basep;
  832         size_t                           io_size, io_offset;
  833         int                              error;
  834 
  835         bcm = (struct bhnd_nvram_bcm *)nv;
  836 
  837         io_offset = sizeof(struct bhnd_nvram_bcmhdr);
  838         io_size = bhnd_nvram_io_getsize(bcm->data) - io_offset;
  839 
  840         /* Map backing buffer */
  841         error = bhnd_nvram_io_read_ptr(bcm->data, io_offset, &ptr, io_size,
  842             NULL);
  843         if (error) {
  844                 BHND_NV_LOG("error mapping backing buffer: %d\n", error);
  845                 return (NULL);
  846         }
  847 
  848         basep = ptr;
  849 
  850         /* If cookiep pointers into our header variable array, handle as header
  851          * variable iteration. */
  852         hvar = bhnd_nvram_bcm_to_hdrvar(bcm, *cookiep);
  853         if (hvar != NULL) {
  854                 size_t idx;
  855 
  856                 /* Advance to next entry, if any */
  857                 idx = bhnd_nvram_bcm_hdrvar_index(bcm, hvar) + 1;
  858 
  859                 /* Find the next header-defined variable that isn't defined in
  860                  * the NVRAM data, start iteration there */
  861                 for (size_t i = idx; i < nitems(bcm->hvars); i++) {
  862                         hvar_next = &bcm->hvars[i];
  863                         if (hvar_next->envp != NULL && !hvar_next->stale)
  864                                 continue;
  865 
  866                         *cookiep = hvar_next;
  867                         return (hvar_next->name);
  868                 }
  869 
  870                 /* No further header-defined variables; iteration
  871                  * complete */
  872                 return (NULL);
  873         }
  874 
  875         /* Handle standard NVRAM data iteration */
  876         if (*cookiep == NULL) {
  877                 /* Start at the first NVRAM data record */
  878                 envp = basep;
  879         } else {
  880                 /* Seek to next record */
  881                 envp = *cookiep;
  882                 envp += strlen(envp) + 1;       /* key + '\0' */
  883                 envp += strlen(envp) + 1;       /* value + '\0' */
  884         }
  885 
  886         /*
  887          * Skip entries that have an existing header variable entry that takes
  888          * precedence over the NVRAM data value.
  889          * 
  890          * The header's value will be provided when performing header variable
  891          * iteration
  892          */
  893          while ((size_t)(envp - basep) < io_size && *envp != '\0') {
  894                 /* Locate corresponding header variable */
  895                 hvar = NULL;
  896                 for (size_t i = 0; i < nitems(bcm->hvars); i++) {
  897                         if (bcm->hvars[i].envp != envp)
  898                                 continue;
  899 
  900                         hvar = &bcm->hvars[i];
  901                         break;
  902                 }
  903 
  904                 /* If no corresponding hvar entry, or the entry does not take
  905                  * precedence over this NVRAM value, we can safely return this
  906                  * value as-is. */
  907                 if (hvar == NULL || !hvar->stale)
  908                         break;
  909 
  910                 /* Seek to next record */
  911                 envp += strlen(envp) + 1;       /* key + '\0' */
  912                 envp += strlen(envp) + 1;       /* value + '\0' */
  913          }
  914 
  915         /* On NVRAM data EOF, try switching to header variables */
  916         if ((size_t)(envp - basep) == io_size || *envp == '\0') {
  917                 /* Find first valid header variable */
  918                 for (size_t i = 0; i < nitems(bcm->hvars); i++) {
  919                         if (bcm->hvars[i].envp != NULL)
  920                                 continue;
  921                         
  922                         *cookiep = &bcm->hvars[i];
  923                         return (bcm->hvars[i].name);
  924                 }
  925 
  926                 /* No header variables */
  927                 return (NULL);
  928         }
  929 
  930         *cookiep = __DECONST(void *, envp);
  931         return (envp);
  932 }
  933 
  934 static void *
  935 bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name)
  936 {
  937         return (bhnd_nvram_data_generic_find(nv, name));
  938 }
  939 
  940 static int
  941 bhnd_nvram_bcm_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
  942     void *cookiep2)
  943 {
  944         struct bhnd_nvram_bcm           *bcm;
  945         struct bhnd_nvram_bcm_hvar      *hvar1, *hvar2;
  946 
  947         bcm = (struct bhnd_nvram_bcm *)nv;
  948 
  949         hvar1 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep1);
  950         hvar2 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep2);
  951 
  952         /* Header variables are always ordered below any variables defined
  953          * in the BCM data */
  954         if (hvar1 != NULL && hvar2 == NULL) {
  955                 return (1);     /* hvar follows non-hvar */
  956         } else if (hvar1 == NULL && hvar2 != NULL) {
  957                 return (-1);    /* non-hvar precedes hvar */
  958         }
  959 
  960         /* Otherwise, both cookies are either hvars or non-hvars. We can
  961          * safely fall back on pointer order, which will provide a correct
  962          * ordering matching the behavior of bhnd_nvram_data_next() for
  963          * both cases */
  964         if (cookiep1 < cookiep2)
  965                 return (-1);
  966 
  967         if (cookiep1 > cookiep2)
  968                 return (1);
  969 
  970         return (0);
  971 }
  972 
  973 static int
  974 bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
  975     size_t *len, bhnd_nvram_type type)
  976 {
  977         return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
  978 }
  979 
  980 static int
  981 bhnd_nvram_bcm_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
  982     bhnd_nvram_val **value)
  983 {
  984         return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
  985 }
  986 
  987 static const void *
  988 bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
  989     size_t *len, bhnd_nvram_type *type)
  990 {
  991         struct bhnd_nvram_bcm           *bcm;
  992         struct bhnd_nvram_bcm_hvar      *hvar;
  993         const char                      *envp;
  994 
  995         bcm = (struct bhnd_nvram_bcm *)nv;
  996 
  997         /* Handle header variables */
  998         if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
  999                 BHND_NV_ASSERT(bhnd_nvram_value_check_aligned(&hvar->value,
 1000                     hvar->len, hvar->type) == 0, ("value misaligned"));
 1001 
 1002                 *type = hvar->type;
 1003                 *len = hvar->len;
 1004                 return (&hvar->value);
 1005         }
 1006 
 1007         /* Cookie points to key\0value\0 -- get the value address */
 1008         BHND_NV_ASSERT(cookiep != NULL, ("NULL cookiep"));
 1009 
 1010         envp = cookiep;
 1011         envp += strlen(envp) + 1;       /* key + '\0' */
 1012         *len = strlen(envp) + 1;        /* value + '\0' */
 1013         *type = BHND_NVRAM_TYPE_STRING;
 1014 
 1015         return (envp);
 1016 }
 1017 
 1018 static const char *
 1019 bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
 1020 {
 1021         struct bhnd_nvram_bcm           *bcm;
 1022         struct bhnd_nvram_bcm_hvar      *hvar;
 1023 
 1024         bcm = (struct bhnd_nvram_bcm *)nv;
 1025 
 1026         /* Handle header variables */
 1027         if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
 1028                 return (hvar->name);
 1029         }
 1030 
 1031         /* Cookie points to key\0value\0 */
 1032         return (cookiep);
 1033 }
 1034 
 1035 static int
 1036 bhnd_nvram_bcm_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
 1037     bhnd_nvram_val *value, bhnd_nvram_val **result)
 1038 {
 1039         bhnd_nvram_val  *str;
 1040         int              error;
 1041 
 1042         /* Name (trimmed of any path prefix) must be valid */
 1043         if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
 1044                 return (EINVAL);
 1045 
 1046         /* Value must be bcm-formatted string */
 1047         error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
 1048             value, BHND_NVRAM_VAL_DYNAMIC);
 1049         if (error)
 1050                 return (error);
 1051 
 1052         /* Success. Transfer result ownership to the caller. */
 1053         *result = str;
 1054         return (0);
 1055 }
 1056 
 1057 static int
 1058 bhnd_nvram_bcm_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
 1059 {
 1060         /* We permit deletion of any variable */
 1061         return (0);
 1062 }
 1063 
 1064 /**
 1065  * Return the internal BCM data reference for a header-defined variable
 1066  * with @p name, or NULL if none exists.
 1067  */
 1068 static struct bhnd_nvram_bcm_hvar *
 1069 bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm *bcm, const char *name)
 1070 {
 1071         for (size_t i = 0; i < nitems(bcm->hvars); i++) {
 1072                 if (strcmp(bcm->hvars[i].name, name) == 0)
 1073                         return (&bcm->hvars[i]);
 1074         }
 1075 
 1076         /* Not found */
 1077         return (NULL);
 1078 }
 1079 
 1080 /**
 1081  * If @p cookiep references a header-defined variable, return the
 1082  * internal BCM data reference. Otherwise, returns NULL.
 1083  */
 1084 static struct bhnd_nvram_bcm_hvar *
 1085 bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm *bcm, void *cookiep)
 1086 {
 1087 #ifdef BHND_NVRAM_INVARIANTS                                                                                                                                                                                                                                
 1088         uintptr_t base, ptr;
 1089 #endif
 1090 
 1091         /* If the cookie falls within the hvar array, it's a
 1092          * header variable cookie */
 1093         if (nitems(bcm->hvars) == 0)
 1094                 return (NULL);
 1095 
 1096         if (cookiep < (void *)&bcm->hvars[0])
 1097                 return (NULL);
 1098 
 1099         if (cookiep > (void *)&bcm->hvars[nitems(bcm->hvars)-1])
 1100                 return (NULL);
 1101 
 1102 #ifdef BHND_NVRAM_INVARIANTS
 1103         base = (uintptr_t)bcm->hvars;
 1104         ptr = (uintptr_t)cookiep;
 1105 
 1106         BHND_NV_ASSERT((ptr - base) % sizeof(bcm->hvars[0]) == 0,
 1107             ("misaligned hvar pointer %p/%p", cookiep, bcm->hvars));
 1108 #endif /* INVARIANTS */
 1109 
 1110         return ((struct bhnd_nvram_bcm_hvar *)cookiep);
 1111 }
 1112 
 1113 /**
 1114  * Return the index of @p hdrvar within @p bcm's backing hvars array.
 1115  */
 1116 static size_t
 1117 bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm *bcm,
 1118     struct bhnd_nvram_bcm_hvar *hdrvar)
 1119 {
 1120         BHND_NV_ASSERT(bhnd_nvram_bcm_to_hdrvar(bcm, (void *)hdrvar) != NULL,
 1121             ("%p is not a valid hdrvar reference", hdrvar));
 1122 
 1123         return (hdrvar - &bcm->hvars[0]);
 1124 }

Cache object: df266131d4e13434d2674c9ce4947e84


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