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_btxt.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) 2015-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/endian.h>
   34 
   35 #ifdef _KERNEL
   36 
   37 #include <sys/param.h>
   38 #include <sys/ctype.h>
   39 #include <sys/malloc.h>
   40 #include <sys/systm.h>
   41 
   42 #else /* !_KERNEL */
   43 
   44 #include <ctype.h>
   45 #include <stdint.h>
   46 #include <stdlib.h>
   47 #include <string.h>
   48 
   49 #endif /* _KERNEL */
   50 
   51 #include "bhnd_nvram_private.h"
   52 
   53 #include "bhnd_nvram_datavar.h"
   54 
   55 #include "bhnd_nvram_data_bcmreg.h"     /* for BCM_NVRAM_MAGIC */
   56 
   57 /**
   58  * Broadcom "Board Text" data class.
   59  *
   60  * This format is used to provide external NVRAM data for some
   61  * fullmac WiFi devices, and as an input format when programming
   62  * NVRAM/SPROM/OTP.
   63  */
   64 
   65 struct bhnd_nvram_btxt {
   66         struct bhnd_nvram_data   nv;    /**< common instance state */
   67         struct bhnd_nvram_io    *data;  /**< memory-backed board text data */
   68         size_t                   count; /**< variable count */
   69 };
   70 
   71 BHND_NVRAM_DATA_CLASS_DEFN(btxt, "Broadcom Board Text",
   72     BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_btxt))
   73 
   74 /** Minimal identification header */
   75 union bhnd_nvram_btxt_ident {
   76         uint32_t        bcm_magic;
   77         char            btxt[8];
   78 };
   79 
   80 static void     *bhnd_nvram_btxt_offset_to_cookiep(struct bhnd_nvram_btxt *btxt,
   81                  size_t io_offset);
   82 static size_t    bhnd_nvram_btxt_cookiep_to_offset(struct bhnd_nvram_btxt *btxt,
   83                      void *cookiep);
   84 
   85 static int      bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io,
   86                     size_t offset, size_t *line_len, size_t *env_len);
   87 static int      bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io,
   88                     size_t *offset);
   89 static int      bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io,
   90                     size_t *offset);
   91 
   92 static int
   93 bhnd_nvram_btxt_probe(struct bhnd_nvram_io *io)
   94 {
   95         union bhnd_nvram_btxt_ident     ident;
   96         char                            c;
   97         int                             error;
   98 
   99         /* Look at the initial header for something that looks like 
  100          * an ASCII board text file */
  101         if ((error = bhnd_nvram_io_read(io, 0x0, &ident, sizeof(ident))))
  102                 return (error);
  103 
  104         /* The BCM NVRAM format uses a 'FLSH' little endian magic value, which
  105          * shouldn't be interpreted as BTXT */
  106         if (le32toh(ident.bcm_magic) == BCM_NVRAM_MAGIC)
  107                 return (ENXIO);
  108 
  109         /* Don't match on non-ASCII/non-printable data */
  110         for (size_t i = 0; i < nitems(ident.btxt); i++) {
  111                 c = ident.btxt[i];
  112                 if (!bhnd_nv_isprint(c))
  113                         return (ENXIO);
  114         }
  115 
  116         /* The first character should either be a valid key char (alpha),
  117          * whitespace, or the start of a comment ('#') */
  118         c = ident.btxt[0];
  119         if (!bhnd_nv_isspace(c) && !bhnd_nv_isalpha(c) && c != '#')
  120                 return (ENXIO);
  121 
  122         /* We assert a low priority, given that we've only scanned an
  123          * initial few bytes of the file. */
  124         return (BHND_NVRAM_DATA_PROBE_MAYBE);
  125 }
  126 
  127 /**
  128  * Parser states for bhnd_nvram_bcm_getvar_direct_common().
  129  */
  130 typedef enum {
  131         BTXT_PARSE_LINE_START,
  132         BTXT_PARSE_KEY,
  133         BTXT_PARSE_KEY_END,
  134         BTXT_PARSE_NEXT_LINE,
  135         BTXT_PARSE_VALUE_START,
  136         BTXT_PARSE_VALUE
  137 } btxt_parse_state;
  138 
  139 static int
  140 bhnd_nvram_btxt_getvar_direct(struct bhnd_nvram_io *io, const char *name,
  141     void *outp, size_t *olen, bhnd_nvram_type otype)
  142 {
  143         char                             buf[512];
  144         btxt_parse_state                 pstate;
  145         size_t                           limit, offset;
  146         size_t                           buflen, bufpos;
  147         size_t                           namelen, namepos;
  148         size_t                           vlen;
  149         int                              error;
  150 
  151         limit = bhnd_nvram_io_getsize(io);
  152         offset = 0;
  153 
  154         /* Loop our parser until we find the requested variable, or hit EOF */
  155         pstate = BTXT_PARSE_LINE_START;
  156         buflen = 0;
  157         bufpos = 0;
  158         namelen = strlen(name);
  159         namepos = 0;
  160         vlen = 0;
  161 
  162         while ((offset - bufpos) < limit) {
  163                 BHND_NV_ASSERT(bufpos <= buflen,
  164                     ("buf position invalid (%zu > %zu)", bufpos, buflen));
  165                 BHND_NV_ASSERT(buflen <= sizeof(buf),
  166                     ("buf length invalid (%zu > %zu", buflen, sizeof(buf)));
  167 
  168                 /* Repopulate our parse buffer? */
  169                 if (buflen - bufpos == 0) {
  170                         BHND_NV_ASSERT(offset < limit, ("offset overrun"));
  171 
  172                         buflen = bhnd_nv_ummin(sizeof(buf), limit - offset);
  173                         bufpos = 0;
  174 
  175                         error = bhnd_nvram_io_read(io, offset, buf, buflen);
  176                         if (error)
  177                                 return (error);
  178 
  179                         offset += buflen;
  180                 }
  181 
  182                 switch (pstate) {
  183                 case BTXT_PARSE_LINE_START:
  184                         BHND_NV_ASSERT(bufpos < buflen, ("empty buffer!"));
  185 
  186                         /* Reset name matching position */
  187                         namepos = 0;
  188 
  189                         /* Trim any leading whitespace */
  190                         while (bufpos < buflen && bhnd_nv_isspace(buf[bufpos]))
  191                         {
  192                                 bufpos++;
  193                         }
  194 
  195                         if (bufpos == buflen) {
  196                                 /* Continue parsing the line */
  197                                 pstate = BTXT_PARSE_LINE_START;
  198                         } else if (bufpos < buflen && buf[bufpos] == '#') {
  199                                 /* Comment; skip to next line */
  200                                 pstate = BTXT_PARSE_NEXT_LINE;
  201                         } else {
  202                                 /* Start name matching */
  203                                 pstate = BTXT_PARSE_KEY;
  204                         }
  205 
  206                         break;
  207 
  208                 case BTXT_PARSE_KEY: {
  209                         size_t navail, nleft;
  210 
  211                         nleft = namelen - namepos;
  212                         navail = bhnd_nv_ummin(buflen - bufpos, nleft);
  213 
  214                         if (strncmp(name+namepos, buf+bufpos, navail) == 0) {
  215                                 /* Matched */
  216                                 namepos += navail;
  217                                 bufpos += navail;
  218 
  219                                 if (namepos == namelen) {
  220                                         /* Matched the full variable; look for
  221                                          * its trailing delimiter */
  222                                         pstate = BTXT_PARSE_KEY_END;
  223                                 } else {
  224                                         /* Continue matching the name */
  225                                         pstate = BTXT_PARSE_KEY;
  226                                 }
  227                         } else {
  228                                 /* No match; advance to next entry and restart
  229                                  * name matching */
  230                                 pstate = BTXT_PARSE_NEXT_LINE;
  231                         }
  232 
  233                         break;
  234                 }
  235 
  236                 case BTXT_PARSE_KEY_END:
  237                         BHND_NV_ASSERT(bufpos < buflen, ("empty buffer!"));
  238 
  239                         if (buf[bufpos] == '=') {
  240                                 /* Key fully matched; advance past '=' and
  241                                  * parse the value */
  242                                 bufpos++;
  243                                 pstate = BTXT_PARSE_VALUE_START;
  244                         } else {
  245                                 /* No match; advance to next line and restart
  246                                  * name matching */
  247                                 pstate = BTXT_PARSE_NEXT_LINE;
  248                         }
  249 
  250                         break;
  251 
  252                 case BTXT_PARSE_NEXT_LINE: {
  253                         const char *p;
  254 
  255                         /* Scan for a '\r', '\n', or '\r\n' terminator */
  256                         p = memchr(buf+bufpos, '\n', buflen - bufpos);
  257                         if (p == NULL)
  258                                 p = memchr(buf+bufpos, '\r', buflen - bufpos);
  259 
  260                         if (p != NULL) {
  261                                 /* Found entry terminator; restart name
  262                                  * matching at next line */
  263                                 pstate = BTXT_PARSE_LINE_START;
  264                                 bufpos = (p - buf);
  265                         } else {
  266                                 /* Consumed full buffer looking for newline; 
  267                                  * force repopulation of the buffer and
  268                                  * retry */
  269                                 pstate = BTXT_PARSE_NEXT_LINE;
  270                                 bufpos = buflen;
  271                         }
  272 
  273                         break;
  274                 }
  275 
  276                 case BTXT_PARSE_VALUE_START: {
  277                         const char *p;
  278 
  279                         /* Scan for a terminating newline */
  280                         p = memchr(buf+bufpos, '\n', buflen - bufpos);
  281                         if (p == NULL)
  282                                 p = memchr(buf+bufpos, '\r', buflen - bufpos);
  283 
  284                         if (p != NULL) {
  285                                 /* Found entry terminator; parse the value */
  286                                 vlen = p - &buf[bufpos];
  287                                 pstate = BTXT_PARSE_VALUE;
  288 
  289                         } else if (p == NULL && offset == limit) {
  290                                 /* Hit EOF without a terminating newline;
  291                                  * treat the entry as implicitly terminated */
  292                                 vlen = buflen - bufpos;
  293                                 pstate = BTXT_PARSE_VALUE;
  294 
  295                         } else if (p == NULL && bufpos > 0) {
  296                                 size_t  nread;
  297 
  298                                 /* Move existing value data to start of
  299                                  * buffer */
  300                                 memmove(buf, buf+bufpos, buflen - bufpos);
  301                                 buflen = bufpos;
  302                                 bufpos = 0;
  303 
  304                                 /* Populate full buffer to allow retry of
  305                                  * value parsing */
  306                                 nread = bhnd_nv_ummin(sizeof(buf) - buflen,
  307                                     limit - offset);
  308 
  309                                 error = bhnd_nvram_io_read(io, offset,
  310                                     buf+buflen, nread);
  311                                 if (error)
  312                                         return (error);
  313 
  314                                 offset += nread;
  315                                 buflen += nread;
  316                         } else {
  317                                 /* Value exceeds our buffer capacity */
  318                                 BHND_NV_LOG("cannot parse value for '%s' "
  319                                     "(exceeds %zu byte limit)\n", name,
  320                                     sizeof(buf));
  321 
  322                                 return (ENXIO);
  323                         }
  324 
  325                         break;
  326                 }
  327 
  328                 case BTXT_PARSE_VALUE:
  329                         BHND_NV_ASSERT(vlen <= buflen, ("value buf overrun"));
  330 
  331                         /* Trim any trailing whitespace */
  332                         while (vlen > 0 && bhnd_nv_isspace(buf[bufpos+vlen-1]))
  333                                 vlen--;
  334 
  335                         /* Write the value to the caller's buffer */
  336                         return (bhnd_nvram_value_coerce(buf+bufpos, vlen,
  337                             BHND_NVRAM_TYPE_STRING, outp, olen, otype));
  338                 }
  339         }
  340 
  341         /* Variable not found */
  342         return (ENOENT);
  343 }
  344 
  345 static int
  346 bhnd_nvram_btxt_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
  347     bhnd_nvram_plist *options, void *outp, size_t *olen)
  348 {
  349         bhnd_nvram_prop *prop;
  350         size_t           limit, nbytes;
  351         int              error;
  352 
  353         /* Determine output byte limit */
  354         if (outp != NULL)
  355                 limit = *olen;
  356         else
  357                 limit = 0;
  358 
  359         nbytes = 0;
  360 
  361         /* Write all properties */
  362         prop = NULL;
  363         while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
  364                 const char      *name;
  365                 char            *p;
  366                 size_t           prop_limit;
  367                 size_t           name_len, value_len;
  368 
  369                 if (outp == NULL || limit < nbytes) {
  370                         p = NULL;
  371                         prop_limit = 0;
  372                 } else {
  373                         p = ((char *)outp) + nbytes;
  374                         prop_limit = limit - nbytes;
  375                 }
  376 
  377                 /* Fetch and write 'name=' to output */
  378                 name = bhnd_nvram_prop_name(prop);
  379                 name_len = strlen(name) + 1;
  380 
  381                 if (prop_limit > name_len) {
  382                         memcpy(p, name, name_len - 1);
  383                         p[name_len - 1] = '=';
  384 
  385                         prop_limit -= name_len;
  386                         p += name_len;
  387                 } else {
  388                         prop_limit = 0;
  389                         p = NULL;
  390                 }
  391 
  392                 /* Advance byte count */
  393                 if (SIZE_MAX - nbytes < name_len)
  394                         return (EFTYPE); /* would overflow size_t */
  395 
  396                 nbytes += name_len;
  397 
  398                 /* Write NUL-terminated value to output, rewrite NUL as
  399                  * '\n' record delimiter */
  400                 value_len = prop_limit;
  401                 error = bhnd_nvram_prop_encode(prop, p, &value_len,
  402                     BHND_NVRAM_TYPE_STRING);
  403                 if (p != NULL && error == 0) {
  404                         /* Replace trailing '\0' with newline */
  405                         BHND_NV_ASSERT(value_len > 0, ("string length missing "
  406                             "minimum required trailing NUL"));
  407 
  408                         *(p + (value_len - 1)) = '\n';
  409                 } else if (error && error != ENOMEM) {
  410                         /* If encoding failed for any reason other than ENOMEM
  411                          * (which we'll detect and report after encoding all
  412                          * properties), return immediately */
  413                         BHND_NV_LOG("error serializing %s to required type "
  414                             "%s: %d\n", name,
  415                             bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
  416                             error);
  417                         return (error);
  418                 }
  419 
  420                 /* Advance byte count */
  421                 if (SIZE_MAX - nbytes < value_len)
  422                         return (EFTYPE); /* would overflow size_t */
  423 
  424                 nbytes += value_len;
  425         }
  426 
  427         /* Provide required length */
  428         *olen = nbytes;
  429         if (limit < *olen) {
  430                 if (outp == NULL)
  431                         return (0);
  432 
  433                 return (ENOMEM);
  434         }
  435 
  436         return (0);
  437 }
  438 
  439 /**
  440  * Initialize @p btxt with the provided board text data mapped by @p src.
  441  * 
  442  * @param btxt A newly allocated data instance.
  443  */
  444 static int
  445 bhnd_nvram_btxt_init(struct bhnd_nvram_btxt *btxt, struct bhnd_nvram_io *src)
  446 {
  447         const void              *ptr;
  448         const char              *name, *value;
  449         size_t                   name_len, value_len;
  450         size_t                   line_len, env_len;
  451         size_t                   io_offset, io_size, str_size;
  452         int                      error;
  453 
  454         BHND_NV_ASSERT(btxt->data == NULL, ("btxt data already allocated"));
  455 
  456         if ((btxt->data = bhnd_nvram_iobuf_copy(src)) == NULL)
  457                 return (ENOMEM);
  458 
  459         io_size = bhnd_nvram_io_getsize(btxt->data);
  460         io_offset = 0;
  461 
  462         /* Fetch a pointer mapping the entirity of the board text data */
  463         error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_size, NULL);
  464         if (error)
  465                 return (error);
  466 
  467         /* Determine the actual size, minus any terminating NUL. We
  468          * parse NUL-terminated C strings, but do not include NUL termination
  469          * in our internal or serialized representations */
  470         str_size = strnlen(ptr, io_size);
  471 
  472         /* If the terminating NUL is not found at the end of the buffer,
  473          * this is BCM-RAW or other NUL-delimited NVRAM format. */
  474         if (str_size < io_size && str_size + 1 < io_size)
  475                 return (EINVAL);
  476 
  477         /* Adjust buffer size to account for NUL termination (if any) */
  478         io_size = str_size;
  479         if ((error = bhnd_nvram_io_setsize(btxt->data, io_size)))
  480                 return (error);
  481 
  482         /* Process the buffer */
  483         btxt->count = 0;
  484         while (io_offset < io_size) {
  485                 const void      *envp;
  486 
  487                 /* Seek to the next key=value entry */
  488                 if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset)))
  489                         return (error);
  490 
  491                 /* Determine the entry and line length */
  492                 error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset,
  493                     &line_len, &env_len);
  494                 if (error)
  495                         return (error);
  496 
  497                 /* EOF? */
  498                 if (env_len == 0) {
  499                         BHND_NV_ASSERT(io_offset == io_size,
  500                            ("zero-length record returned from "
  501                             "bhnd_nvram_btxt_seek_next()"));
  502                         break;
  503                 }
  504 
  505                 /* Fetch a pointer to the line start */
  506                 error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &envp,
  507                     env_len, NULL);
  508                 if (error)
  509                         return (error);
  510 
  511                 /* Parse the key=value string */
  512                 error = bhnd_nvram_parse_env(envp, env_len, '=', &name,
  513                     &name_len, &value, &value_len);
  514                 if (error) {
  515                         return (error);
  516                 }
  517 
  518                 /* Insert a '\0' character, replacing the '=' delimiter and
  519                  * allowing us to vend references directly to the variable
  520                  * name */
  521                 error = bhnd_nvram_io_write(btxt->data, io_offset+name_len,
  522                     &(char){'\0'}, 1);
  523                 if (error)
  524                         return (error);
  525 
  526                 /* Add to variable count */
  527                 btxt->count++;
  528 
  529                 /* Advance past EOL */
  530                 io_offset += line_len;
  531         }
  532 
  533         return (0);
  534 }
  535 
  536 static int
  537 bhnd_nvram_btxt_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
  538 {
  539         struct bhnd_nvram_btxt  *btxt;
  540         int                      error;
  541 
  542         /* Allocate and initialize the BTXT data instance */
  543         btxt = (struct bhnd_nvram_btxt *)nv;
  544 
  545         /* Parse the BTXT input data and initialize our backing
  546          * data representation */
  547         if ((error = bhnd_nvram_btxt_init(btxt, io))) {
  548                 bhnd_nvram_btxt_free(nv);
  549                 return (error);
  550         }
  551 
  552         return (0);
  553 }
  554 
  555 static void
  556 bhnd_nvram_btxt_free(struct bhnd_nvram_data *nv)
  557 {
  558         struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv;
  559         if (btxt->data != NULL)
  560                 bhnd_nvram_io_free(btxt->data);
  561 }
  562 
  563 size_t
  564 bhnd_nvram_btxt_count(struct bhnd_nvram_data *nv)
  565 {
  566         struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv;
  567         return (btxt->count);
  568 }
  569 
  570 static bhnd_nvram_plist *
  571 bhnd_nvram_btxt_options(struct bhnd_nvram_data *nv)
  572 {
  573         return (NULL);
  574 }
  575 
  576 static uint32_t
  577 bhnd_nvram_btxt_caps(struct bhnd_nvram_data *nv)
  578 {
  579         return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
  580 }
  581 
  582 static void *
  583 bhnd_nvram_btxt_find(struct bhnd_nvram_data *nv, const char *name)
  584 {
  585         return (bhnd_nvram_data_generic_find(nv, name));
  586 }
  587 
  588 static const char *
  589 bhnd_nvram_btxt_next(struct bhnd_nvram_data *nv, void **cookiep)
  590 {
  591         struct bhnd_nvram_btxt  *btxt;
  592         const void              *nptr;
  593         size_t                   io_offset, io_size;
  594         int                      error;
  595 
  596         btxt = (struct bhnd_nvram_btxt *)nv;
  597 
  598         io_size = bhnd_nvram_io_getsize(btxt->data);
  599 
  600         if (*cookiep == NULL) {
  601                 /* Start search at initial file offset */
  602                 io_offset = 0x0;
  603         } else {
  604                 /* Start search after the current entry */
  605                 io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, *cookiep);
  606 
  607                 /* Scan past the current entry by finding the next newline */
  608                 error = bhnd_nvram_btxt_seek_eol(btxt->data, &io_offset);
  609                 if (error) {
  610                         BHND_NV_LOG("unexpected error in seek_eol(): %d\n",
  611                             error);
  612                         return (NULL);
  613                 }
  614         }
  615 
  616         /* Already at EOF? */
  617         if (io_offset == io_size)
  618                 return (NULL);
  619 
  620         /* Seek to the first valid entry, or EOF */
  621         if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset))) {
  622                 BHND_NV_LOG("unexpected error in seek_next(): %d\n", error);
  623                 return (NULL);
  624         }
  625 
  626         /* Hit EOF? */
  627         if (io_offset == io_size)
  628                 return (NULL);
  629 
  630         /* Provide the new cookie for this offset */
  631         *cookiep = bhnd_nvram_btxt_offset_to_cookiep(btxt, io_offset);
  632 
  633         /* Fetch the name pointer; it must be at least 1 byte long */
  634         error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &nptr, 1, NULL);
  635         if (error) {
  636                 BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error);
  637                 return (NULL);
  638         }
  639 
  640         /* Return the name pointer */
  641         return (nptr);
  642 }
  643 
  644 static int
  645 bhnd_nvram_btxt_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
  646     void *cookiep2)
  647 {
  648         if (cookiep1 < cookiep2)
  649                 return (-1);
  650 
  651         if (cookiep1 > cookiep2)
  652                 return (1);
  653 
  654         return (0);
  655 }
  656 
  657 static int
  658 bhnd_nvram_btxt_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
  659     size_t *len, bhnd_nvram_type type)
  660 {
  661         return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
  662 }
  663 
  664 static int
  665 bhnd_nvram_btxt_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
  666     bhnd_nvram_val **value)
  667 {
  668         return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
  669 }
  670 
  671 const void *
  672 bhnd_nvram_btxt_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
  673     size_t *len, bhnd_nvram_type *type)
  674 {
  675         struct bhnd_nvram_btxt  *btxt;
  676         const void              *eptr;
  677         const char              *vptr;
  678         size_t                   io_offset, io_size;
  679         size_t                   line_len, env_len;
  680         int                      error;
  681 
  682         btxt = (struct bhnd_nvram_btxt *)nv;
  683 
  684         io_size = bhnd_nvram_io_getsize(btxt->data);
  685         io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, cookiep);
  686 
  687         /* At EOF? */
  688         if (io_offset == io_size)
  689                 return (NULL);
  690 
  691         /* Determine the entry length */
  692         error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset, &line_len,
  693             &env_len);
  694         if (error) {
  695                 BHND_NV_LOG("unexpected error in entry_len(): %d\n", error);
  696                 return (NULL);
  697         }
  698 
  699         /* Fetch the entry's value pointer and length */
  700         error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &eptr, env_len,
  701             NULL);
  702         if (error) {
  703                 BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error);
  704                 return (NULL);
  705         }
  706 
  707         error = bhnd_nvram_parse_env(eptr, env_len, '\0', NULL, NULL, &vptr,
  708             len);
  709         if (error) {
  710                 BHND_NV_LOG("unexpected error in parse_env(): %d\n", error);
  711                 return (NULL);
  712         }
  713 
  714         /* Type is always CSTR */
  715         *type = BHND_NVRAM_TYPE_STRING;
  716 
  717         return (vptr);
  718 }
  719 
  720 static const char *
  721 bhnd_nvram_btxt_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
  722 {
  723         struct bhnd_nvram_btxt  *btxt;
  724         const void              *ptr;
  725         size_t                   io_offset, io_size;
  726         int                      error;
  727 
  728         btxt = (struct bhnd_nvram_btxt *)nv;
  729 
  730         io_size = bhnd_nvram_io_getsize(btxt->data);
  731         io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, cookiep);
  732 
  733         /* At EOF? */
  734         if (io_offset == io_size)
  735                 BHND_NV_PANIC("invalid cookiep: %p", cookiep);
  736 
  737         /* Variable name is found directly at the given offset; trailing
  738          * NUL means we can assume that it's at least 1 byte long */
  739         error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &ptr, 1, NULL);
  740         if (error)
  741                 BHND_NV_PANIC("unexpected error in read_ptr(): %d\n", error);
  742 
  743         return (ptr);
  744 }
  745 
  746 /**
  747  * Return a cookiep for the given I/O offset.
  748  */
  749 static void *
  750 bhnd_nvram_btxt_offset_to_cookiep(struct bhnd_nvram_btxt *btxt,
  751     size_t io_offset)
  752 {
  753         const void      *ptr;
  754         int              error;
  755 
  756         BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(btxt->data),
  757             ("io_offset %zu out-of-range", io_offset));
  758         BHND_NV_ASSERT(io_offset < UINTPTR_MAX,
  759             ("io_offset %#zx exceeds UINTPTR_MAX", io_offset));
  760 
  761         error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_offset, NULL);
  762         if (error)
  763                 BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error);
  764 
  765         ptr = (const uint8_t *)ptr + io_offset;
  766         return (__DECONST(void *, ptr));
  767 }
  768 
  769 /* Convert a cookiep back to an I/O offset */
  770 static size_t
  771 bhnd_nvram_btxt_cookiep_to_offset(struct bhnd_nvram_btxt *btxt, void *cookiep)
  772 {
  773         const void      *ptr;
  774         intptr_t         offset;
  775         size_t           io_size;
  776         int              error;
  777 
  778         BHND_NV_ASSERT(cookiep != NULL, ("null cookiep"));
  779 
  780         io_size = bhnd_nvram_io_getsize(btxt->data);
  781         error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_size, NULL);
  782         if (error)
  783                 BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error);
  784 
  785         offset = (const uint8_t *)cookiep - (const uint8_t *)ptr;
  786         BHND_NV_ASSERT(offset >= 0, ("invalid cookiep"));
  787         BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)"));
  788         BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)"));
  789 
  790         return ((size_t)offset);
  791 }
  792 
  793 /* Determine the entry length and env 'key=value' string length of the entry
  794  * at @p offset */
  795 static int
  796 bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io, size_t offset,
  797     size_t *line_len, size_t *env_len)
  798 {
  799         const uint8_t   *baseptr, *p;
  800         const void      *rbuf;
  801         size_t           nbytes;
  802         int              error;
  803 
  804         /* Fetch read buffer */
  805         if ((error = bhnd_nvram_io_read_ptr(io, offset, &rbuf, 0, &nbytes)))
  806                 return (error);
  807 
  808         /* Find record termination (EOL, or '#') */
  809         p = rbuf;
  810         baseptr = rbuf;
  811         while ((size_t)(p - baseptr) < nbytes) {
  812                 if (*p == '#' || *p == '\n' || *p == '\r')
  813                         break;
  814 
  815                 p++;
  816         }
  817 
  818         /* Got line length, now trim any trailing whitespace to determine
  819          * actual env length */
  820         *line_len = p - baseptr;
  821         *env_len = *line_len;
  822 
  823         for (size_t i = 0; i < *line_len; i++) {
  824                 char c = baseptr[*line_len - i - 1];
  825                 if (!bhnd_nv_isspace(c))
  826                         break;
  827 
  828                 *env_len -= 1;
  829         }
  830 
  831         return (0);
  832 }
  833 
  834 /* Seek past the next line ending (\r, \r\n, or \n) */
  835 static int
  836 bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io, size_t *offset)
  837 {
  838         const uint8_t   *baseptr, *p;
  839         const void      *rbuf;
  840         size_t           nbytes;
  841         int              error;
  842 
  843         /* Fetch read buffer */
  844         if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes)))
  845                 return (error);
  846 
  847         baseptr = rbuf;
  848         p = rbuf;
  849         while ((size_t)(p - baseptr) < nbytes) {
  850                 char c = *p;
  851 
  852                 /* Advance to next char. The next position may be EOF, in which
  853                  * case a read will be invalid */
  854                 p++;
  855 
  856                 if (c == '\r') {
  857                         /* CR, check for optional LF */
  858                         if ((size_t)(p - baseptr) < nbytes) {
  859                                 if (*p == '\n')
  860                                         p++;
  861                         }
  862 
  863                         break;
  864                 } else if (c == '\n') {
  865                         break;
  866                 }
  867         }
  868 
  869         /* Hit newline or EOF */
  870         *offset += (p - baseptr);
  871         return (0);
  872 }
  873 
  874 /* Seek to the next valid non-comment line (or EOF) */
  875 static int
  876 bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io, size_t *offset)
  877 {
  878         const uint8_t   *baseptr, *p;
  879         const void      *rbuf;
  880         size_t           nbytes;
  881         int              error;
  882 
  883         /* Fetch read buffer */
  884         if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes)))
  885                 return (error);
  886 
  887         /* Skip leading whitespace and comments */
  888         baseptr = rbuf;
  889         p = rbuf;
  890         while ((size_t)(p - baseptr) < nbytes) {
  891                 char c = *p;
  892 
  893                 /* Skip whitespace */
  894                 if (bhnd_nv_isspace(c)) {
  895                         p++;
  896                         continue;
  897                 }
  898 
  899                 /* Skip entire comment line */
  900                 if (c == '#') {
  901                         size_t line_off = *offset + (p - baseptr);
  902 
  903                         if ((error = bhnd_nvram_btxt_seek_eol(io, &line_off)))
  904                                 return (error);
  905 
  906                         p = baseptr + (line_off - *offset);
  907                         continue;
  908                 }
  909 
  910                 /* Non-whitespace, non-comment */
  911                 break;
  912         }
  913 
  914         *offset += (p - baseptr);
  915         return (0);
  916 }
  917 
  918 static int
  919 bhnd_nvram_btxt_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
  920     bhnd_nvram_val *value, bhnd_nvram_val **result)
  921 {
  922         bhnd_nvram_val  *str;
  923         const char      *inp;
  924         bhnd_nvram_type  itype;
  925         size_t           ilen;
  926         int              error;
  927 
  928         /* Name (trimmed of any path prefix) must be valid */
  929         if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
  930                 return (EINVAL);
  931 
  932         /* Value must be bcm-formatted string */
  933         error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
  934             value, BHND_NVRAM_VAL_DYNAMIC);
  935         if (error)
  936                 return (error);
  937 
  938         /* Value string must not contain our record delimiter character ('\n'),
  939          * or our comment character ('#') */
  940         inp = bhnd_nvram_val_bytes(str, &ilen, &itype);
  941         BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_STRING, ("non-string value"));
  942         for (size_t i = 0; i < ilen; i++) {
  943                 switch (inp[i]) {
  944                 case '\n':
  945                 case '#':
  946                         BHND_NV_LOG("invalid character (%#hhx) in value\n",
  947                             inp[i]);
  948                         bhnd_nvram_val_release(str);
  949                         return (EINVAL);
  950                 }
  951         }
  952 
  953         /* Success. Transfer result ownership to the caller. */
  954         *result = str;
  955         return (0);
  956 }
  957 
  958 static int
  959 bhnd_nvram_btxt_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
  960 {
  961         /* We permit deletion of any variable */
  962         return (0);
  963 }

Cache object: 47bd6614fcc23c0c7a38efd07c254a3d


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