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