]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c
Merge compiler-rt trunk r321017 to contrib/compiler-rt.
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / nvram / bhnd_nvram_data_sprom.c
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 occured */
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 /**
1269  * Common variable decoding; fetches and decodes variable to @p val,
1270  * using @p storage for actual data storage.
1271  * 
1272  * The returned @p val instance will hold a borrowed reference to @p storage,
1273  * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
1274  * the lifetime of @p storage.
1275  *
1276  * The caller is responsible for releasing any allocated value state
1277  * via bhnd_nvram_val_release().
1278  */
1279 static int
1280 bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep,
1281     union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
1282 {
1283         struct bhnd_nvram_sprom         *sp;
1284         bhnd_sprom_opcode_idx_entry     *entry;
1285         const struct bhnd_nvram_vardefn *var;
1286
1287         BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
1288
1289         sp = (struct bhnd_nvram_sprom *)nv;
1290         entry = cookiep;
1291
1292         /* Fetch canonical variable definition */
1293         var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
1294         BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
1295
1296         return (bhnd_nvram_sprom_read_var(&sp->state, entry, sp->data, storage,
1297             val));
1298 }
1299
1300 static int
1301 bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
1302     void *cookiep2)
1303 {
1304         struct bhnd_sprom_opcode_idx_entry *e1, *e2;
1305
1306         e1 = cookiep1;
1307         e2 = cookiep2;
1308
1309         /* Use the index entry order; this matches the order of variables
1310          * returned via bhnd_nvram_sprom_next() */
1311         if (e1 < e2)
1312                 return (-1);
1313         else if (e1 > e2)
1314                 return (1);
1315
1316         return (0);
1317 }
1318
1319 static int
1320 bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
1321     size_t *len, bhnd_nvram_type otype)
1322 {
1323         bhnd_nvram_val                  val;
1324         union bhnd_nvram_sprom_storage  storage;
1325         int                             error;
1326
1327         /* Decode variable to a new value instance */
1328         error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
1329         if (error)
1330                 return (error);
1331
1332         /* Perform value coercion */
1333         error = bhnd_nvram_val_encode(&val, buf, len, otype);
1334
1335         /* Clean up */
1336         bhnd_nvram_val_release(&val);
1337         return (error);
1338 }
1339
1340 static int
1341 bhnd_nvram_sprom_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
1342     bhnd_nvram_val **value)
1343 {
1344         bhnd_nvram_val                  val;
1345         union bhnd_nvram_sprom_storage  storage;
1346         int                             error;
1347
1348         /* Decode variable to a new value instance */
1349         error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
1350         if (error)
1351                 return (error);
1352
1353         /* Attempt to copy to heap */
1354         *value = bhnd_nvram_val_copy(&val);
1355         bhnd_nvram_val_release(&val);
1356
1357         if (*value == NULL)
1358                 return (ENOMEM);
1359
1360         return (0);
1361 }
1362
1363 static const void *
1364 bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
1365     size_t *len, bhnd_nvram_type *type)
1366 {
1367         /* Unsupported */
1368         return (NULL);
1369 }
1370
1371 static const char *
1372 bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
1373 {
1374         const struct bhnd_nvram_vardefn *var;
1375
1376         BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
1377
1378         var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
1379         BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
1380
1381         return (var->name);
1382 }
1383
1384 static int
1385 bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
1386     bhnd_nvram_val *value, bhnd_nvram_val **result)
1387 {
1388         struct bhnd_nvram_sprom         *sp;
1389         const struct bhnd_nvram_vardefn *var;
1390         bhnd_sprom_opcode_idx_entry     *entry;
1391         bhnd_nvram_val                  *spval;
1392         int                              error;
1393
1394         sp = (struct bhnd_nvram_sprom *)nv;
1395
1396         /* Is this an externally immutable variable name? */
1397         if (bhnd_sprom_is_external_immutable(name))
1398                 return (EINVAL);
1399
1400         /* Variable must be defined in our SPROM layout */
1401         if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
1402                 return (ENOENT);
1403
1404         var = bhnd_nvram_get_vardefn(entry->vid);
1405         BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
1406
1407         /* Value must be convertible to the native variable type */
1408         error = bhnd_nvram_val_convert_new(&spval, var->fmt, value,
1409             BHND_NVRAM_VAL_DYNAMIC);
1410         if (error)
1411                 return (error);
1412
1413         /* Value must be encodeable by our SPROM layout */
1414         error = bhnd_nvram_sprom_write_var(&sp->state, entry, spval, NULL);
1415         if (error) {
1416                 bhnd_nvram_val_release(spval);
1417                 return (error);
1418         }
1419
1420         /* Success. Transfer our ownership of the converted value to the
1421          * caller */
1422         *result = spval;
1423         return (0);
1424 }
1425
1426 static int
1427 bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
1428 {
1429         struct bhnd_nvram_sprom         *sp;
1430         const struct bhnd_nvram_vardefn *var;
1431         bhnd_sprom_opcode_idx_entry     *entry;
1432
1433         sp = (struct bhnd_nvram_sprom *)nv;
1434
1435         /* Is this an externally immutable variable name? */
1436         if (bhnd_sprom_is_external_immutable(name))
1437                 return (EINVAL);
1438
1439         /* Variable must be defined in our SPROM layout */
1440         if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
1441                 return (ENOENT);
1442
1443         var = bhnd_nvram_get_vardefn(entry->vid);
1444         BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
1445
1446         /* Variable must be capable of representing a NULL/deleted value.
1447          * 
1448          * Since SPROM's layout is fixed, this requires IGNALL -- if
1449          * all bits are set, an IGNALL variable is treated as unset. */
1450         if (!(var->flags & BHND_NVRAM_VF_IGNALL1))
1451                 return (EINVAL);
1452
1453         return (0);
1454 }
1455
1456 /**
1457  * Return true if @p name represents a special immutable variable name
1458  * (e.g. sromrev) that cannot be updated in an SPROM existing image.
1459  * 
1460  * @param name The name to check.
1461  */
1462 static bool
1463 bhnd_sprom_is_external_immutable(const char *name)
1464 {
1465         /* The layout revision is immutable and cannot be changed */
1466         if (strcmp(name, BHND_NVAR_SROMREV) == 0)
1467                 return (true);
1468
1469         return (false);
1470 }