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_tlv.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 #ifdef _KERNEL
   34 #include <sys/param.h>
   35 #include <sys/ctype.h>
   36 #include <sys/limits.h>
   37 #include <sys/malloc.h>
   38 #include <sys/systm.h>
   39 #else /* !_KERNEL */
   40 #include <ctype.h>
   41 #include <errno.h>
   42 #include <stdint.h>
   43 #include <stdio.h>
   44 #include <stdlib.h>
   45 #include <string.h>
   46 #endif /* _KERNEL */
   47 
   48 #include "bhnd_nvram_private.h"
   49 
   50 #include "bhnd_nvram_datavar.h"
   51 
   52 #include "bhnd_nvram_data_tlvreg.h"
   53 
   54 /*
   55  * CFE TLV NVRAM data class.
   56  * 
   57  * The CFE-defined TLV NVRAM format is used on the WGT634U.
   58  */
   59 
   60 struct bhnd_nvram_tlv {
   61         struct bhnd_nvram_data   nv;    /**< common instance state */
   62         struct bhnd_nvram_io    *data;  /**< backing buffer */
   63         size_t                   count; /**< variable count */
   64 };
   65 
   66 BHND_NVRAM_DATA_CLASS_DEFN(tlv, "WGT634U", BHND_NVRAM_DATA_CAP_DEVPATHS,
   67     sizeof(struct bhnd_nvram_tlv))
   68 
   69 /** Minimal TLV_ENV record header */
   70 struct bhnd_nvram_tlv_env_hdr {
   71         uint8_t         tag;
   72         uint8_t         size;
   73 } __packed;
   74 
   75 /** Minimal TLV_ENV record */
   76 struct bhnd_nvram_tlv_env {
   77         struct bhnd_nvram_tlv_env_hdr   hdr;
   78         uint8_t                         flags;
   79         char                            envp[];
   80 } __packed;
   81 
   82 /* Return the length in bytes of an TLV_ENV's envp data */
   83 #define NVRAM_TLV_ENVP_DATA_LEN(_env)   \
   84         (((_env)->hdr.size < sizeof((_env)->flags)) ? 0 :       \
   85             ((_env)->hdr.size - sizeof((_env)->flags)))
   86 
   87 /* Maximum supported length of the envp data field, in bytes */
   88 #define NVRAM_TLV_ENVP_DATA_MAX_LEN     \
   89         (UINT8_MAX - sizeof(uint8_t) /* flags */)
   90 
   91 static int                               bhnd_nvram_tlv_parse_size(
   92                                              struct bhnd_nvram_io *io,
   93                                              size_t *size);
   94 
   95 static int                               bhnd_nvram_tlv_next_record(
   96                                              struct bhnd_nvram_io *io,
   97                                              size_t *next, size_t *offset,
   98                                              uint8_t *tag);
   99 
  100 static struct bhnd_nvram_tlv_env        *bhnd_nvram_tlv_next_env(
  101                                              struct bhnd_nvram_tlv *tlv,
  102                                              size_t *next, void **cookiep);
  103 
  104 static struct bhnd_nvram_tlv_env        *bhnd_nvram_tlv_get_env(
  105                                              struct bhnd_nvram_tlv *tlv,
  106                                              void *cookiep);
  107 
  108 static void                             *bhnd_nvram_tlv_to_cookie(
  109                                              struct bhnd_nvram_tlv *tlv,
  110                                              size_t io_offset);
  111 static size_t                            bhnd_nvram_tlv_to_offset(
  112                                              struct bhnd_nvram_tlv *tlv,
  113                                              void *cookiep);
  114 
  115 static int
  116 bhnd_nvram_tlv_probe(struct bhnd_nvram_io *io)
  117 {
  118         struct bhnd_nvram_tlv_env       ident;
  119         size_t                          nbytes;
  120         int                             error;
  121 
  122         nbytes = bhnd_nvram_io_getsize(io);
  123 
  124         /* Handle what might be an empty TLV image */
  125         if (nbytes < sizeof(ident)) {
  126                 uint8_t tag;
  127 
  128                 /* Fetch just the first tag */
  129                 error = bhnd_nvram_io_read(io, 0x0, &tag, sizeof(tag));
  130                 if (error)
  131                         return (error);
  132 
  133                 /* This *could* be an empty TLV image, but all we're
  134                  * testing for here is a single 0x0 byte followed by EOF */
  135                 if (tag == NVRAM_TLV_TYPE_END)
  136                         return (BHND_NVRAM_DATA_PROBE_MAYBE);
  137 
  138                 return (ENXIO);
  139         }
  140 
  141         /* Otherwise, look at the initial header for a valid TLV ENV tag,
  142          * plus one byte of the entry data */
  143         error = bhnd_nvram_io_read(io, 0x0, &ident,
  144             sizeof(ident) + sizeof(ident.envp[0]));
  145         if (error)
  146                 return (error);
  147 
  148         /* First entry should be a variable record (which we statically
  149          * assert as being defined to use a single byte size field) */
  150         if (ident.hdr.tag != NVRAM_TLV_TYPE_ENV)
  151                 return (ENXIO);
  152 
  153         _Static_assert(NVRAM_TLV_TYPE_ENV & NVRAM_TLV_TF_U8_LEN,
  154             "TYPE_ENV is not a U8-sized field");
  155 
  156         /* The entry must be at least 3 characters ('x=\0') in length */
  157         if (ident.hdr.size < 3)
  158                 return (ENXIO);
  159 
  160         /* The first character should be a valid key char (alpha) */
  161         if (!bhnd_nv_isalpha(ident.envp[0]))
  162                 return (ENXIO);
  163 
  164         return (BHND_NVRAM_DATA_PROBE_DEFAULT);
  165 }
  166 
  167 static int
  168 bhnd_nvram_tlv_getvar_direct(struct bhnd_nvram_io *io, const char *name,
  169     void *buf, size_t *len, bhnd_nvram_type type)
  170 {
  171         struct bhnd_nvram_tlv_env        env;
  172         char                             data[NVRAM_TLV_ENVP_DATA_MAX_LEN];
  173         size_t                           data_len;
  174         const char                      *key, *value;
  175         size_t                           keylen, vlen;
  176         size_t                           namelen;
  177         size_t                           next, off;
  178         uint8_t                          tag;
  179         int                              error;
  180 
  181         namelen = strlen(name);
  182 
  183         /* Iterate over the input looking for the requested variable */
  184         next = 0;
  185         while (!(error = bhnd_nvram_tlv_next_record(io, &next, &off, &tag))) {
  186                 switch (tag) {
  187                 case NVRAM_TLV_TYPE_END:
  188                         /* Not found */
  189                         return (ENOENT);
  190 
  191                 case NVRAM_TLV_TYPE_ENV:
  192                         /* Read the record header */
  193                         error = bhnd_nvram_io_read(io, off, &env, sizeof(env));
  194                         if (error) {
  195                                 BHND_NV_LOG("error reading TLV_ENV record "
  196                                     "header: %d\n", error);
  197                                 return (error);
  198                         }
  199 
  200                         /* Read the record data */
  201                         data_len = NVRAM_TLV_ENVP_DATA_LEN(&env);
  202                         error = bhnd_nvram_io_read(io, off + sizeof(env), data,
  203                             data_len);
  204                         if (error) {
  205                                 BHND_NV_LOG("error reading TLV_ENV record "
  206                                     "data: %d\n", error);
  207                                 return (error);
  208                         }
  209 
  210                         /* Parse the key=value string */
  211                         error = bhnd_nvram_parse_env(data, data_len, '=', &key,
  212                             &keylen, &value, &vlen);
  213                         if (error) {
  214                                 BHND_NV_LOG("error parsing TLV_ENV data: %d\n",
  215                                     error);
  216                                 return (error);
  217                         }
  218 
  219                         /* Match against requested variable name */
  220                         if (keylen == namelen && 
  221                             strncmp(key, name, namelen) == 0)
  222                         {
  223                                 return (bhnd_nvram_value_coerce(value, vlen,
  224                                     BHND_NVRAM_TYPE_STRING, buf, len, type));
  225                         }
  226 
  227                         break;
  228 
  229                 default:
  230                         /* Skip unknown tags */
  231                         break;
  232                 }
  233         }
  234 
  235         /* Hit I/O error */
  236         return (error);
  237 }
  238 
  239 static int
  240 bhnd_nvram_tlv_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
  241     bhnd_nvram_plist *options, void *outp, size_t *olen)
  242 {
  243         bhnd_nvram_prop *prop;
  244         size_t           limit, nbytes;
  245         int              error;
  246 
  247         /* Determine output byte limit */
  248         if (outp != NULL)
  249                 limit = *olen;
  250         else
  251                 limit = 0;
  252 
  253         nbytes = 0;
  254 
  255         /* Write all properties */
  256         prop = NULL;
  257         while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
  258                 struct bhnd_nvram_tlv_env        env;
  259                 const char                      *name;
  260                 uint8_t                         *p;
  261                 size_t                           name_len, value_len;
  262                 size_t                           rec_size;
  263 
  264                 env.hdr.tag = NVRAM_TLV_TYPE_ENV;
  265                 env.hdr.size = sizeof(env.flags);
  266                 env.flags = 0x0;
  267 
  268                 /* Fetch name value and add to record length */
  269                 name = bhnd_nvram_prop_name(prop);
  270                 name_len = strlen(name) + 1 /* '=' */;
  271 
  272                 if (UINT8_MAX - env.hdr.size < name_len) {
  273                         BHND_NV_LOG("%s name exceeds maximum TLV record "
  274                             "length\n", name);
  275                         return (EFTYPE); /* would overflow TLV size */
  276                 }
  277 
  278                 env.hdr.size += name_len;
  279 
  280                 /* Add string value to record length */
  281                 error = bhnd_nvram_prop_encode(prop, NULL, &value_len,
  282                     BHND_NVRAM_TYPE_STRING);
  283                 if (error) {
  284                         BHND_NV_LOG("error serializing %s to required type "
  285                             "%s: %d\n", name,
  286                             bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
  287                             error);
  288                         return (error);
  289                 }
  290 
  291                 if (UINT8_MAX - env.hdr.size < value_len) {
  292                         BHND_NV_LOG("%s value exceeds maximum TLV record "
  293                             "length\n", name);
  294                         return (EFTYPE); /* would overflow TLV size */
  295                 }
  296 
  297                 env.hdr.size += value_len;
  298 
  299                 /* Calculate total record size */
  300                 rec_size = sizeof(env.hdr) + env.hdr.size;
  301                 if (SIZE_MAX - nbytes < rec_size)
  302                         return (EFTYPE); /* would overflow size_t */
  303 
  304                 /* Calculate our output pointer */
  305                 if (nbytes > limit || limit - nbytes < rec_size) {
  306                         /* buffer is full; cannot write */
  307                         p = NULL;
  308                 } else {
  309                         p = (uint8_t *)outp + nbytes;
  310                 }
  311 
  312                 /* Write to output */
  313                 if (p != NULL) {
  314                         memcpy(p, &env, sizeof(env));
  315                         p += sizeof(env);
  316 
  317                         memcpy(p, name, name_len - 1);
  318                         p[name_len - 1] = '=';
  319                         p += name_len;
  320 
  321                         error = bhnd_nvram_prop_encode(prop, p, &value_len,
  322                             BHND_NVRAM_TYPE_STRING);
  323                         if (error) {
  324                                 BHND_NV_LOG("error serializing %s to required "
  325                                     "type %s: %d\n", name,
  326                                     bhnd_nvram_type_name(
  327                                         BHND_NVRAM_TYPE_STRING),
  328                                     error);
  329                                 return (error);
  330                         }
  331                 }
  332 
  333                 nbytes += rec_size;
  334         }
  335 
  336         /* Write terminating END record */
  337         if (limit > nbytes)
  338                 *((uint8_t *)outp + nbytes) = NVRAM_TLV_TYPE_END;
  339 
  340         if (nbytes == SIZE_MAX)
  341                 return (EFTYPE); /* would overflow size_t */
  342         nbytes++;
  343 
  344         /* Provide required length */
  345         *olen = nbytes;
  346         if (limit < *olen) {
  347                 if (outp == NULL)
  348                         return (0);
  349 
  350                 return (ENOMEM);
  351         }
  352 
  353         return (0);
  354 }
  355 
  356 /**
  357  * Initialize @p tlv with the provided NVRAM TLV data mapped by @p src.
  358  * 
  359  * @param tlv A newly allocated data instance.
  360  */
  361 static int
  362 bhnd_nvram_tlv_init(struct bhnd_nvram_tlv *tlv, struct bhnd_nvram_io *src)
  363 {
  364         struct bhnd_nvram_tlv_env       *env;
  365         size_t                           size;
  366         size_t                           next;
  367         int                              error;
  368 
  369         BHND_NV_ASSERT(tlv->data == NULL, ("tlv data already initialized"));
  370 
  371         /* Determine the actual size of the TLV source data */
  372         if ((error = bhnd_nvram_tlv_parse_size(src, &size)))
  373                 return (error);
  374 
  375         /* Copy to our own internal buffer */
  376         if ((tlv->data = bhnd_nvram_iobuf_copy_range(src, 0x0, size)) == NULL)
  377                 return (ENOMEM);
  378 
  379         /* Initialize our backing buffer */
  380         tlv->count = 0;
  381         next = 0;
  382         while ((env = bhnd_nvram_tlv_next_env(tlv, &next, NULL)) != NULL) {
  383                 size_t env_len;
  384                 size_t name_len;
  385 
  386                 /* TLV_ENV data must not be empty */
  387                 env_len = NVRAM_TLV_ENVP_DATA_LEN(env);
  388                 if (env_len == 0) {
  389                         BHND_NV_LOG("cannot parse zero-length TLV_ENV record "
  390                             "data\n");
  391                         return (EINVAL);
  392                 }
  393 
  394                 /* Parse the key=value string, and then replace the '='
  395                  * delimiter with '\0' to allow us to provide direct 
  396                  * name pointers from our backing buffer */
  397                 error = bhnd_nvram_parse_env(env->envp, env_len, '=', NULL,
  398                     &name_len, NULL, NULL);
  399                 if (error) {
  400                         BHND_NV_LOG("error parsing TLV_ENV data: %d\n", error);
  401                         return (error);
  402                 }
  403 
  404                 /* Replace '=' with '\0' */
  405                 *(env->envp + name_len) = '\0';
  406 
  407                 /* Add to variable count */
  408                 tlv->count++;
  409         };
  410 
  411         return (0);
  412 }
  413 
  414 static int
  415 bhnd_nvram_tlv_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
  416 {
  417 
  418         struct bhnd_nvram_tlv   *tlv;
  419         int                      error;
  420 
  421         /* Allocate and initialize the TLV data instance */
  422         tlv = (struct bhnd_nvram_tlv *)nv;
  423 
  424         /* Parse the TLV input data and initialize our backing
  425          * data representation */
  426         if ((error = bhnd_nvram_tlv_init(tlv, io))) {
  427                 bhnd_nvram_tlv_free(nv);
  428                 return (error);
  429         }
  430 
  431         return (0);
  432 }
  433 
  434 static void
  435 bhnd_nvram_tlv_free(struct bhnd_nvram_data *nv)
  436 {
  437         struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv;
  438         if (tlv->data != NULL)
  439                 bhnd_nvram_io_free(tlv->data);
  440 }
  441 
  442 size_t
  443 bhnd_nvram_tlv_count(struct bhnd_nvram_data *nv)
  444 {
  445         struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv;
  446         return (tlv->count);
  447 }
  448 
  449 static bhnd_nvram_plist *
  450 bhnd_nvram_tlv_options(struct bhnd_nvram_data *nv)
  451 {
  452         return (NULL);
  453 }
  454 
  455 static uint32_t
  456 bhnd_nvram_tlv_caps(struct bhnd_nvram_data *nv)
  457 {
  458         return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
  459 }
  460 
  461 static const char *
  462 bhnd_nvram_tlv_next(struct bhnd_nvram_data *nv, void **cookiep)
  463 {
  464         struct bhnd_nvram_tlv           *tlv;
  465         struct bhnd_nvram_tlv_env       *env;
  466         size_t                           io_offset;
  467 
  468         tlv = (struct bhnd_nvram_tlv *)nv;
  469 
  470         /* Find next readable TLV record */
  471         if (*cookiep == NULL) {
  472                 /* Start search at offset 0x0 */
  473                 io_offset = 0x0;
  474                 env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep);
  475         } else {
  476                 /* Seek past the previous env record */
  477                 io_offset = bhnd_nvram_tlv_to_offset(tlv, *cookiep);
  478                 env = bhnd_nvram_tlv_next_env(tlv, &io_offset, NULL);
  479                 if (env == NULL)
  480                         BHND_NV_PANIC("invalid cookiep; record missing");
  481 
  482                 /* Advance to next env record, update the caller's cookiep */
  483                 env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep);
  484         }
  485 
  486         /* Check for EOF */
  487         if (env == NULL)
  488                 return (NULL);
  489 
  490         /* Return the NUL terminated name */
  491         return (env->envp);
  492 }
  493 
  494 static void *
  495 bhnd_nvram_tlv_find(struct bhnd_nvram_data *nv, const char *name)
  496 {
  497         return (bhnd_nvram_data_generic_find(nv, name));
  498 }
  499 
  500 static int
  501 bhnd_nvram_tlv_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
  502     void *cookiep2)
  503 {
  504         if (cookiep1 < cookiep2)
  505                 return (-1);
  506 
  507         if (cookiep1 > cookiep2)
  508                 return (1);
  509 
  510         return (0);
  511 }
  512 
  513 static int
  514 bhnd_nvram_tlv_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
  515     size_t *len, bhnd_nvram_type type)
  516 {
  517         return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
  518 }
  519 
  520 static int
  521 bhnd_nvram_tlv_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
  522     bhnd_nvram_val **value)
  523 {
  524         return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
  525 }
  526 
  527 static const void *
  528 bhnd_nvram_tlv_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
  529     size_t *len, bhnd_nvram_type *type)
  530 {
  531         struct bhnd_nvram_tlv           *tlv;
  532         struct bhnd_nvram_tlv_env       *env;
  533         const char                      *val;
  534         int                              error;
  535 
  536         tlv = (struct bhnd_nvram_tlv *)nv;
  537 
  538         /* Fetch pointer to the TLV_ENV record */
  539         if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL)
  540                 BHND_NV_PANIC("invalid cookiep: %p", cookiep);
  541 
  542         /* Parse value pointer and length from key\0value data */
  543         error = bhnd_nvram_parse_env(env->envp, NVRAM_TLV_ENVP_DATA_LEN(env),
  544             '\0', NULL, NULL, &val, len);
  545         if (error)
  546                 BHND_NV_PANIC("unexpected error parsing '%s'", env->envp);
  547 
  548         /* Type is always CSTR */
  549         *type = BHND_NVRAM_TYPE_STRING;
  550 
  551         return (val);
  552 }
  553 
  554 static const char *
  555 bhnd_nvram_tlv_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
  556 {
  557         struct bhnd_nvram_tlv           *tlv;
  558         const struct bhnd_nvram_tlv_env *env;
  559 
  560         tlv = (struct bhnd_nvram_tlv *)nv;
  561 
  562         /* Fetch pointer to the TLV_ENV record */
  563         if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL)
  564                 BHND_NV_PANIC("invalid cookiep: %p", cookiep);
  565 
  566         /* Return name pointer */
  567         return (&env->envp[0]);
  568 }
  569 
  570 static int
  571 bhnd_nvram_tlv_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
  572     bhnd_nvram_val *value, bhnd_nvram_val **result)
  573 {
  574         bhnd_nvram_val  *str;
  575         const char      *inp;
  576         bhnd_nvram_type  itype;
  577         size_t           ilen;
  578         size_t           name_len, tlv_nremain;
  579         int              error;
  580 
  581         tlv_nremain = NVRAM_TLV_ENVP_DATA_MAX_LEN;
  582 
  583         /* Name (trimmed of any path prefix) must be valid */
  584         if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
  585                 return (EINVAL);
  586 
  587         /* 'name=' must fit within the maximum TLV_ENV record length */
  588         name_len = strlen(name) + 1; /* '=' */
  589         if (tlv_nremain < name_len) {
  590                 BHND_NV_LOG("'%s=' exceeds maximum TLV_ENV record length\n",
  591                     name);
  592                 return (EINVAL);
  593         }
  594         tlv_nremain -= name_len;
  595 
  596         /* Convert value to a (bcm-formatted) string */
  597         error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
  598             value, BHND_NVRAM_VAL_DYNAMIC);
  599         if (error)
  600                 return (error);
  601 
  602         /* The string value must fit within remaining TLV_ENV record length */
  603         inp = bhnd_nvram_val_bytes(str, &ilen, &itype);
  604         if (tlv_nremain < ilen) {
  605                 BHND_NV_LOG("'%.*s\\' exceeds maximum TLV_ENV record length\n",
  606                     BHND_NV_PRINT_WIDTH(ilen), inp);
  607 
  608                 bhnd_nvram_val_release(str);
  609                 return (EINVAL);
  610         }
  611         tlv_nremain -= name_len;
  612 
  613         /* Success. Transfer result ownership to the caller. */
  614         *result = str;
  615         return (0);
  616 }
  617 
  618 static int
  619 bhnd_nvram_tlv_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
  620 {
  621         /* We permit deletion of any variable */
  622         return (0);
  623 }
  624 
  625 /**
  626  * Iterate over the records starting at @p next, returning the parsed
  627  * record's @p tag, @p size, and @p offset.
  628  * 
  629  * @param               io              The I/O context to parse.
  630  * @param[in,out]       next            The next offset to be parsed, or 0x0
  631  *                                      to begin parsing. Upon successful
  632  *                                      return, will be set to the offset of the
  633  *                                      next record (or EOF, if
  634  *                                      NVRAM_TLV_TYPE_END was parsed).
  635  * @param[out]          offset          The record's value offset.
  636  * @param[out]          tag             The record's tag.
  637  * 
  638  * @retval 0            success
  639  * @retval EINVAL       if parsing @p io as TLV fails.
  640  * @retval non-zero     if reading @p io otherwise fails, a regular unix error
  641  *                      code will be returned.
  642  */
  643 static int
  644 bhnd_nvram_tlv_next_record(struct bhnd_nvram_io *io, size_t *next, size_t
  645     *offset, uint8_t *tag)
  646 {
  647         size_t          io_offset, io_size;
  648         uint16_t        parsed_len;
  649         uint8_t         len_hdr[2];
  650         int             error;
  651 
  652         io_offset = *next;
  653         io_size = bhnd_nvram_io_getsize(io);
  654 
  655         /* Save the record offset */
  656         if (offset != NULL)
  657                 *offset = io_offset;
  658 
  659         /* Fetch initial tag */
  660         error = bhnd_nvram_io_read(io, io_offset, tag, sizeof(*tag));
  661         if (error)
  662                 return (error);
  663         io_offset++;
  664 
  665         /* EOF */
  666         if (*tag == NVRAM_TLV_TYPE_END) {
  667                 *next = io_offset;
  668                 return (0);
  669         }
  670 
  671         /* Read length field */
  672         if (*tag & NVRAM_TLV_TF_U8_LEN) {
  673                 error = bhnd_nvram_io_read(io, io_offset, &len_hdr,
  674                     sizeof(len_hdr[0]));
  675                 if (error) {
  676                         BHND_NV_LOG("error reading TLV record size: %d\n",
  677                             error);
  678                         return (error);
  679                 }
  680 
  681                 parsed_len = len_hdr[0];
  682                 io_offset++;
  683         } else {
  684                 error = bhnd_nvram_io_read(io, io_offset, &len_hdr,
  685                     sizeof(len_hdr));
  686                 if (error) {
  687                         BHND_NV_LOG("error reading 16-bit TLV record "
  688                             "size: %d\n", error);
  689                         return (error);
  690                 }
  691 
  692                 parsed_len = (len_hdr[0] << 8) | len_hdr[1];
  693                 io_offset += 2;
  694         }
  695 
  696         /* Advance to next record */
  697         if (parsed_len > io_size || io_size - parsed_len < io_offset) {
  698                 /* Hit early EOF */
  699                 BHND_NV_LOG("TLV record length %hu truncated by input "
  700                     "size of %zu\n", parsed_len, io_size);
  701                 return (EINVAL);
  702         }
  703 
  704         *next = io_offset + parsed_len;
  705 
  706         /* Valid record found */
  707         return (0);
  708 }
  709 
  710 /**
  711  * Parse the TLV data in @p io to determine the total size of the TLV
  712  * data mapped by @p io (which may be less than the size of @p io).
  713  */
  714 static int
  715 bhnd_nvram_tlv_parse_size(struct bhnd_nvram_io *io, size_t *size)
  716 {
  717         size_t          next;
  718         uint8_t         tag;
  719         int             error;
  720 
  721         /* We have to perform a minimal parse to determine the actual length */
  722         next = 0x0;
  723         *size = 0x0;
  724 
  725         /* Iterate over the input until we hit END tag or the read fails */
  726         do {
  727                 error = bhnd_nvram_tlv_next_record(io, &next, NULL, &tag);
  728                 if (error)
  729                         return (error);
  730         } while (tag != NVRAM_TLV_TYPE_END);
  731 
  732         /* Offset should now point to EOF */
  733         BHND_NV_ASSERT(next <= bhnd_nvram_io_getsize(io),
  734             ("parse returned invalid EOF offset"));
  735 
  736         *size = next;
  737         return (0);
  738 }
  739 
  740 /**
  741  * Iterate over the records in @p tlv, returning a pointer to the next
  742  * NVRAM_TLV_TYPE_ENV record, or NULL if EOF is reached.
  743  * 
  744  * @param               tlv             The TLV instance.
  745  * @param[in,out]       next            The next offset to be parsed, or 0x0
  746  *                                      to begin parsing. Upon successful
  747  *                                      return, will be set to the offset of the
  748  *                                      next record.
  749  */
  750 static struct bhnd_nvram_tlv_env *
  751 bhnd_nvram_tlv_next_env(struct bhnd_nvram_tlv *tlv, size_t *next,
  752     void **cookiep)
  753 {
  754         uint8_t tag;
  755         int     error;
  756 
  757         /* Find the next TLV_ENV record, starting at @p next */
  758         do {
  759                 void    *c;
  760                 size_t   offset;
  761 
  762                 /* Fetch the next TLV record */
  763                 error = bhnd_nvram_tlv_next_record(tlv->data, next, &offset,
  764                     &tag);
  765                 if (error) {
  766                         BHND_NV_LOG("unexpected error in next_record(): %d\n",
  767                             error);
  768                         return (NULL);
  769                 }
  770 
  771                 /* Only interested in ENV records */
  772                 if (tag != NVRAM_TLV_TYPE_ENV)
  773                         continue;
  774 
  775                 /* Map and return TLV_ENV record pointer */
  776                 c = bhnd_nvram_tlv_to_cookie(tlv, offset);
  777 
  778                 /* Provide the cookiep value for the returned record */
  779                 if (cookiep != NULL)
  780                         *cookiep = c;
  781 
  782                 return (bhnd_nvram_tlv_get_env(tlv, c));
  783         } while (tag != NVRAM_TLV_TYPE_END);
  784 
  785         /* No remaining ENV records */
  786         return (NULL);
  787 }
  788 
  789 /**
  790  * Return a pointer to the TLV_ENV record for @p cookiep, or NULL
  791  * if none vailable.
  792  */
  793 static struct bhnd_nvram_tlv_env *
  794 bhnd_nvram_tlv_get_env(struct bhnd_nvram_tlv *tlv, void *cookiep)
  795 {
  796         struct bhnd_nvram_tlv_env       *env;
  797         void                            *ptr;
  798         size_t                           navail;
  799         size_t                           io_offset, io_size;
  800         int                              error;
  801 
  802         io_size = bhnd_nvram_io_getsize(tlv->data);
  803         io_offset = bhnd_nvram_tlv_to_offset(tlv, cookiep);
  804 
  805         /* At EOF? */
  806         if (io_offset == io_size)
  807                 return (NULL);
  808 
  809         /* Fetch non-const pointer to the record entry */
  810         error = bhnd_nvram_io_write_ptr(tlv->data, io_offset, &ptr,
  811             sizeof(env->hdr), &navail);
  812         if (error) {
  813                 /* Should never occur with a valid cookiep */
  814                 BHND_NV_LOG("error mapping record for cookiep: %d\n", error);
  815                 return (NULL);
  816         }
  817 
  818         /* Validate the record pointer */
  819         env = ptr;
  820         if (env->hdr.tag != NVRAM_TLV_TYPE_ENV) {
  821                 /* Should never occur with a valid cookiep */
  822                 BHND_NV_LOG("non-ENV record mapped for %p\n", cookiep);
  823                 return (NULL);
  824         }
  825 
  826         /* Is the required variable name data is mapped? */
  827         if (navail < sizeof(struct bhnd_nvram_tlv_env_hdr) + env->hdr.size ||
  828             env->hdr.size == sizeof(env->flags))
  829         {
  830                 /* Should never occur with a valid cookiep */
  831                 BHND_NV_LOG("TLV_ENV variable data not mapped for %p\n",
  832                     cookiep);
  833                 return (NULL);
  834         }
  835 
  836         return (env);
  837 }
  838 
  839 /**
  840  * Return a cookiep for the given I/O offset.
  841  */
  842 static void *
  843 bhnd_nvram_tlv_to_cookie(struct bhnd_nvram_tlv *tlv, size_t io_offset)
  844 {
  845         const void      *ptr;
  846         int              error;
  847 
  848         BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(tlv->data),
  849             ("io_offset %zu out-of-range", io_offset));
  850         BHND_NV_ASSERT(io_offset < UINTPTR_MAX,
  851             ("io_offset %#zx exceeds UINTPTR_MAX", io_offset));
  852 
  853         error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_offset, NULL);
  854         if (error)
  855                 BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error);
  856 
  857         ptr = (const uint8_t *)ptr + io_offset;
  858         return (__DECONST(void *, ptr));
  859 }
  860 
  861 /* Convert a cookiep back to an I/O offset */
  862 static size_t
  863 bhnd_nvram_tlv_to_offset(struct bhnd_nvram_tlv *tlv, void *cookiep)
  864 {
  865         const void      *ptr;
  866         intptr_t         offset;
  867         size_t           io_size;
  868         int              error;
  869 
  870         BHND_NV_ASSERT(cookiep != NULL, ("null cookiep"));
  871 
  872         io_size = bhnd_nvram_io_getsize(tlv->data);
  873 
  874         error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_size, NULL);
  875         if (error)
  876                 BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error);
  877 
  878         offset = (const uint8_t *)cookiep - (const uint8_t *)ptr;
  879         BHND_NV_ASSERT(offset >= 0, ("invalid cookiep"));
  880         BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)"));
  881         BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)"));
  882 
  883         return ((size_t)offset);
  884 }

Cache object: 77ef32b5e680506915e5916b6adeeeb7


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