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_sprom.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 #include <sys/param.h>
   37 #include <sys/ctype.h>
   38 #include <sys/malloc.h>
   39 #include <sys/systm.h>
   40 
   41 #include <machine/_inttypes.h>
   42 #else /* !_KERNEL */
   43 #include <ctype.h>
   44 #include <errno.h>
   45 #include <inttypes.h>
   46 #include <stdint.h>
   47 #include <stdio.h>
   48 #include <stdlib.h>
   49 #include <string.h>
   50 #endif /* _KERNEL */
   51 
   52 #include "bhnd_nvram_map.h"
   53 
   54 #include "bhnd_nvram_private.h"
   55 #include "bhnd_nvram_datavar.h"
   56 
   57 #include "bhnd_nvram_data_spromvar.h"
   58 
   59 /*
   60  * BHND SPROM NVRAM data class
   61  *
   62  * The SPROM data format is a fixed-layout, non-self-descriptive binary format,
   63  * used on Broadcom wireless and wired adapters, that provides a subset of the
   64  * variables defined by Broadcom SoC NVRAM formats.
   65  */
   66 
   67 static const bhnd_sprom_layout  *bhnd_nvram_sprom_get_layout(uint8_t sromrev);
   68 
   69 static int                       bhnd_nvram_sprom_ident(
   70                                      struct bhnd_nvram_io *io,
   71                                      const bhnd_sprom_layout **ident);
   72 
   73 static int                       bhnd_nvram_sprom_write_var(
   74                                      bhnd_sprom_opcode_state *state,
   75                                      bhnd_sprom_opcode_idx_entry *entry,
   76                                      bhnd_nvram_val *value,
   77                                      struct bhnd_nvram_io *io);
   78 
   79 static int                       bhnd_nvram_sprom_read_var(
   80                                      struct bhnd_sprom_opcode_state *state,
   81                                      struct bhnd_sprom_opcode_idx_entry *entry,
   82                                      struct bhnd_nvram_io *io,
   83                                      union bhnd_nvram_sprom_storage *storage,
   84                                      bhnd_nvram_val *val);
   85 
   86 static int                       bhnd_nvram_sprom_write_offset(
   87                                      const struct bhnd_nvram_vardefn *var,
   88                                      struct bhnd_nvram_io *data,
   89                                      bhnd_nvram_type type, size_t offset,
   90                                      uint32_t mask, int8_t shift,
   91                                      uint32_t value);
   92 
   93 static int                       bhnd_nvram_sprom_read_offset(
   94                                      const struct bhnd_nvram_vardefn *var,
   95                                      struct bhnd_nvram_io *data,
   96                                      bhnd_nvram_type type, size_t offset,
   97                                      uint32_t mask, int8_t shift,
   98                                      uint32_t *value);
   99 
  100 static bool                      bhnd_sprom_is_external_immutable(
  101                                      const char *name);
  102 
  103 BHND_NVRAM_DATA_CLASS_DEFN(sprom, "Broadcom SPROM",
  104     BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_sprom))
  105 
  106 #define SPROM_COOKIE_TO_VID(_cookie)    \
  107         (((struct bhnd_sprom_opcode_idx_entry *)(_cookie))->vid)
  108 
  109 #define SPROM_COOKIE_TO_NVRAM_VAR(_cookie)      \
  110         bhnd_nvram_get_vardefn(SPROM_COOKIE_TO_VID(_cookie))
  111 
  112 /**
  113  * Read the magic value from @p io, and verify that it matches
  114  * the @p layout's expected magic value.
  115  * 
  116  * If @p layout does not defined a magic value, @p magic is set to 0x0
  117  * and success is returned.
  118  * 
  119  * @param       io      An I/O context mapping the SPROM data to be identified.
  120  * @param       layout  The SPROM layout against which @p io should be verified.
  121  * @param[out]  magic   On success, the SPROM magic value.
  122  * 
  123  * @retval 0            success
  124  * @retval non-zero     If checking @p io otherwise fails, a regular unix
  125  *                      error code will be returned.
  126  */
  127 static int
  128 bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io *io,
  129     const bhnd_sprom_layout *layout, uint16_t *magic)
  130 {
  131         int error;
  132 
  133         /* Skip if layout does not define a magic value */
  134         if (layout->flags & SPROM_LAYOUT_MAGIC_NONE)
  135                 return (0);
  136 
  137         /* Read the magic value */
  138         error = bhnd_nvram_io_read(io, layout->magic_offset, magic,
  139             sizeof(*magic));
  140         if (error)
  141                 return (error);
  142 
  143         *magic = le16toh(*magic);
  144 
  145         /* If the signature does not match, skip to next layout */
  146         if (*magic != layout->magic_value)
  147                 return (ENXIO);
  148 
  149         return (0);
  150 }
  151 
  152 /**
  153  * Attempt to identify the format of the SPROM data mapped by @p io.
  154  *
  155  * The SPROM data format does not provide any identifying information at a
  156  * known offset, instead requiring that we iterate over the known SPROM image
  157  * sizes until we are able to compute a valid checksum (and, for later
  158  * revisions, validate a signature at a revision-specific offset).
  159  *
  160  * @param       io      An I/O context mapping the SPROM data to be identified.
  161  * @param[out]  ident   On success, the identified SPROM layout.
  162  *
  163  * @retval 0            success
  164  * @retval non-zero     If identifying @p io otherwise fails, a regular unix
  165  *                      error code will be returned.
  166  */
  167 static int
  168 bhnd_nvram_sprom_ident(struct bhnd_nvram_io *io,
  169     const bhnd_sprom_layout **ident)
  170 {
  171         uint8_t crc;
  172         size_t  crc_errors;
  173         size_t  nbytes;
  174         int     error;
  175 
  176         crc = BHND_NVRAM_CRC8_INITIAL;
  177         crc_errors = 0;
  178         nbytes = 0;
  179 
  180         /* We iterate the SPROM layouts smallest to largest, allowing us to
  181          * perform incremental checksum calculation */
  182         for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
  183                 const bhnd_sprom_layout *layout;
  184                 u_char                   buf[512];
  185                 size_t                   nread;
  186                 uint16_t                 magic;
  187                 uint8_t                  srevcrc[2];
  188                 uint8_t                  srev;
  189                 bool                     crc_valid;
  190                 bool                     have_magic;
  191 
  192                 layout = &bhnd_sprom_layouts[i];
  193                 crc_valid = true;
  194 
  195                 have_magic = true;
  196                 if ((layout->flags & SPROM_LAYOUT_MAGIC_NONE))
  197                         have_magic = false;
  198 
  199                 /*
  200                  * Read image data and update CRC (errors are reported
  201                  * after the signature check)
  202                  * 
  203                  * Layout instances must be ordered from smallest to largest by
  204                  * the nvram_map compiler, allowing us to incrementally update
  205                  * our CRC.
  206                  */
  207                 if (nbytes > layout->size)
  208                         BHND_NV_PANIC("SPROM layout defined out-of-order");
  209 
  210                 nread = layout->size - nbytes;
  211 
  212                 while (nread > 0) {
  213                         size_t nr;
  214 
  215                         nr = bhnd_nv_ummin(nread, sizeof(buf));
  216 
  217                         if ((error = bhnd_nvram_io_read(io, nbytes, buf, nr)))
  218                                 return (error);
  219 
  220                         crc = bhnd_nvram_crc8(buf, nr, crc);
  221                         crc_valid = (crc == BHND_NVRAM_CRC8_VALID);
  222                         if (!crc_valid)
  223                                 crc_errors++;
  224 
  225                         nread -= nr;
  226                         nbytes += nr;
  227                 }
  228 
  229                 /* Read 8-bit SPROM revision, maintaining 16-bit size alignment
  230                  * required by some OTP/SPROM chipsets. */
  231                 error = bhnd_nvram_io_read(io, layout->srev_offset, &srevcrc,
  232                     sizeof(srevcrc));
  233                 if (error)
  234                         return (error);
  235 
  236                 srev = srevcrc[0];
  237 
  238                 /* Early sromrev 1 devices (specifically some BCM440x enet
  239                  * cards) are reported to have been incorrectly programmed
  240                  * with a revision of 0x10. */
  241                 if (layout->rev == 1 && srev == 0x10)
  242                         srev = 0x1;
  243                 
  244                 /* Check revision against the layout definition */
  245                 if (srev != layout->rev)
  246                         continue;
  247 
  248                 /* Check the magic value, skipping to the next layout on
  249                  * failure. */
  250                 error = bhnd_nvram_sprom_check_magic(io, layout, &magic);
  251                 if (error) {
  252                         /* If the CRC is was valid, log the mismatch */
  253                         if (crc_valid || BHND_NV_VERBOSE) {
  254                                 BHND_NV_LOG("invalid sprom %hhu signature: "
  255                                             "0x%hx (expected 0x%hx)\n", srev,
  256                                             magic, layout->magic_value);
  257 
  258                                         return (ENXIO);
  259                         }
  260 
  261                         continue;
  262                 }
  263 
  264                 /* Check for an earlier CRC error */
  265                 if (!crc_valid) {
  266                         /* If the magic check succeeded, then we may just have
  267                          * data corruption -- log the CRC error */
  268                         if (have_magic || BHND_NV_VERBOSE) {
  269                                 BHND_NV_LOG("sprom %hhu CRC error (crc=%#hhx, "
  270                                             "expected=%#x)\n", srev, crc,
  271                                             BHND_NVRAM_CRC8_VALID);
  272                         }
  273 
  274                         continue;
  275                 }
  276 
  277                 /* Identified */
  278                 *ident = layout;
  279                 return (0);
  280         }
  281 
  282         /* No match */
  283         if (crc_errors > 0 && BHND_NV_VERBOSE) {
  284                 BHND_NV_LOG("sprom parsing failed with %zu CRC errors\n",
  285                     crc_errors);
  286         }
  287 
  288         return (ENXIO);
  289 }
  290 
  291 static int
  292 bhnd_nvram_sprom_probe(struct bhnd_nvram_io *io)
  293 {
  294         const bhnd_sprom_layout *layout;
  295         int                      error;
  296 
  297         /* Try to parse the input */
  298         if ((error = bhnd_nvram_sprom_ident(io, &layout)))
  299                 return (error);
  300 
  301         return (BHND_NVRAM_DATA_PROBE_DEFAULT);
  302 }
  303 
  304 static int
  305 bhnd_nvram_sprom_getvar_direct(struct bhnd_nvram_io *io, const char *name,
  306     void *buf, size_t *len, bhnd_nvram_type type)
  307 {
  308         const bhnd_sprom_layout         *layout;
  309         bhnd_sprom_opcode_state          state;
  310         const struct bhnd_nvram_vardefn *var;
  311         size_t                           vid;
  312         int                              error;
  313 
  314         /* Look up the variable definition and ID */
  315         if ((var = bhnd_nvram_find_vardefn(name)) == NULL)
  316                 return (ENOENT);
  317 
  318         vid = bhnd_nvram_get_vardefn_id(var);
  319 
  320         /* Identify the SPROM image layout */
  321         if ((error = bhnd_nvram_sprom_ident(io, &layout)))
  322                 return (error);
  323 
  324         /* Initialize SPROM layout interpreter */
  325         if ((error = bhnd_sprom_opcode_init(&state, layout))) {
  326                 BHND_NV_LOG("error initializing opcode state: %d\n", error);
  327                 return (ENXIO);
  328         }
  329 
  330         /* Find SPROM layout entry for the requested variable */
  331         while ((error = bhnd_sprom_opcode_next_var(&state)) == 0) {
  332                 bhnd_sprom_opcode_idx_entry     entry;
  333                 union bhnd_nvram_sprom_storage  storage;
  334                 bhnd_nvram_val                  val;
  335 
  336                 /* Fetch the variable's entry state */
  337                 if ((error = bhnd_sprom_opcode_init_entry(&state, &entry)))
  338                         return (error);
  339 
  340                 /* Match against expected VID */
  341                 if (entry.vid != vid)
  342                         continue;
  343 
  344                 /* Decode variable to a new value instance */
  345                 error = bhnd_nvram_sprom_read_var(&state, &entry, io, &storage,
  346                     &val);
  347                 if (error)
  348                         return (error);
  349 
  350                 /* Perform value coercion */
  351                 error = bhnd_nvram_val_encode(&val, buf, len, type);
  352 
  353                 /* Clean up */
  354                 bhnd_nvram_val_release(&val);
  355                 return (error);
  356         }
  357 
  358         /* Hit EOF without matching the requested variable? */
  359         if (error == ENOENT)
  360                 return (ENOENT);
  361 
  362         /* Some other parse error occurred */
  363         return (error);
  364 }
  365 
  366 /**
  367  * Return the SPROM layout definition for the given @p sromrev, or NULL if
  368  * not found.
  369  */
  370 static const bhnd_sprom_layout *
  371 bhnd_nvram_sprom_get_layout(uint8_t sromrev)
  372 {
  373         /* Find matching SPROM layout definition */
  374         for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
  375                 if (bhnd_sprom_layouts[i].rev == sromrev)
  376                         return (&bhnd_sprom_layouts[i]);
  377         }
  378 
  379         /* Not found */
  380         return (NULL);
  381 }
  382 
  383 /**
  384  * Serialize a SPROM variable.
  385  *
  386  * @param state The SPROM opcode state describing the layout of @p io.
  387  * @param entry The variable's SPROM opcode index entry.
  388  * @param value The value to encode to @p io as per @p entry.
  389  * @param io    I/O context to which @p value should be written, or NULL
  390  *              if no output should be produced. This may be used to validate
  391  *              values prior to write.
  392  *
  393  * @retval 0            success
  394  * @retval EFTYPE       If value coercion from @p value to the type required by
  395  *                      @p entry is unsupported.
  396  * @retval ERANGE       If value coercion from @p value would overflow
  397  *                      (or underflow) the type required by @p entry.
  398  * @retval non-zero     If serialization otherwise fails, a regular unix error
  399  *                      code will be returned.
  400  */
  401 static int
  402 bhnd_nvram_sprom_write_var(bhnd_sprom_opcode_state *state,
  403     bhnd_sprom_opcode_idx_entry *entry, bhnd_nvram_val *value,
  404     struct bhnd_nvram_io *io)
  405 {
  406         const struct bhnd_nvram_vardefn *var;
  407         uint32_t                         u32[BHND_SPROM_ARRAY_MAXLEN];
  408         bhnd_nvram_type                  itype, var_base_type;
  409         size_t                           ipos, ilen, nelem;
  410         int                              error;
  411 
  412         /* Fetch variable definition and the native element type */
  413         var = bhnd_nvram_get_vardefn(entry->vid);
  414         BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
  415 
  416         var_base_type = bhnd_nvram_base_type(var->type);
  417 
  418         /* Fetch the element count from the SPROM variable layout definition */
  419         if ((error = bhnd_sprom_opcode_eval_var(state, entry)))
  420                 return (error);
  421 
  422         nelem = state->var.nelem;
  423         BHND_NV_ASSERT(nelem <= var->nelem, ("SPROM nelem=%zu exceeds maximum "
  424              "NVRAM nelem=%hhu", nelem, var->nelem));
  425 
  426         /* Promote the data to a common 32-bit representation */
  427         if (bhnd_nvram_is_signed_type(var_base_type))
  428                 itype = BHND_NVRAM_TYPE_INT32_ARRAY;
  429         else
  430                 itype = BHND_NVRAM_TYPE_UINT32_ARRAY;
  431 
  432         /* Calculate total size of the 32-bit promoted representation */
  433         if ((ilen = bhnd_nvram_value_size(NULL, 0, itype, nelem)) == 0) {
  434                 /* Variable-width types are unsupported */
  435                 BHND_NV_LOG("invalid %s SPROM variable type %d\n",
  436                             var->name, var->type);
  437                 return (EFTYPE);
  438         }
  439 
  440         /* The native representation must fit within our scratch array */
  441         if (ilen > sizeof(u32)) {
  442                 BHND_NV_LOG("error encoding '%s', SPROM_ARRAY_MAXLEN "
  443                             "incorrect\n", var->name);
  444                 return (EFTYPE);
  445         }
  446 
  447         /* Initialize our common 32-bit value representation */
  448         if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) {
  449                 /* No value provided; can this variable be encoded as missing
  450                  * by setting all bits to one? */
  451                 if (!(var->flags & BHND_NVRAM_VF_IGNALL1)) {
  452                         BHND_NV_LOG("missing required property: %s\n",
  453                             var->name);
  454                         return (EINVAL);
  455                 }
  456 
  457                 /* Set all bits */
  458                 memset(u32, 0xFF, ilen);
  459         } else {
  460                 bhnd_nvram_val   bcm_val;
  461                 const void      *var_ptr;
  462                 bhnd_nvram_type  var_type, raw_type;
  463                 size_t           var_len, enc_nelem;
  464 
  465                 /* Try to coerce the value to the native variable format. */
  466                 error = bhnd_nvram_val_convert_init(&bcm_val, var->fmt, value,
  467                     BHND_NVRAM_VAL_DYNAMIC|BHND_NVRAM_VAL_BORROW_DATA);
  468                 if (error) {
  469                         BHND_NV_LOG("error converting input type %s to %s "
  470                             "format\n",
  471                             bhnd_nvram_type_name(bhnd_nvram_val_type(value)),
  472                             bhnd_nvram_val_fmt_name(var->fmt));
  473                         return (error);
  474                 }
  475 
  476                 var_ptr = bhnd_nvram_val_bytes(&bcm_val, &var_len, &var_type);
  477 
  478                 /*
  479                  * Promote to a common 32-bit representation. 
  480                  *
  481                  * We must use the raw type to interpret the input data as its
  482                  * underlying integer representation -- otherwise, coercion
  483                  * would attempt to parse the input as its complex
  484                  * representation.
  485                  *
  486                  * For example, direct CHAR -> UINT32 coercion would attempt to
  487                  * parse the character as a decimal integer, rather than
  488                  * promoting the raw UTF8 byte value to a 32-bit value.
  489                  */
  490                 raw_type = bhnd_nvram_raw_type(var_type);
  491                 error = bhnd_nvram_value_coerce(var_ptr, var_len, raw_type,
  492                      u32, &ilen, itype);
  493 
  494                 /* Clean up temporary value representation */
  495                 bhnd_nvram_val_release(&bcm_val);
  496 
  497                 /* Report coercion failure */
  498                 if (error) {
  499                         BHND_NV_LOG("error promoting %s to %s: %d\n",
  500                             bhnd_nvram_type_name(var_type),
  501                             bhnd_nvram_type_name(itype), error);
  502                         return (error);
  503                 }
  504 
  505                 /* Encoded element count must match SPROM's definition */
  506                 error = bhnd_nvram_value_nelem(u32, ilen, itype, &enc_nelem);
  507                 if (error)
  508                         return (error);
  509 
  510                 if (enc_nelem != nelem) {
  511                         const char *type_name;
  512 
  513                         type_name = bhnd_nvram_type_name(var_base_type);
  514                         BHND_NV_LOG("invalid %s property value '%s[%zu]': "
  515                             "required %s[%zu]", var->name, type_name,
  516                             enc_nelem, type_name, nelem);
  517                         return (EFTYPE);
  518                 }
  519         }
  520 
  521         /*
  522          * Seek to the start of the variable's SPROM layout definition and
  523          * iterate over all bindings.
  524          */
  525         if ((error = bhnd_sprom_opcode_seek(state, entry))) {
  526                 BHND_NV_LOG("variable seek failed: %d\n", error);
  527                 return (error);
  528         }
  529 
  530         ipos = 0;
  531         while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) {
  532                 bhnd_sprom_opcode_bind  *binding;
  533                 bhnd_sprom_opcode_var   *binding_var;
  534                 size_t                   offset;
  535                 uint32_t                 skip_out_bytes;
  536 
  537                 BHND_NV_ASSERT(
  538                     state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
  539                     ("invalid var state"));
  540                 BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state"));
  541 
  542                 binding_var = &state->var;
  543                 binding = &state->var.bind;
  544 
  545                 /* Calculate output skip bytes for this binding.
  546                  * 
  547                  * Skip directions are defined in terms of decoding, and
  548                  * reversed when encoding. */
  549                 skip_out_bytes = binding->skip_in;
  550                 error = bhnd_sprom_opcode_apply_scale(state, &skip_out_bytes);
  551                 if (error)
  552                         return (error);
  553 
  554                 /* Bind */
  555                 offset = state->offset;
  556                 for (size_t i = 0; i < binding->count; i++) {
  557                         if (ipos >= nelem) {
  558                                 BHND_NV_LOG("input skip %u positioned %zu "
  559                                     "beyond nelem %zu\n", binding->skip_out,
  560                                     ipos, nelem);
  561                                 return (EINVAL);
  562                         }
  563 
  564                         /* Write next offset */
  565                         if (io != NULL) {
  566                                 error = bhnd_nvram_sprom_write_offset(var, io,
  567                                     binding_var->base_type,
  568                                     offset,
  569                                     binding_var->mask,
  570                                     binding_var->shift,
  571                                     u32[ipos]);
  572                                 if (error)
  573                                         return (error);
  574                         }
  575 
  576                         /* Adjust output position; this was already verified to
  577                          * not overflow/underflow during SPROM opcode
  578                          * evaluation */
  579                         if (binding->skip_in_negative) {
  580                                 offset -= skip_out_bytes;
  581                         } else {
  582                                 offset += skip_out_bytes;
  583                         }
  584 
  585                         /* Skip advancing input if additional bindings are
  586                          * required to fully encode intv */
  587                         if (binding->skip_out == 0)
  588                                 continue;
  589 
  590                         /* Advance input position */
  591                         if (SIZE_MAX - binding->skip_out < ipos) {
  592                                 BHND_NV_LOG("output skip %u would overflow "
  593                                     "%zu\n", binding->skip_out, ipos);
  594                                 return (EINVAL);
  595                         }
  596 
  597                         ipos += binding->skip_out;
  598                 }
  599         }
  600 
  601         /* Did we iterate all bindings until hitting end of the variable
  602          * definition? */
  603         BHND_NV_ASSERT(error != 0, ("loop terminated early"));
  604         if (error != ENOENT)
  605                 return (error);
  606 
  607         return (0);
  608 }
  609 
  610 static int
  611 bhnd_nvram_sprom_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
  612     bhnd_nvram_plist *options, void *outp, size_t *olen)
  613 {
  614         bhnd_sprom_opcode_state          state;
  615         struct bhnd_nvram_io            *io;
  616         bhnd_nvram_prop                 *prop;
  617         bhnd_sprom_opcode_idx_entry     *entry;
  618         const bhnd_sprom_layout         *layout;
  619         size_t                           limit;
  620         uint8_t                          crc;
  621         uint8_t                          sromrev;
  622         int                              error;
  623 
  624         limit = *olen;
  625         layout = NULL;
  626         io = NULL;
  627 
  628         /* Fetch sromrev property */
  629         if (!bhnd_nvram_plist_contains(props, BHND_NVAR_SROMREV)) {
  630                 BHND_NV_LOG("missing required property: %s\n",
  631                     BHND_NVAR_SROMREV);
  632                 return (EINVAL);
  633         }
  634 
  635         error = bhnd_nvram_plist_get_uint8(props, BHND_NVAR_SROMREV, &sromrev);
  636         if (error) {
  637                 BHND_NV_LOG("error reading sromrev property: %d\n", error);
  638                 return (EFTYPE);
  639         }
  640 
  641         /* Find SPROM layout definition */
  642         if ((layout = bhnd_nvram_sprom_get_layout(sromrev)) == NULL) {
  643                 BHND_NV_LOG("unsupported sromrev: %hhu\n", sromrev);
  644                 return (EFTYPE);
  645         }
  646 
  647         /* Provide required size to caller */
  648         *olen = layout->size;
  649         if (outp == NULL)
  650                 return (0);
  651         else if (limit < *olen)
  652                 return (ENOMEM);
  653 
  654         /* Initialize SPROM layout interpreter */
  655         if ((error = bhnd_sprom_opcode_init(&state, layout))) {
  656                 BHND_NV_LOG("error initializing opcode state: %d\n", error);
  657                 return (ENXIO);
  658         }
  659 
  660         /* Check for unsupported properties */
  661         prop = NULL;
  662         while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
  663                 const char *name;
  664 
  665                 /* Fetch the corresponding SPROM layout index entry */
  666                 name = bhnd_nvram_prop_name(prop);
  667                 entry = bhnd_sprom_opcode_index_find(&state, name);
  668                 if (entry == NULL) {
  669                         BHND_NV_LOG("property '%s' unsupported by sromrev "
  670                             "%hhu\n", name, layout->rev);
  671                         error = EINVAL;
  672                         goto finished;
  673                 }
  674         }
  675 
  676         /* Zero-initialize output */
  677         memset(outp, 0, *olen);
  678 
  679         /* Allocate wrapping I/O context for output buffer */
  680         io = bhnd_nvram_ioptr_new(outp, *olen, *olen, BHND_NVRAM_IOPTR_RDWR);
  681         if (io == NULL) {
  682                 error = ENOMEM;
  683                 goto finished;
  684         }
  685 
  686         /*
  687          * Serialize all SPROM variable data.
  688          */
  689         entry = NULL;
  690         while ((entry = bhnd_sprom_opcode_index_next(&state, entry)) != NULL) {
  691                 const struct bhnd_nvram_vardefn *var;
  692                 bhnd_nvram_val                  *val;
  693 
  694                 var = bhnd_nvram_get_vardefn(entry->vid);
  695                 BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
  696 
  697                 /* Fetch prop; will be NULL if unavailable */
  698                 prop = bhnd_nvram_plist_get_prop(props, var->name);
  699                 if (prop != NULL) {
  700                         val = bhnd_nvram_prop_val(prop);
  701                 } else {
  702                         val = BHND_NVRAM_VAL_NULL;
  703                 }
  704 
  705                 /* Attempt to serialize the property value to the appropriate
  706                  * offset within the output buffer */
  707                 error = bhnd_nvram_sprom_write_var(&state, entry, val, io);
  708                 if (error) {
  709                         BHND_NV_LOG("error serializing %s to required type "
  710                             "%s: %d\n", var->name,
  711                             bhnd_nvram_type_name(var->type), error);
  712 
  713                         /* ENOMEM is reserved for signaling that the output
  714                          * buffer capacity is insufficient */
  715                         if (error == ENOMEM)
  716                                 error = EINVAL;
  717 
  718                         goto finished;
  719                 }
  720         }
  721 
  722         /*
  723          * Write magic value, if any.
  724          */
  725         if (!(layout->flags & SPROM_LAYOUT_MAGIC_NONE)) {
  726                 uint16_t magic;
  727 
  728                 magic = htole16(layout->magic_value);
  729                 error = bhnd_nvram_io_write(io, layout->magic_offset, &magic,
  730                     sizeof(magic));
  731                 if (error) {
  732                         BHND_NV_LOG("error writing magic value: %d\n", error);
  733                         goto finished;
  734                 }
  735         }
  736 
  737         /* Calculate the CRC over all SPROM data, not including the CRC byte. */
  738         crc = ~bhnd_nvram_crc8(outp, layout->crc_offset,
  739             BHND_NVRAM_CRC8_INITIAL);
  740 
  741         /* Write the checksum. */
  742         error = bhnd_nvram_io_write(io, layout->crc_offset, &crc, sizeof(crc));
  743         if (error) {
  744                 BHND_NV_LOG("error writing CRC value: %d\n", error);
  745                 goto finished;
  746         }
  747 
  748         /*
  749          * Success!
  750          */
  751         error = 0;
  752 
  753 finished:
  754         bhnd_sprom_opcode_fini(&state);
  755 
  756         if (io != NULL)
  757                 bhnd_nvram_io_free(io);
  758 
  759         return (error);
  760 }
  761 
  762 static int
  763 bhnd_nvram_sprom_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
  764 {
  765         struct bhnd_nvram_sprom *sp;
  766         int                      error;
  767 
  768         sp = (struct bhnd_nvram_sprom *)nv;
  769 
  770         /* Identify the SPROM input data */
  771         if ((error = bhnd_nvram_sprom_ident(io, &sp->layout)))
  772                 return (error);
  773 
  774         /* Copy SPROM image to our shadow buffer */
  775         sp->data = bhnd_nvram_iobuf_copy_range(io, 0, sp->layout->size);
  776         if (sp->data == NULL)
  777                 goto failed;
  778 
  779         /* Initialize SPROM binding eval state */
  780         if ((error = bhnd_sprom_opcode_init(&sp->state, sp->layout)))
  781                 goto failed;
  782 
  783         return (0);
  784 
  785 failed:
  786         if (sp->data != NULL)
  787                 bhnd_nvram_io_free(sp->data);
  788 
  789         return (error);
  790 }
  791 
  792 static void
  793 bhnd_nvram_sprom_free(struct bhnd_nvram_data *nv)
  794 {
  795         struct bhnd_nvram_sprom *sp = (struct bhnd_nvram_sprom *)nv;
  796 
  797         bhnd_sprom_opcode_fini(&sp->state);
  798         bhnd_nvram_io_free(sp->data);
  799 }
  800 
  801 size_t
  802 bhnd_nvram_sprom_count(struct bhnd_nvram_data *nv)
  803 {
  804         struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv;
  805         return (sprom->layout->num_vars);
  806 }
  807 
  808 static bhnd_nvram_plist *
  809 bhnd_nvram_sprom_options(struct bhnd_nvram_data *nv)
  810 {
  811         return (NULL);
  812 }
  813 
  814 static uint32_t
  815 bhnd_nvram_sprom_caps(struct bhnd_nvram_data *nv)
  816 {
  817         return (BHND_NVRAM_DATA_CAP_INDEXED);
  818 }
  819 
  820 static const char *
  821 bhnd_nvram_sprom_next(struct bhnd_nvram_data *nv, void **cookiep)
  822 {
  823         struct bhnd_nvram_sprom         *sp;
  824         bhnd_sprom_opcode_idx_entry     *entry;
  825         const struct bhnd_nvram_vardefn *var;
  826 
  827         sp = (struct bhnd_nvram_sprom *)nv;
  828 
  829         /* Find next index entry that is not disabled by virtue of IGNALL1 */
  830         entry = *cookiep;
  831         while ((entry = bhnd_sprom_opcode_index_next(&sp->state, entry))) {
  832                 /* Update cookiep and fetch variable definition */
  833                 *cookiep = entry;
  834                 var = SPROM_COOKIE_TO_NVRAM_VAR(*cookiep);
  835                 BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
  836 
  837                 /* We might need to parse the variable's value to determine
  838                  * whether it should be treated as unset */
  839                 if (var->flags & BHND_NVRAM_VF_IGNALL1) {
  840                         int     error;
  841                         size_t  len;
  842 
  843                         error = bhnd_nvram_sprom_getvar(nv, *cookiep, NULL,
  844                             &len, var->type);
  845                         if (error) {
  846                                 BHND_NV_ASSERT(error == ENOENT, ("unexpected "
  847                                     "error parsing variable: %d", error));
  848                                 continue;
  849                         }
  850                 }
  851 
  852                 /* Found! */
  853                 return (var->name);
  854         }
  855 
  856         /* Reached end of index entries */
  857         return (NULL);
  858 }
  859 
  860 static void *
  861 bhnd_nvram_sprom_find(struct bhnd_nvram_data *nv, const char *name)
  862 {
  863         struct bhnd_nvram_sprom         *sp;
  864         bhnd_sprom_opcode_idx_entry     *entry;
  865 
  866         sp = (struct bhnd_nvram_sprom *)nv;
  867 
  868         entry = bhnd_sprom_opcode_index_find(&sp->state, name);
  869         return (entry);
  870 }
  871 
  872 /**
  873  * Write @p value of @p type to the SPROM @p data at @p offset, applying
  874  * @p mask and @p shift, and OR with the existing data.
  875  *
  876  * @param var The NVRAM variable definition.
  877  * @param data The SPROM data to be modified.
  878  * @param type The type to write at @p offset.
  879  * @param offset The data offset to be written.
  880  * @param mask The mask to be applied to @p value after shifting.
  881  * @param shift The shift to be applied to @p value; if positive, a left
  882  * shift will be applied, if negative, a right shift (this is the reverse of the
  883  * decoding behavior)
  884  * @param value The value to be written. The parsed value will be OR'd with the
  885  * current contents of @p data at @p offset.
  886  */
  887 static int
  888 bhnd_nvram_sprom_write_offset(const struct bhnd_nvram_vardefn *var,
  889     struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset,
  890     uint32_t mask, int8_t shift, uint32_t value)
  891 {
  892         union bhnd_nvram_sprom_storage  scratch;
  893         int                             error;
  894 
  895 #define NV_WRITE_INT(_widen, _repr, _swap)      do {            \
  896         /* Narrow the 32-bit representation */                  \
  897         scratch._repr[1] = (_widen)value;                       \
  898                                                                 \
  899         /* Shift and mask the new value */                      \
  900         if (shift > 0)                                          \
  901                 scratch._repr[1] <<= shift;                     \
  902         else if (shift < 0)                                     \
  903                 scratch._repr[1] >>= -shift;                    \
  904         scratch._repr[1] &= mask;                               \
  905                                                                 \
  906         /* Swap to output byte order */                         \
  907         scratch._repr[1] = _swap(scratch._repr[1]);             \
  908                                                                 \
  909         /* Fetch the current value */                           \
  910         error = bhnd_nvram_io_read(data, offset,                \
  911             &scratch._repr[0], sizeof(scratch._repr[0]));       \
  912         if (error) {                                            \
  913                 BHND_NV_LOG("error reading %s SPROM offset "    \
  914                     "%#zx: %d\n", var->name, offset, error);    \
  915                 return (EFTYPE);                                \
  916         }                                                       \
  917                                                                 \
  918         /* Mask and set our new value's bits in the current     \
  919          * value */                                             \
  920         if (shift >= 0)                                         \
  921                 scratch._repr[0] &= ~_swap(mask << shift);      \
  922         else if (shift < 0)                                     \
  923                 scratch._repr[0] &= ~_swap(mask >> (-shift));   \
  924         scratch._repr[0] |= scratch._repr[1];                   \
  925                                                                 \
  926         /* Perform write */                                     \
  927         error = bhnd_nvram_io_write(data, offset,               \
  928             &scratch._repr[0], sizeof(scratch._repr[0]));       \
  929         if (error) {                                            \
  930                 BHND_NV_LOG("error writing %s SPROM offset "    \
  931                     "%#zx: %d\n", var->name, offset, error);    \
  932                 return (EFTYPE);                                \
  933         }                                                       \
  934 } while(0)
  935 
  936         /* Apply mask/shift and widen to a common 32bit representation */
  937         switch (type) {
  938         case BHND_NVRAM_TYPE_UINT8:
  939                 NV_WRITE_INT(uint32_t,  u8,     );
  940                 break;
  941         case BHND_NVRAM_TYPE_UINT16:
  942                 NV_WRITE_INT(uint32_t,  u16,    htole16);
  943                 break;
  944         case BHND_NVRAM_TYPE_UINT32:
  945                 NV_WRITE_INT(uint32_t,  u32,    htole32);
  946                 break;
  947         case BHND_NVRAM_TYPE_INT8:
  948                 NV_WRITE_INT(int32_t,   i8,     );
  949                 break;
  950         case BHND_NVRAM_TYPE_INT16:
  951                 NV_WRITE_INT(int32_t,   i16,    htole16);
  952                 break;
  953         case BHND_NVRAM_TYPE_INT32:
  954                 NV_WRITE_INT(int32_t,   i32,    htole32);
  955                 break;
  956         case BHND_NVRAM_TYPE_CHAR:
  957                 NV_WRITE_INT(uint32_t,  u8,     );
  958                 break;
  959         default:
  960                 BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
  961                 return (EFTYPE);
  962         }
  963 #undef  NV_WRITE_INT
  964 
  965         return (0);
  966 }
  967 
  968 /**
  969  * Read the value of @p type from the SPROM @p data at @p offset, apply @p mask
  970  * and @p shift, and OR with the existing @p value.
  971  * 
  972  * @param var The NVRAM variable definition.
  973  * @param data The SPROM data to be decoded.
  974  * @param type The type to read at @p offset
  975  * @param offset The data offset to be read.
  976  * @param mask The mask to be applied to the value read at @p offset.
  977  * @param shift The shift to be applied after masking; if positive, a right
  978  * shift will be applied, if negative, a left shift.
  979  * @param value The read destination; the parsed value will be OR'd with the
  980  * current contents of @p value.
  981  */
  982 static int
  983 bhnd_nvram_sprom_read_offset(const struct bhnd_nvram_vardefn *var,
  984     struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset,
  985     uint32_t mask, int8_t shift, uint32_t *value)
  986 {
  987         union bhnd_nvram_sprom_storage  scratch;
  988         int                             error;
  989 
  990 #define NV_PARSE_INT(_widen, _repr, _swap)              do {    \
  991         /* Perform read */                                      \
  992         error = bhnd_nvram_io_read(data, offset,                \
  993             &scratch._repr[0], sizeof(scratch._repr[0]));       \
  994         if (error) {                                            \
  995                 BHND_NV_LOG("error reading %s SPROM offset "    \
  996                     "%#zx: %d\n", var->name, offset, error);    \
  997                 return (EFTYPE);                                \
  998         }                                                       \
  999                                                                 \
 1000         /* Swap to host byte order */                           \
 1001         scratch._repr[0] = _swap(scratch._repr[0]);             \
 1002                                                                 \
 1003         /* Mask and shift the value */                          \
 1004         scratch._repr[0] &= mask;                               \
 1005         if (shift > 0) {                                        \
 1006                 scratch. _repr[0] >>= shift;                    \
 1007         } else if (shift < 0) {                                 \
 1008                 scratch. _repr[0] <<= -shift;                   \
 1009         }                                                       \
 1010                                                                 \
 1011         /* Widen to 32-bit representation and OR with current   \
 1012          * value */                                             \
 1013         (*value) |= (_widen)scratch._repr[0];                   \
 1014 } while(0)
 1015 
 1016         /* Apply mask/shift and widen to a common 32bit representation */
 1017         switch (type) {
 1018         case BHND_NVRAM_TYPE_UINT8:
 1019                 NV_PARSE_INT(uint32_t,  u8,     );
 1020                 break;
 1021         case BHND_NVRAM_TYPE_UINT16:
 1022                 NV_PARSE_INT(uint32_t,  u16,    le16toh);
 1023                 break;
 1024         case BHND_NVRAM_TYPE_UINT32:
 1025                 NV_PARSE_INT(uint32_t,  u32,    le32toh);
 1026                 break;
 1027         case BHND_NVRAM_TYPE_INT8:
 1028                 NV_PARSE_INT(int32_t,   i8,     );
 1029                 break;
 1030         case BHND_NVRAM_TYPE_INT16:
 1031                 NV_PARSE_INT(int32_t,   i16,    le16toh);
 1032                 break;
 1033         case BHND_NVRAM_TYPE_INT32:
 1034                 NV_PARSE_INT(int32_t,   i32,    le32toh);
 1035                 break;
 1036         case BHND_NVRAM_TYPE_CHAR:
 1037                 NV_PARSE_INT(uint32_t,  u8,     );
 1038                 break;
 1039         default:
 1040                 BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
 1041                 return (EFTYPE);
 1042         }
 1043 #undef  NV_PARSE_INT
 1044 
 1045         return (0);
 1046 }
 1047 
 1048 /**
 1049  * Read a SPROM variable value from @p io.
 1050  * 
 1051  * @param       state           The SPROM opcode state describing the layout of @p io.
 1052  * @param       entry           The variable's SPROM opcode index entry.
 1053  * @param       io              The input I/O context.
 1054  * @param       storage         Storage to be used with @p val.
 1055  * @param[out]  val             Value instance to be initialized with the
 1056  *                              parsed variable data.
 1057  *
 1058  * The returned @p val instance will hold a borrowed reference to @p storage,
 1059  * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
 1060  * the lifetime of @p storage.
 1061  *
 1062  * The caller is responsible for releasing any allocated value state
 1063  * via bhnd_nvram_val_release().
 1064  */
 1065 static int
 1066 bhnd_nvram_sprom_read_var(struct bhnd_sprom_opcode_state *state,
 1067     struct bhnd_sprom_opcode_idx_entry *entry, struct bhnd_nvram_io *io,
 1068     union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
 1069 {
 1070         union bhnd_nvram_sprom_storage  *inp;
 1071         const struct bhnd_nvram_vardefn *var;
 1072         bhnd_nvram_type                  var_btype;
 1073         uint32_t                         intv;
 1074         size_t                           ilen, ipos, iwidth;
 1075         size_t                           nelem;
 1076         bool                             all_bits_set;
 1077         int                              error;
 1078 
 1079         /* Fetch canonical variable definition */
 1080         var = bhnd_nvram_get_vardefn(entry->vid);
 1081         BHND_NV_ASSERT(var != NULL, ("invalid entry"));
 1082 
 1083         /*
 1084          * Fetch the array length from the SPROM variable definition.
 1085          *
 1086          * This generally be identical to the array length provided by the
 1087          * canonical NVRAM variable definition, but some SPROM layouts may
 1088          * define a smaller element count.
 1089          */
 1090         if ((error = bhnd_sprom_opcode_eval_var(state, entry))) {
 1091                 BHND_NV_LOG("variable evaluation failed: %d\n", error);
 1092                 return (error);
 1093         }
 1094 
 1095         nelem = state->var.nelem;
 1096         if (nelem > var->nelem) {
 1097                 BHND_NV_LOG("SPROM array element count %zu cannot be "
 1098                     "represented by '%s' element count of %hhu\n", nelem,
 1099                     var->name, var->nelem);
 1100                 return (EFTYPE);
 1101         }
 1102 
 1103         /* Fetch the var's base element type */
 1104         var_btype = bhnd_nvram_base_type(var->type);
 1105 
 1106         /* Calculate total byte length of the native encoding */
 1107         if ((iwidth = bhnd_nvram_value_size(NULL, 0, var_btype, 1)) == 0) {
 1108                 /* SPROM does not use (and we do not support) decoding of
 1109                  * variable-width data types */
 1110                 BHND_NV_LOG("invalid SPROM data type: %d", var->type);
 1111                 return (EFTYPE);
 1112         }
 1113         ilen = nelem * iwidth;
 1114 
 1115         /* Decode into our caller's local storage */
 1116         inp = storage;
 1117         if (ilen > sizeof(*storage)) {
 1118                 BHND_NV_LOG("error decoding '%s', SPROM_ARRAY_MAXLEN "
 1119                     "incorrect\n", var->name);
 1120                 return (EFTYPE);
 1121         }
 1122 
 1123         /* Zero-initialize our decode buffer; any output elements skipped
 1124          * during decode should default to zero. */
 1125         memset(inp, 0, ilen);
 1126 
 1127         /*
 1128          * Decode the SPROM data, iteratively decoding up to nelem values.
 1129          */
 1130         if ((error = bhnd_sprom_opcode_seek(state, entry))) {
 1131                 BHND_NV_LOG("variable seek failed: %d\n", error);
 1132                 return (error);
 1133         }
 1134 
 1135         ipos = 0;
 1136         intv = 0x0;
 1137         if (var->flags & BHND_NVRAM_VF_IGNALL1)
 1138                 all_bits_set = true;
 1139         else
 1140                 all_bits_set = false;
 1141         while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) {
 1142                 bhnd_sprom_opcode_bind  *binding;
 1143                 bhnd_sprom_opcode_var   *binding_var;
 1144                 bhnd_nvram_type          intv_type;
 1145                 size_t                   offset;
 1146                 size_t                   nbyte;
 1147                 uint32_t                 skip_in_bytes;
 1148                 void                    *ptr;
 1149 
 1150                 BHND_NV_ASSERT(
 1151                     state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
 1152                     ("invalid var state"));
 1153                 BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state"));
 1154 
 1155                 binding_var = &state->var;
 1156                 binding = &state->var.bind;
 1157 
 1158                 if (ipos >= nelem) {
 1159                         BHND_NV_LOG("output skip %u positioned "
 1160                             "%zu beyond nelem %zu\n",
 1161                             binding->skip_out, ipos, nelem);
 1162                         return (EINVAL);
 1163                 }
 1164 
 1165                 /* Calculate input skip bytes for this binding */
 1166                 skip_in_bytes = binding->skip_in;
 1167                 error = bhnd_sprom_opcode_apply_scale(state, &skip_in_bytes);
 1168                 if (error)
 1169                         return (error);
 1170 
 1171                 /* Bind */
 1172                 offset = state->offset;
 1173                 for (size_t i = 0; i < binding->count; i++) {
 1174                         /* Read the offset value, OR'ing with the current
 1175                          * value of intv */
 1176                         error = bhnd_nvram_sprom_read_offset(var, io,
 1177                             binding_var->base_type,
 1178                             offset,
 1179                             binding_var->mask,
 1180                             binding_var->shift,
 1181                             &intv);
 1182                         if (error)
 1183                                 return (error);
 1184 
 1185                         /* If IGNALL1, record whether value does not have
 1186                          * all bits set. */
 1187                         if (var->flags & BHND_NVRAM_VF_IGNALL1 &&
 1188                             all_bits_set)
 1189                         {
 1190                                 uint32_t all1;
 1191 
 1192                                 all1 = binding_var->mask;
 1193                                 if (binding_var->shift > 0)
 1194                                         all1 >>= binding_var->shift;
 1195                                 else if (binding_var->shift < 0)
 1196                                         all1 <<= -binding_var->shift;
 1197 
 1198                                 if ((intv & all1) != all1)
 1199                                         all_bits_set = false;
 1200                         }
 1201 
 1202                         /* Adjust input position; this was already verified to
 1203                          * not overflow/underflow during SPROM opcode
 1204                          * evaluation */
 1205                         if (binding->skip_in_negative) {
 1206                                 offset -= skip_in_bytes;
 1207                         } else {
 1208                                 offset += skip_in_bytes;
 1209                         }
 1210 
 1211                         /* Skip writing to inp if additional bindings are
 1212                          * required to fully populate intv */
 1213                         if (binding->skip_out == 0)
 1214                                 continue;
 1215 
 1216                         /* We use bhnd_nvram_value_coerce() to perform
 1217                          * overflow-checked coercion from the widened
 1218                          * uint32/int32 intv value to the requested output
 1219                          * type */
 1220                         if (bhnd_nvram_is_signed_type(var_btype))
 1221                                 intv_type = BHND_NVRAM_TYPE_INT32;
 1222                         else
 1223                                 intv_type = BHND_NVRAM_TYPE_UINT32;
 1224 
 1225                         /* Calculate address of the current element output
 1226                          * position */
 1227                         ptr = (uint8_t *)inp + (iwidth * ipos);
 1228 
 1229                         /* Perform coercion of the array element */
 1230                         nbyte = iwidth;
 1231                         error = bhnd_nvram_value_coerce(&intv, sizeof(intv),
 1232                             intv_type, ptr, &nbyte, var_btype);
 1233                         if (error)
 1234                                 return (error);
 1235 
 1236                         /* Clear temporary state */
 1237                         intv = 0x0;
 1238 
 1239                         /* Advance output position */
 1240                         if (SIZE_MAX - binding->skip_out < ipos) {
 1241                                 BHND_NV_LOG("output skip %u would overflow "
 1242                                     "%zu\n", binding->skip_out, ipos);
 1243                                 return (EINVAL);
 1244                         }
 1245 
 1246                         ipos += binding->skip_out;
 1247                 }
 1248         }
 1249 
 1250         /* Did we iterate all bindings until hitting end of the variable
 1251          * definition? */
 1252         BHND_NV_ASSERT(error != 0, ("loop terminated early"));
 1253         if (error != ENOENT) {
 1254                 return (error);
 1255         }
 1256 
 1257         /* If marked IGNALL1 and all bits are set, treat variable as
 1258          * unavailable */
 1259         if ((var->flags & BHND_NVRAM_VF_IGNALL1) && all_bits_set)
 1260                 return (ENOENT);
 1261 
 1262         /* Provide value wrapper */
 1263         return (bhnd_nvram_val_init(val, var->fmt, inp, ilen, var->type,
 1264             BHND_NVRAM_VAL_BORROW_DATA));
 1265 }
 1266 
 1267 /**
 1268  * Common variable decoding; fetches and decodes variable to @p val,
 1269  * using @p storage for actual data storage.
 1270  * 
 1271  * The returned @p val instance will hold a borrowed reference to @p storage,
 1272  * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
 1273  * the lifetime of @p storage.
 1274  *
 1275  * The caller is responsible for releasing any allocated value state
 1276  * via bhnd_nvram_val_release().
 1277  */
 1278 static int
 1279 bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep,
 1280     union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
 1281 {
 1282         struct bhnd_nvram_sprom         *sp;
 1283         bhnd_sprom_opcode_idx_entry     *entry;
 1284         const struct bhnd_nvram_vardefn *var __diagused;
 1285 
 1286         BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
 1287 
 1288         sp = (struct bhnd_nvram_sprom *)nv;
 1289         entry = cookiep;
 1290 
 1291         /* Fetch canonical variable definition */
 1292         var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
 1293         BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
 1294 
 1295         return (bhnd_nvram_sprom_read_var(&sp->state, entry, sp->data, storage,
 1296             val));
 1297 }
 1298 
 1299 static int
 1300 bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
 1301     void *cookiep2)
 1302 {
 1303         struct bhnd_sprom_opcode_idx_entry *e1, *e2;
 1304 
 1305         e1 = cookiep1;
 1306         e2 = cookiep2;
 1307 
 1308         /* Use the index entry order; this matches the order of variables
 1309          * returned via bhnd_nvram_sprom_next() */
 1310         if (e1 < e2)
 1311                 return (-1);
 1312         else if (e1 > e2)
 1313                 return (1);
 1314 
 1315         return (0);
 1316 }
 1317 
 1318 static int
 1319 bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
 1320     size_t *len, bhnd_nvram_type otype)
 1321 {
 1322         bhnd_nvram_val                  val;
 1323         union bhnd_nvram_sprom_storage  storage;
 1324         int                             error;
 1325 
 1326         /* Decode variable to a new value instance */
 1327         error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
 1328         if (error)
 1329                 return (error);
 1330 
 1331         /* Perform value coercion */
 1332         error = bhnd_nvram_val_encode(&val, buf, len, otype);
 1333 
 1334         /* Clean up */
 1335         bhnd_nvram_val_release(&val);
 1336         return (error);
 1337 }
 1338 
 1339 static int
 1340 bhnd_nvram_sprom_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
 1341     bhnd_nvram_val **value)
 1342 {
 1343         bhnd_nvram_val                  val;
 1344         union bhnd_nvram_sprom_storage  storage;
 1345         int                             error;
 1346 
 1347         /* Decode variable to a new value instance */
 1348         error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
 1349         if (error)
 1350                 return (error);
 1351 
 1352         /* Attempt to copy to heap */
 1353         *value = bhnd_nvram_val_copy(&val);
 1354         bhnd_nvram_val_release(&val);
 1355 
 1356         if (*value == NULL)
 1357                 return (ENOMEM);
 1358 
 1359         return (0);
 1360 }
 1361 
 1362 static const void *
 1363 bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
 1364     size_t *len, bhnd_nvram_type *type)
 1365 {
 1366         /* Unsupported */
 1367         return (NULL);
 1368 }
 1369 
 1370 static const char *
 1371 bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
 1372 {
 1373         const struct bhnd_nvram_vardefn *var;
 1374 
 1375         BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
 1376 
 1377         var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
 1378         BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
 1379 
 1380         return (var->name);
 1381 }
 1382 
 1383 static int
 1384 bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
 1385     bhnd_nvram_val *value, bhnd_nvram_val **result)
 1386 {
 1387         struct bhnd_nvram_sprom         *sp;
 1388         const struct bhnd_nvram_vardefn *var;
 1389         bhnd_sprom_opcode_idx_entry     *entry;
 1390         bhnd_nvram_val                  *spval;
 1391         int                              error;
 1392 
 1393         sp = (struct bhnd_nvram_sprom *)nv;
 1394 
 1395         /* Is this an externally immutable variable name? */
 1396         if (bhnd_sprom_is_external_immutable(name))
 1397                 return (EINVAL);
 1398 
 1399         /* Variable must be defined in our SPROM layout */
 1400         if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
 1401                 return (ENOENT);
 1402 
 1403         var = bhnd_nvram_get_vardefn(entry->vid);
 1404         BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
 1405 
 1406         /* Value must be convertible to the native variable type */
 1407         error = bhnd_nvram_val_convert_new(&spval, var->fmt, value,
 1408             BHND_NVRAM_VAL_DYNAMIC);
 1409         if (error)
 1410                 return (error);
 1411 
 1412         /* Value must be encodeable by our SPROM layout */
 1413         error = bhnd_nvram_sprom_write_var(&sp->state, entry, spval, NULL);
 1414         if (error) {
 1415                 bhnd_nvram_val_release(spval);
 1416                 return (error);
 1417         }
 1418 
 1419         /* Success. Transfer our ownership of the converted value to the
 1420          * caller */
 1421         *result = spval;
 1422         return (0);
 1423 }
 1424 
 1425 static int
 1426 bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
 1427 {
 1428         struct bhnd_nvram_sprom         *sp;
 1429         const struct bhnd_nvram_vardefn *var;
 1430         bhnd_sprom_opcode_idx_entry     *entry;
 1431 
 1432         sp = (struct bhnd_nvram_sprom *)nv;
 1433 
 1434         /* Is this an externally immutable variable name? */
 1435         if (bhnd_sprom_is_external_immutable(name))
 1436                 return (EINVAL);
 1437 
 1438         /* Variable must be defined in our SPROM layout */
 1439         if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
 1440                 return (ENOENT);
 1441 
 1442         var = bhnd_nvram_get_vardefn(entry->vid);
 1443         BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
 1444 
 1445         /* Variable must be capable of representing a NULL/deleted value.
 1446          * 
 1447          * Since SPROM's layout is fixed, this requires IGNALL -- if
 1448          * all bits are set, an IGNALL variable is treated as unset. */
 1449         if (!(var->flags & BHND_NVRAM_VF_IGNALL1))
 1450                 return (EINVAL);
 1451 
 1452         return (0);
 1453 }
 1454 
 1455 /**
 1456  * Return true if @p name represents a special immutable variable name
 1457  * (e.g. sromrev) that cannot be updated in an SPROM existing image.
 1458  * 
 1459  * @param name The name to check.
 1460  */
 1461 static bool
 1462 bhnd_sprom_is_external_immutable(const char *name)
 1463 {
 1464         /* The layout revision is immutable and cannot be changed */
 1465         if (strcmp(name, BHND_NVAR_SROMREV) == 0)
 1466                 return (true);
 1467 
 1468         return (false);
 1469 }

Cache object: 3909695285eed50f558ba32eed089f9a


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