]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/nvram/bhnd_nvram_data_sprom_subr.c
Merge compiler-rt release_40 branch r292009.
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / nvram / bhnd_nvram_data_sprom_subr.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/param.h>
34 #include <sys/endian.h>
35
36 #ifdef _KERNEL
37 #include <sys/systm.h>
38 #include <machine/_inttypes.h>
39 #else /* !_KERNEL */
40 #include <errno.h>
41 #include <inttypes.h>
42 #include <stdint.h>
43 #include <string.h>
44 #endif /* _KERNEL */
45
46 #include "bhnd_nvram_private.h"
47 #include "bhnd_nvram_data_spromvar.h"
48
49 static int      bhnd_sprom_opcode_sort_idx(const void *lhs, const void *rhs);
50 static int      bhnd_nvram_opcode_idx_vid_compare(const void *key,
51                     const void *rhs);
52
53 static int      bhnd_sprom_opcode_reset(bhnd_sprom_opcode_state *state);
54 static int      bhnd_sprom_opcode_next_var(bhnd_sprom_opcode_state *state);
55
56 static int      bhnd_sprom_opcode_set_type(bhnd_sprom_opcode_state *state,
57                     bhnd_nvram_type type);
58
59 static int      bhnd_sprom_opcode_set_var(bhnd_sprom_opcode_state *state,
60                     size_t vid);
61 static int      bhnd_sprom_opcode_clear_var(bhnd_sprom_opcode_state *state);
62
63 static int      bhnd_sprom_opcode_flush_bind(bhnd_sprom_opcode_state *state);
64
65 static int      bhnd_sprom_opcode_read_opval32(bhnd_sprom_opcode_state *state,
66                     uint8_t type, uint32_t *opval);
67
68 static int      bhnd_sprom_opcode_step(bhnd_sprom_opcode_state *state,
69                     uint8_t *opcode);
70
71 #define SPROM_OP_BAD(_state, _fmt, ...)                                 \
72         BHND_NV_LOG("bad encoding at %td: " _fmt,                       \
73             (_state)->input - (_state)->layout->bindings, ##__VA_ARGS__)
74
75 /**
76  * Initialize SPROM opcode evaluation state.
77  * 
78  * @param state The opcode state to be initialized.
79  * @param layout The SPROM layout to be parsed by this instance.
80  * 
81  * 
82  * @retval 0 success
83  * @retval non-zero If initialization fails, a regular unix error code will be
84  * returned.
85  */
86 int
87 bhnd_sprom_opcode_init(bhnd_sprom_opcode_state *state,
88     const struct bhnd_sprom_layout *layout)
89 {
90         bhnd_sprom_opcode_idx_entry     *idx;
91         size_t                           num_vars, num_idx;
92         int                              error;
93
94         idx = NULL;
95
96         state->layout = layout;
97         state->idx = NULL;
98         state->num_idx = 0;
99
100         /* Initialize interpretation state */
101         if ((error = bhnd_sprom_opcode_reset(state)))
102                 return (error);
103
104         /* Allocate and populate our opcode index */
105         num_idx = state->layout->num_vars;
106         idx = bhnd_nv_calloc(num_idx, sizeof(*idx));
107         if (idx == NULL)
108                 return (ENOMEM);
109
110         for (num_vars = 0; num_vars < num_idx; num_vars++) {
111                 size_t opcodes;
112
113                 /* Seek to next entry */
114                 if ((error = bhnd_sprom_opcode_next_var(state))) {
115                         SPROM_OP_BAD(state, "error reading expected variable "
116                             "entry: %d\n", error);
117                         bhnd_nv_free(idx);
118                         return (error);
119                 }
120
121                 /* We limit the SPROM index representations to the minimal
122                  * type widths capable of covering all known layouts */
123
124                 /* Save SPROM image offset */
125                 if (state->offset > UINT16_MAX) {
126                         SPROM_OP_BAD(state, "cannot index large offset %u\n",
127                             state->offset);
128                         bhnd_nv_free(idx);
129                         return (ENXIO);
130                 }
131                 idx[num_vars].offset = state->offset;
132
133                 /* Save current variable ID */
134                 if (state->vid > UINT16_MAX) {
135                         SPROM_OP_BAD(state, "cannot index large vid %zu\n",
136                             state->vid);
137                         bhnd_nv_free(idx);
138                         return (ENXIO);
139                 }
140                 idx[num_vars].vid = state->vid;
141
142                 /* Save opcode position */
143                 opcodes = (state->input - state->layout->bindings);
144                 if (opcodes > UINT16_MAX) {
145                         SPROM_OP_BAD(state, "cannot index large opcode offset "
146                             "%zu\n", opcodes);
147                         bhnd_nv_free(idx);
148                         return (ENXIO);
149                 }
150                 idx[num_vars].opcodes = opcodes;
151         }
152
153         /* Should have reached end of binding table; next read must return
154          * ENOENT */
155         if ((error = bhnd_sprom_opcode_next_var(state)) != ENOENT) {
156                 BHND_NV_LOG("expected EOF parsing binding table: %d\n", error);
157                 bhnd_nv_free(idx);
158                 return (ENXIO);
159         }
160
161         /* Reset interpretation state */
162         if ((error = bhnd_sprom_opcode_reset(state))) {
163                 bhnd_nv_free(idx);
164                 return (error);
165         }
166
167         /* Make index available to opcode state evaluation */
168         qsort(idx, num_idx, sizeof(idx[0]), bhnd_sprom_opcode_sort_idx);
169
170         state->idx = idx;
171         state->num_idx = num_idx;
172
173         return (0);
174 }
175
176 /**
177  * Reset SPROM opcode evaluation state; future evaluation will be performed
178  * starting at the first opcode.
179  * 
180  * @param state The opcode state to be reset.
181  *
182  * @retval 0 success
183  * @retval non-zero If reset fails, a regular unix error code will be returned.
184  */
185 static int
186 bhnd_sprom_opcode_reset(bhnd_sprom_opcode_state *state)
187 {
188         memset(&state->var, 0, sizeof(state->var));
189
190         state->input = state->layout->bindings;
191         state->offset = 0;
192         state->vid = 0;
193         state->var_state = SPROM_OPCODE_VAR_STATE_NONE;
194         bit_set(state->revs, state->layout->rev);
195
196         return (0);
197 }
198
199 /**
200  * Free any resources associated with @p state.
201  * 
202  * @param state An opcode state previously successfully initialized with
203  * bhnd_sprom_opcode_init().
204  */
205 void
206 bhnd_sprom_opcode_fini(bhnd_sprom_opcode_state *state)
207 {
208         bhnd_nv_free(state->idx);
209 }
210
211
212 /**
213  * Sort function used to prepare our index for querying; sorts
214  * bhnd_sprom_opcode_idx_entry values by variable ID, ascending.
215  */
216 static int
217 bhnd_sprom_opcode_sort_idx(const void *lhs, const void *rhs)
218 {
219         const bhnd_sprom_opcode_idx_entry *l, *r;
220
221         l = lhs;
222         r = rhs;
223
224         if (l->vid < r->vid)
225                 return (-1);
226         if (l->vid > r->vid)
227                 return (1);
228         return (0);
229 }
230
231 /**
232  * Binary search comparison function used by bhnd_sprom_opcode_index_find();
233  * searches bhnd_sprom_opcode_idx_entry values by variable ID, ascending.
234  */
235 static int
236 bhnd_nvram_opcode_idx_vid_compare(const void *key, const void *rhs)
237 {
238         const bhnd_sprom_opcode_idx_entry       *entry;
239         size_t                                   vid;
240
241         vid = *(const size_t *)key;
242         entry = rhs;
243
244         if (vid < entry->vid)
245                 return (-1);
246         if (vid > entry->vid)
247                 return (1);
248
249         return (0);
250 }
251
252 /**
253  * Locate an index entry for the variable with @p name, or NULL if not found.
254  * 
255  * @param state The opcode state to be queried.
256  * @param name  The name to search for.
257  *
258  * @retval non-NULL     If @p name is found, its index entry value will be
259  *                      returned.
260  * @retval NULL         If @p name is not found.
261  */
262 bhnd_sprom_opcode_idx_entry *
263 bhnd_sprom_opcode_index_find(bhnd_sprom_opcode_state *state, const char *name)
264 {
265         const struct bhnd_nvram_vardefn *var;
266         size_t                           vid;
267
268         /* Determine the variable ID for the given name */
269         if ((var = bhnd_nvram_find_vardefn(name)) == NULL)
270                 return (NULL);
271
272         vid = bhnd_nvram_get_vardefn_id(var);
273
274         /* Search our index for the variable ID */
275         return (bsearch(&vid, state->idx, state->num_idx, sizeof(state->idx[0]),
276             bhnd_nvram_opcode_idx_vid_compare));
277 }
278
279
280 /**
281  * Iterate over all index entries in @p state.
282  * 
283  * @param               state   The opcode state to be iterated.
284  * @param[in,out]       prev    An entry previously returned by
285  *                              bhnd_sprom_opcode_index_next(), or a NULL value
286  *                              to begin iteration.
287  * 
288  * @return Returns the next index entry name, or NULL if all entries have
289  * been iterated.
290  */
291 bhnd_sprom_opcode_idx_entry *
292 bhnd_sprom_opcode_index_next(bhnd_sprom_opcode_state *state,
293     bhnd_sprom_opcode_idx_entry *prev)
294 {
295         size_t idxpos;
296
297         /* Get next index position */
298         if (prev == NULL) {
299                 idxpos = 0;
300         } else {
301                 /* Determine current position */
302                 idxpos = (size_t)(prev - state->idx);
303                 BHND_NV_ASSERT(idxpos < state->num_idx,
304                     ("invalid index %zu", idxpos));
305
306                 /* Advance to next entry */
307                 idxpos++;
308         }
309
310         /* Check for EOF */
311         if (idxpos == state->num_idx)
312                 return (NULL);
313
314         return (&state->idx[idxpos]);
315 }
316
317 /**
318  * Reset SPROM opcode evaluation state and seek to the @p entry's position.
319  * 
320  * @param state The opcode state to be reset.
321  * @param entry The indexed entry to which we'll seek the opcode state.
322  */
323 int
324 bhnd_sprom_opcode_seek(bhnd_sprom_opcode_state *state,
325     bhnd_sprom_opcode_idx_entry *entry)
326 {
327         int error;
328
329         BHND_NV_ASSERT(entry->opcodes < state->layout->bindings_size,
330             ("index entry references invalid opcode position"));
331
332         /* Reset state */
333         if ((error = bhnd_sprom_opcode_reset(state)))
334                 return (error);
335
336         /* Seek to the indexed sprom opcode offset */
337         state->input = state->layout->bindings + entry->opcodes;
338
339         /* Restore the indexed sprom data offset and VID */
340         state->offset = entry->offset;
341
342         /* Restore the indexed sprom variable ID */
343         if ((error = bhnd_sprom_opcode_set_var(state, entry->vid)))
344                 return (error);
345
346         return (0);
347 }
348
349 /**
350  * Set the current revision range for @p state. This also resets
351  * variable state.
352  * 
353  * @param state The opcode state to update
354  * @param start The first revision in the range.
355  * @param end The last revision in the range.
356  *
357  * @retval 0 success
358  * @retval non-zero If updating @p state fails, a regular unix error code will
359  * be returned.
360  */
361 static inline int
362 bhnd_sprom_opcode_set_revs(bhnd_sprom_opcode_state *state, uint8_t start,
363     uint8_t end)
364 {
365         int error;
366
367         /* Validate the revision range */
368         if (start > SPROM_OP_REV_MAX ||
369             end > SPROM_OP_REV_MAX ||
370             end < start)
371         {
372                 SPROM_OP_BAD(state, "invalid revision range: %hhu-%hhu\n",
373                     start, end);
374                 return (EINVAL);
375         }
376
377         /* Clear variable state */
378         if ((error = bhnd_sprom_opcode_clear_var(state)))
379                 return (error);
380
381         /* Reset revision mask */
382         memset(state->revs, 0x0, sizeof(state->revs));
383         bit_nset(state->revs, start, end);
384
385         return (0);
386 }
387
388 /**
389  * Set the current variable's value mask for @p state.
390  * 
391  * @param state The opcode state to update
392  * @param mask The mask to be set
393  *
394  * @retval 0 success
395  * @retval non-zero If updating @p state fails, a regular unix error code will
396  * be returned.
397  */
398 static inline int
399 bhnd_sprom_opcode_set_mask(bhnd_sprom_opcode_state *state, uint32_t mask)
400 {
401         if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
402                 SPROM_OP_BAD(state, "no open variable definition\n");
403                 return (EINVAL);
404         }
405
406         state->var.mask = mask;
407         return (0);
408 }
409
410 /**
411  * Set the current variable's value shift for @p state.
412  * 
413  * @param state The opcode state to update
414  * @param shift The shift to be set
415  *
416  * @retval 0 success
417  * @retval non-zero If updating @p state fails, a regular unix error code will
418  * be returned.
419  */
420 static inline int
421 bhnd_sprom_opcode_set_shift(bhnd_sprom_opcode_state *state, int8_t shift)
422 {
423         if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
424                 SPROM_OP_BAD(state, "no open variable definition\n");
425                 return (EINVAL);
426         }
427
428         state->var.shift = shift;
429         return (0);
430 }
431
432 /**
433  * Register a new BIND/BINDN operation with @p state.
434  * 
435  * @param state The opcode state to update.
436  * @param count The number of elements to be bound.
437  * @param skip_in The number of input elements to skip after each bind.
438  * @param skip_in_negative If true, the input skip should be subtracted from
439  * the current offset after each bind. If false, the input skip should be
440  * added.
441  * @param skip_out The number of output elements to skip after each bind.
442  * 
443  * @retval 0 success
444  * @retval EINVAL if a variable definition is not open.
445  * @retval EINVAL if @p skip_in and @p count would trigger an overflow or
446  * underflow when applied to the current input offset.
447  * @retval ERANGE if @p skip_in would overflow uint32_t when multiplied by
448  * @p count and the scale value.
449  * @retval ERANGE if @p skip_out would overflow uint32_t when multiplied by
450  * @p count and the scale value.
451  * @retval non-zero If updating @p state otherwise fails, a regular unix error
452  * code will be returned.
453  */
454 static inline int
455 bhnd_sprom_opcode_set_bind(bhnd_sprom_opcode_state *state, uint8_t count,
456     uint8_t skip_in, bool skip_in_negative, uint8_t skip_out)
457 {
458         uint32_t        iskip_total;
459         uint32_t        iskip_scaled;
460         int             error;
461
462         /* Must have an open variable */
463         if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
464                 SPROM_OP_BAD(state, "no open variable definition\n");
465                 SPROM_OP_BAD(state, "BIND outside of variable definition\n");
466                 return (EINVAL);
467         }
468
469         /* Cannot overwite an existing bind definition */
470         if (state->var.have_bind) {
471                 SPROM_OP_BAD(state, "BIND overwrites existing definition\n");
472                 return (EINVAL);
473         }
474
475         /* Must have a count of at least 1 */
476         if (count == 0) {
477                 SPROM_OP_BAD(state, "BIND with zero count\n");
478                 return (EINVAL);
479         }
480
481         /* Scale skip_in by the current type width */
482         iskip_scaled = skip_in;
483         if ((error = bhnd_sprom_opcode_apply_scale(state, &iskip_scaled)))
484                 return (error);
485
486         /* Calculate total input bytes skipped: iskip_scaled * count) */
487         if (iskip_scaled > 0 && UINT32_MAX / iskip_scaled < count) {
488                 SPROM_OP_BAD(state, "skip_in %hhu would overflow", skip_in);
489                 return (EINVAL);
490         }
491
492         iskip_total = iskip_scaled * count;
493
494         /* Verify that the skip_in value won't under/overflow the current
495          * input offset. */
496         if (skip_in_negative) {
497                 if (iskip_total > state->offset) {
498                         SPROM_OP_BAD(state, "skip_in %hhu would underflow "
499                             "offset %u\n", skip_in, state->offset);
500                         return (EINVAL);
501                 }
502         } else {
503                 if (UINT32_MAX - iskip_total < state->offset) {
504                         SPROM_OP_BAD(state, "skip_in %hhu would overflow "
505                             "offset %u\n", skip_in, state->offset);
506                         return (EINVAL);
507                 }
508         }
509
510         /* Set the actual count and skip values */
511         state->var.have_bind = true;
512         state->var.bind.count = count;
513         state->var.bind.skip_in = skip_in;
514         state->var.bind.skip_out = skip_out;
515
516         state->var.bind.skip_in_negative = skip_in_negative;
517
518         /* Update total bind count for the current variable */
519         state->var.bind_total++;
520
521         return (0);
522 }
523
524
525 /**
526  * Apply and clear the current opcode bind state, if any.
527  * 
528  * @param state The opcode state to update.
529  * 
530  * @retval 0 success
531  * @retval non-zero If updating @p state otherwise fails, a regular unix error
532  * code will be returned.
533  */
534 static int
535 bhnd_sprom_opcode_flush_bind(bhnd_sprom_opcode_state *state)
536 {
537         int             error;
538         uint32_t        skip;
539
540         /* Nothing to do? */
541         if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN ||
542             !state->var.have_bind)
543                 return (0);
544
545         /* Apply SPROM offset adjustment */
546         if (state->var.bind.count > 0) {
547                 skip = state->var.bind.skip_in * state->var.bind.count;
548                 if ((error = bhnd_sprom_opcode_apply_scale(state, &skip)))
549                         return (error);
550
551                 if (state->var.bind.skip_in_negative) {
552                         state->offset -= skip;
553                 } else {
554                         state->offset += skip;
555                 }
556         }
557
558         /* Clear bind state */
559         memset(&state->var.bind, 0, sizeof(state->var.bind));
560         state->var.have_bind = false;
561
562         return (0);
563 }
564
565 /**
566  * Set the current type to @p type, and reset type-specific
567  * stream state.
568  *
569  * @param state The opcode state to update.
570  * @param type The new type.
571  * 
572  * @retval 0 success
573  * @retval EINVAL if @p vid is not a valid variable ID.
574  */
575 static int
576 bhnd_sprom_opcode_set_type(bhnd_sprom_opcode_state *state, bhnd_nvram_type type)
577 {
578         bhnd_nvram_type base_type;
579         size_t          width;
580         uint32_t        mask;
581
582         /* Must have an open variable definition */
583         if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
584                 SPROM_OP_BAD(state, "type set outside variable definition\n");
585                 return (EINVAL);
586         }
587
588         /* Fetch type width for use as our scale value */
589         width = bhnd_nvram_type_width(type);
590         if (width == 0) {
591                 SPROM_OP_BAD(state, "unsupported variable-width type: %d\n",
592                     type);
593                 return (EINVAL);
594         } else if (width > UINT32_MAX) {
595                 SPROM_OP_BAD(state, "invalid type width %zu for type: %d\n",
596                     width, type);
597                 return (EINVAL);
598         }
599
600         /* Determine default mask value for the element type */
601         base_type = bhnd_nvram_base_type(type);
602         switch (base_type) {
603         case BHND_NVRAM_TYPE_UINT8:
604         case BHND_NVRAM_TYPE_INT8:
605         case BHND_NVRAM_TYPE_CHAR:
606                 mask = UINT8_MAX;
607                 break;
608         case BHND_NVRAM_TYPE_UINT16:
609         case BHND_NVRAM_TYPE_INT16:
610                 mask = UINT16_MAX;
611                 break;
612         case BHND_NVRAM_TYPE_UINT32:
613         case BHND_NVRAM_TYPE_INT32:
614                 mask = UINT32_MAX;
615                 break;
616         case BHND_NVRAM_TYPE_STRING:
617                 /* fallthrough (unused by SPROM) */
618         default:
619                 SPROM_OP_BAD(state, "unsupported type: %d\n", type);
620                 return (EINVAL);
621         }
622         
623         /* Update state */
624         state->var.base_type = base_type;
625         state->var.mask = mask;
626         state->var.scale = (uint32_t)width;
627
628         return (0);
629 }
630
631 /**
632  * Clear current variable state, if any.
633  * 
634  * @param state The opcode state to update.
635  */
636 static int
637 bhnd_sprom_opcode_clear_var(bhnd_sprom_opcode_state *state)
638 {
639         if (state->var_state == SPROM_OPCODE_VAR_STATE_NONE)
640                 return (0);
641
642         BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
643             ("incomplete variable definition"));
644         BHND_NV_ASSERT(!state->var.have_bind, ("stale bind state"));
645
646         memset(&state->var, 0, sizeof(state->var));
647         state->var_state = SPROM_OPCODE_VAR_STATE_NONE;
648
649         return (0);
650 }
651
652 /**
653  * Set the current variable's array element count to @p nelem.
654  *
655  * @param state The opcode state to update.
656  * @param nelem The new array length.
657  * 
658  * @retval 0 success
659  * @retval EINVAL if no open variable definition exists.
660  * @retval EINVAL if @p nelem is zero.
661  * @retval ENXIO if @p nelem is greater than one, and the current variable does
662  * not have an array type.
663  * @retval ENXIO if @p nelem exceeds the array length of the NVRAM variable
664  * definition.
665  */
666 static int
667 bhnd_sprom_opcode_set_nelem(bhnd_sprom_opcode_state *state, uint8_t nelem)
668 {
669         const struct bhnd_nvram_vardefn *var;
670
671         /* Must have a defined variable */
672         if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
673                 SPROM_OP_BAD(state, "array length set without open variable "
674                     "state");
675                 return (EINVAL);
676         }
677
678         /* Locate the actual variable definition */
679         if ((var = bhnd_nvram_get_vardefn(state->vid)) == NULL) {
680                 SPROM_OP_BAD(state, "unknown variable ID: %zu\n", state->vid);
681                 return (EINVAL);
682         }
683
684         /* Must be greater than zero */
685         if (nelem == 0) {
686                 SPROM_OP_BAD(state, "invalid nelem: %hhu\n", nelem);
687                 return (EINVAL);
688         }
689
690         /* If the variable is not an array-typed value, the array length
691          * must be 1 */
692         if (!bhnd_nvram_is_array_type(var->type) && nelem != 1) {
693                 SPROM_OP_BAD(state, "nelem %hhu on non-array %zu\n", nelem,
694                     state->vid);
695                 return (ENXIO);
696         }
697         
698         /* Cannot exceed the variable's defined array length */
699         if (nelem > var->nelem) {
700                 SPROM_OP_BAD(state, "nelem %hhu exceeds %zu length %hhu\n",
701                     nelem, state->vid, var->nelem);
702                 return (ENXIO);
703         }
704
705         /* Valid length; update state */
706         state->var.nelem = nelem;
707
708         return (0);
709 }
710
711 /**
712  * Set the current variable ID to @p vid, and reset variable-specific
713  * stream state.
714  *
715  * @param state The opcode state to update.
716  * @param vid The new variable ID.
717  * 
718  * @retval 0 success
719  * @retval EINVAL if @p vid is not a valid variable ID.
720  */
721 static int
722 bhnd_sprom_opcode_set_var(bhnd_sprom_opcode_state *state, size_t vid)
723 {
724         const struct bhnd_nvram_vardefn *var;
725         int                              error;
726
727         BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_NONE,
728             ("overwrite of open variable definition"));
729
730         /* Locate the variable definition */
731         if ((var = bhnd_nvram_get_vardefn(vid)) == NULL) {
732                 SPROM_OP_BAD(state, "unknown variable ID: %zu\n", vid);
733                 return (EINVAL);
734         }
735
736         /* Update vid and var state */
737         state->vid = vid;
738         state->var_state = SPROM_OPCODE_VAR_STATE_OPEN;
739
740         /* Initialize default variable record values */
741         memset(&state->var, 0x0, sizeof(state->var));
742
743         /* Set initial base type */
744         if ((error = bhnd_sprom_opcode_set_type(state, var->type)))
745                 return (error);
746
747         /* Set default array length */
748         if ((error = bhnd_sprom_opcode_set_nelem(state, var->nelem)))
749                 return (error);
750
751         return (0);
752 }
753
754 /**
755  * Mark the currently open variable definition as complete.
756  * 
757  * @param state The opcode state to update.
758  *
759  * @retval 0 success
760  * @retval EINVAL if no incomplete open variable definition exists.
761  */
762 static int
763 bhnd_sprom_opcode_end_var(bhnd_sprom_opcode_state *state)
764 {
765         if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
766                 SPROM_OP_BAD(state, "no open variable definition\n");
767                 return (EINVAL);
768         }
769
770         state->var_state = SPROM_OPCODE_VAR_STATE_DONE;
771         return (0);
772 }
773
774 /**
775  * Apply the current scale to @p value.
776  * 
777  * @param state The SPROM opcode state.
778  * @param[in,out] value The value to scale
779  * 
780  * @retval 0 success
781  * @retval EINVAL if no open variable definition exists.
782  * @retval EINVAL if applying the current scale would overflow.
783  */
784 int
785 bhnd_sprom_opcode_apply_scale(bhnd_sprom_opcode_state *state, uint32_t *value)
786 {
787         /* Must have a defined variable (and thus, scale) */
788         if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
789                 SPROM_OP_BAD(state, "scaled value encoded without open "
790                     "variable state");
791                 return (EINVAL);
792         }
793
794         /* Applying the scale value must not overflow */
795         if (UINT32_MAX / state->var.scale < *value) {
796                 SPROM_OP_BAD(state, "cannot represent %" PRIu32 " * %" PRIu32
797                     "\n", *value, state->var.scale);
798                 return (EINVAL);
799         }
800
801         *value = (*value) * state->var.scale;
802         return (0);
803 }
804
805 /**
806  * Read a SPROM_OP_DATA_* value from @p opcodes.
807  * 
808  * @param state The SPROM opcode state.
809  * @param type The SROM_OP_DATA_* type to be read.
810  * @param opval On success, the 32bit data representation. If @p type is signed,
811  * the value will be appropriately sign extended and may be directly cast to
812  * int32_t.
813  * 
814  * @retval 0 success
815  * @retval non-zero If reading the value otherwise fails, a regular unix error
816  * code will be returned.
817  */
818 static int
819 bhnd_sprom_opcode_read_opval32(bhnd_sprom_opcode_state *state, uint8_t type,
820    uint32_t *opval)
821 {
822         const uint8_t   *p;
823         int              error;
824
825         p = state->input;
826         switch (type) {
827         case SPROM_OP_DATA_I8:
828                 /* Convert to signed value first, then sign extend */
829                 *opval = (int32_t)(int8_t)(*p);
830                 p += 1;
831                 break;
832         case SPROM_OP_DATA_U8:
833                 *opval = *p;
834                 p += 1;
835                 break;
836         case SPROM_OP_DATA_U8_SCALED:
837                 *opval = *p;
838
839                 if ((error = bhnd_sprom_opcode_apply_scale(state, opval)))
840                         return (error);
841
842                 p += 1;
843                 break;
844         case SPROM_OP_DATA_U16:
845                 *opval = le16dec(p);
846                 p += 2;
847                 break;
848         case SPROM_OP_DATA_U32:
849                 *opval = le32dec(p);
850                 p += 4;
851                 break;
852         default:
853                 SPROM_OP_BAD(state, "unsupported data type: %hhu\n", type);
854                 return (EINVAL);
855         }
856
857         /* Update read address */
858         state->input = p;
859
860         return (0);
861 }
862
863 /**
864  * Return true if our layout revision is currently defined by the SPROM
865  * opcode state.
866  * 
867  * This may be used to test whether the current opcode stream state applies
868  * to the layout that we are actually parsing.
869  * 
870  * A given opcode stream may cover multiple layout revisions, switching
871  * between them prior to defining a set of variables.
872  */
873 static inline bool
874 bhnd_sprom_opcode_matches_layout_rev(bhnd_sprom_opcode_state *state)
875 {
876         return (bit_test(state->revs, state->layout->rev));
877 }
878
879 /**
880  * When evaluating @p state and @p opcode, rewrite @p opcode based on the
881  * current evaluation state.
882  * 
883  * This allows the insertion of implicit opcodes into interpretation of the
884  * opcode stream.
885  * 
886  * If @p opcode is rewritten, it should be returned from
887  * bhnd_sprom_opcode_step() instead of the opcode parsed from @p state's opcode
888  * stream.
889  * 
890  * If @p opcode remains unmodified, then bhnd_sprom_opcode_step() should
891  * proceed to standard evaluation.
892  */
893 static int
894 bhnd_sprom_opcode_rewrite_opcode(bhnd_sprom_opcode_state *state,
895     uint8_t *opcode)
896 {
897         uint8_t op;
898         int     error;
899
900         op = SPROM_OPCODE_OP(*opcode);
901         switch (state->var_state) {
902         case SPROM_OPCODE_VAR_STATE_NONE:
903                 /* No open variable definition */
904                 return (0);
905
906         case SPROM_OPCODE_VAR_STATE_OPEN:
907                 /* Open variable definition; check for implicit closure. */
908
909                 /*
910                  * If a variable definition contains no explicit bind
911                  * instructions prior to closure, we must generate a DO_BIND
912                  * instruction with count and skip values of 1.
913                  */
914                 if (SPROM_OP_IS_VAR_END(op) &&
915                     state->var.bind_total == 0)
916                 {
917                         uint8_t count, skip_in, skip_out;
918                         bool    skip_in_negative;
919
920                         /* Create bind with skip_in/skip_out of 1, count of 1 */
921                         count = 1;
922                         skip_in = 1;
923                         skip_out = 1;
924                         skip_in_negative = false;
925
926                         error = bhnd_sprom_opcode_set_bind(state, count,
927                             skip_in, skip_in_negative, skip_out);
928                         if (error)
929                                 return (error);
930
931                         /* Return DO_BIND */
932                         *opcode = SPROM_OPCODE_DO_BIND |
933                             (0 << SPROM_OP_BIND_SKIP_IN_SIGN) |
934                             (1 << SPROM_OP_BIND_SKIP_IN_SHIFT) |
935                             (1 << SPROM_OP_BIND_SKIP_OUT_SHIFT);
936
937                         return (0);
938                 }
939
940                 /*
941                  * If a variable is implicitly closed (e.g. by a new variable
942                  * definition), we must generate a VAR_END instruction.
943                  */
944                 if (SPROM_OP_IS_IMPLICIT_VAR_END(op)) {
945                         /* Mark as complete */
946                         if ((error = bhnd_sprom_opcode_end_var(state)))
947                                 return (error);
948
949                         /* Return VAR_END */
950                         *opcode = SPROM_OPCODE_VAR_END;
951                         return (0);
952                 }
953                 break;
954
955
956         case SPROM_OPCODE_VAR_STATE_DONE:
957                 /* Previously completed variable definition. Discard variable
958                  * state */
959                 return (bhnd_sprom_opcode_clear_var(state));
960         }
961
962         /* Nothing to do */
963         return (0);
964 }
965
966 /**
967  * Evaluate one opcode from @p state.
968  *
969  * @param state The opcode state to be evaluated.
970  * @param[out] opcode On success, the evaluated opcode
971  * 
972  * @retval 0 success
973  * @retval ENOENT if EOF is reached
974  * @retval non-zero if evaluation otherwise fails, a regular unix error
975  * code will be returned.
976  */
977 static int
978 bhnd_sprom_opcode_step(bhnd_sprom_opcode_state *state, uint8_t *opcode)
979 {
980         int error;
981
982         while (*state->input != SPROM_OPCODE_EOF) {
983                 uint32_t        val;
984                 uint8_t         op, rewrite, immd;
985
986                 /* Fetch opcode */
987                 *opcode = *state->input;
988                 op = SPROM_OPCODE_OP(*opcode);
989                 immd = SPROM_OPCODE_IMM(*opcode);
990
991                 /* Clear any existing bind state */
992                 if ((error = bhnd_sprom_opcode_flush_bind(state)))
993                         return (error);
994
995                 /* Insert local opcode based on current state? */
996                 rewrite = *opcode;
997                 if ((error = bhnd_sprom_opcode_rewrite_opcode(state, &rewrite)))
998                         return (error);
999
1000                 if (rewrite != *opcode) {
1001                         /* Provide rewritten opcode */
1002                         *opcode = rewrite;
1003
1004                         /* We must keep evaluating until we hit a state
1005                          * applicable to the SPROM revision we're parsing */
1006                         if (!bhnd_sprom_opcode_matches_layout_rev(state))
1007                                 continue;
1008
1009                         return (0);
1010                 }
1011
1012                 /* Advance input */
1013                 state->input++;
1014
1015                 switch (op) {
1016                 case SPROM_OPCODE_VAR_IMM:
1017                         if ((error = bhnd_sprom_opcode_set_var(state, immd)))
1018                                 return (error);
1019                         break;
1020
1021                 case SPROM_OPCODE_VAR_REL_IMM:
1022                         error = bhnd_sprom_opcode_set_var(state,
1023                             state->vid + immd);
1024                         if (error)
1025                                 return (error);
1026                         break;
1027
1028                 case SPROM_OPCODE_VAR:
1029                         error = bhnd_sprom_opcode_read_opval32(state, immd,
1030                             &val);
1031                         if (error)
1032                                 return (error);
1033
1034                         if ((error = bhnd_sprom_opcode_set_var(state, val)))
1035                                 return (error);
1036
1037                         break;
1038
1039                 case SPROM_OPCODE_VAR_END:
1040                         if ((error = bhnd_sprom_opcode_end_var(state)))
1041                                 return (error);
1042                         break;
1043
1044                 case SPROM_OPCODE_NELEM:
1045                         immd = *state->input;
1046                         if ((error = bhnd_sprom_opcode_set_nelem(state, immd)))
1047                                 return (error);
1048
1049                         state->input++;
1050                         break;
1051
1052                 case SPROM_OPCODE_DO_BIND:
1053                 case SPROM_OPCODE_DO_BINDN: {
1054                         uint8_t count, skip_in, skip_out;
1055                         bool    skip_in_negative;
1056
1057                         /* Fetch skip arguments */
1058                         skip_in = (immd & SPROM_OP_BIND_SKIP_IN_MASK) >>
1059                             SPROM_OP_BIND_SKIP_IN_SHIFT;
1060
1061                         skip_in_negative =
1062                             ((immd & SPROM_OP_BIND_SKIP_IN_SIGN) != 0);
1063
1064                         skip_out = (immd & SPROM_OP_BIND_SKIP_OUT_MASK) >>
1065                               SPROM_OP_BIND_SKIP_OUT_SHIFT;
1066
1067                         /* Fetch count argument (if any) */
1068                         if (op == SPROM_OPCODE_DO_BINDN) {
1069                                 /* Count is provided as trailing U8 */
1070                                 count = *state->input;
1071                                 state->input++;
1072                         } else {
1073                                 count = 1;
1074                         }
1075
1076                         /* Set BIND state */
1077                         error = bhnd_sprom_opcode_set_bind(state, count,
1078                             skip_in, skip_in_negative, skip_out);
1079                         if (error)
1080                                 return (error);
1081
1082                         break;
1083                 }
1084                 case SPROM_OPCODE_DO_BINDN_IMM: {
1085                         uint8_t count, skip_in, skip_out;
1086                         bool    skip_in_negative;
1087
1088                         /* Implicit skip_in/skip_out of 1, count encoded as immd
1089                          * value */
1090                         count = immd;
1091                         skip_in = 1;
1092                         skip_out = 1;
1093                         skip_in_negative = false;
1094
1095                         error = bhnd_sprom_opcode_set_bind(state, count,
1096                             skip_in, skip_in_negative, skip_out);
1097                         if (error)
1098                                 return (error);
1099                         break;
1100                 }
1101
1102                 case SPROM_OPCODE_REV_IMM:
1103                         error = bhnd_sprom_opcode_set_revs(state, immd, immd);
1104                         if (error)
1105                                 return (error);
1106                         break;
1107
1108                 case SPROM_OPCODE_REV_RANGE: {
1109                         uint8_t range;
1110                         uint8_t rstart, rend;
1111
1112                         /* Revision range is encoded in next byte, as
1113                          * { uint8_t start:4, uint8_t end:4 } */
1114                         range = *state->input;
1115                         rstart = (range & SPROM_OP_REV_START_MASK) >>
1116                             SPROM_OP_REV_START_SHIFT;
1117                         rend = (range & SPROM_OP_REV_END_MASK) >>
1118                             SPROM_OP_REV_END_SHIFT;
1119
1120                         /* Update revision bitmask */
1121                         error = bhnd_sprom_opcode_set_revs(state, rstart, rend);
1122                         if (error)
1123                                 return (error);
1124
1125                         /* Advance input */
1126                         state->input++;
1127                         break;
1128                 }
1129                 case SPROM_OPCODE_MASK_IMM:
1130                         if ((error = bhnd_sprom_opcode_set_mask(state, immd)))
1131                                 return (error);
1132                         break;
1133
1134                 case SPROM_OPCODE_MASK:
1135                         error = bhnd_sprom_opcode_read_opval32(state, immd,
1136                             &val);
1137                         if (error)
1138                                 return (error);
1139
1140                         if ((error = bhnd_sprom_opcode_set_mask(state, val)))
1141                                 return (error);
1142                         break;
1143
1144                 case SPROM_OPCODE_SHIFT_IMM:
1145                         error = bhnd_sprom_opcode_set_shift(state, immd * 2);
1146                         if (error)
1147                                 return (error);
1148                         break;
1149
1150                 case SPROM_OPCODE_SHIFT: {
1151                         int8_t shift;
1152
1153                         if (immd == SPROM_OP_DATA_I8) {
1154                                 shift = (int8_t)(*state->input);
1155                         } else if (immd == SPROM_OP_DATA_U8) {
1156                                 val = *state->input;
1157                                 if (val > INT8_MAX) {
1158                                         SPROM_OP_BAD(state, "invalid shift "
1159                                             "value: %#x\n", val);
1160                                 }
1161
1162                                 shift = val;
1163                         } else {
1164                                 SPROM_OP_BAD(state, "unsupported shift data "
1165                                     "type: %#hhx\n", immd);
1166                                 return (EINVAL);
1167                         }
1168
1169                         if ((error = bhnd_sprom_opcode_set_shift(state, shift)))
1170                                 return (error);
1171
1172                         state->input++;
1173                         break;
1174                 }
1175                 case SPROM_OPCODE_OFFSET_REL_IMM:
1176                         /* Fetch unscaled relative offset */
1177                         val = immd;
1178
1179                         /* Apply scale */
1180                         error = bhnd_sprom_opcode_apply_scale(state, &val);
1181                         if (error)
1182                                 return (error);
1183         
1184                         /* Adding val must not overflow our offset */
1185                         if (UINT32_MAX - state->offset < val) {
1186                                 BHND_NV_LOG("offset out of range\n");
1187                                 return (EINVAL);
1188                         }
1189
1190                         /* Adjust offset */
1191                         state->offset += val;
1192                         break;
1193                 case SPROM_OPCODE_OFFSET:
1194                         error = bhnd_sprom_opcode_read_opval32(state, immd,
1195                             &val);
1196                         if (error)
1197                                 return (error);
1198
1199                         state->offset = val;
1200                         break;
1201
1202                 case SPROM_OPCODE_TYPE:
1203                         /* Type follows as U8 */
1204                         immd = *state->input;
1205                         state->input++;
1206
1207                         /* fall through */
1208                 case SPROM_OPCODE_TYPE_IMM:
1209                         switch (immd) {
1210                         case BHND_NVRAM_TYPE_UINT8:
1211                         case BHND_NVRAM_TYPE_UINT16:
1212                         case BHND_NVRAM_TYPE_UINT32:
1213                         case BHND_NVRAM_TYPE_UINT64:
1214                         case BHND_NVRAM_TYPE_INT8:
1215                         case BHND_NVRAM_TYPE_INT16:
1216                         case BHND_NVRAM_TYPE_INT32:
1217                         case BHND_NVRAM_TYPE_INT64:
1218                         case BHND_NVRAM_TYPE_CHAR:
1219                         case BHND_NVRAM_TYPE_STRING:
1220                                 error = bhnd_sprom_opcode_set_type(state,
1221                                     (bhnd_nvram_type)immd);
1222                                 if (error)
1223                                         return (error);
1224                                 break;
1225                         default:
1226                                 BHND_NV_LOG("unrecognized type %#hhx\n", immd);
1227                                 return (EINVAL);
1228                         }
1229                         break;
1230
1231                 default:
1232                         BHND_NV_LOG("unrecognized opcode %#hhx\n", *opcode);
1233                         return (EINVAL);
1234                 }
1235
1236                 /* We must keep evaluating until we hit a state applicable to
1237                  * the SPROM revision we're parsing */
1238                 if (bhnd_sprom_opcode_matches_layout_rev(state))
1239                         return (0);
1240         }
1241
1242         /* End of opcode stream */
1243         return (ENOENT);
1244 }
1245
1246 /**
1247  * Reset SPROM opcode evaluation state, seek to the @p entry's position,
1248  * and perform complete evaluation of the variable's opcodes.
1249  * 
1250  * @param state The opcode state to be to be evaluated.
1251  * @param entry The indexed variable location.
1252  *
1253  * @retval 0 success
1254  * @retval non-zero If evaluation fails, a regular unix error code will be
1255  * returned.
1256  */
1257 int
1258 bhnd_sprom_opcode_parse_var(bhnd_sprom_opcode_state *state,
1259     bhnd_sprom_opcode_idx_entry *entry)
1260 {
1261         uint8_t opcode;
1262         int     error;
1263
1264         /* Seek to entry */
1265         if ((error = bhnd_sprom_opcode_seek(state, entry)))
1266                 return (error);
1267
1268         /* Parse full variable definition */
1269         while ((error = bhnd_sprom_opcode_step(state, &opcode)) == 0) {
1270                 /* Iterate until VAR_END */
1271                 if (SPROM_OPCODE_OP(opcode) != SPROM_OPCODE_VAR_END)
1272                         continue;
1273
1274                 BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
1275                     ("incomplete variable definition"));
1276
1277                 return (0);
1278         }
1279
1280         /* Error parsing definition */
1281         return (error);
1282 }
1283
1284 /**
1285  * Evaluate @p state until the next variable definition is found.
1286  * 
1287  * @param state The opcode state to be evaluated.
1288  * 
1289  * @retval 0 success
1290  * @retval ENOENT if no additional variable definitions are available.
1291  * @retval non-zero if evaluation otherwise fails, a regular unix error
1292  * code will be returned.
1293  */
1294 static int
1295 bhnd_sprom_opcode_next_var(bhnd_sprom_opcode_state *state)
1296 {
1297         uint8_t opcode;
1298         int     error;
1299
1300         /* Step until we hit a variable opcode */
1301         while ((error = bhnd_sprom_opcode_step(state, &opcode)) == 0) {
1302                 switch (SPROM_OPCODE_OP(opcode)) {
1303                 case SPROM_OPCODE_VAR:
1304                 case SPROM_OPCODE_VAR_IMM:
1305                 case SPROM_OPCODE_VAR_REL_IMM:
1306                         BHND_NV_ASSERT(
1307                             state->var_state == SPROM_OPCODE_VAR_STATE_OPEN,
1308                             ("missing variable definition"));
1309
1310                         return (0);
1311                 default:
1312                         continue;
1313                 }
1314         }
1315
1316         /* Reached EOF, or evaluation failed */
1317         return (error);
1318 }
1319
1320 /**
1321  * Evaluate @p state until the next binding for the current variable definition
1322  * is found.
1323  * 
1324  * @param state The opcode state to be evaluated.
1325  * 
1326  * @retval 0 success
1327  * @retval ENOENT if no additional binding opcodes are found prior to reaching
1328  * a new variable definition, or the end of @p state's binding opcodes.
1329  * @retval non-zero if evaluation otherwise fails, a regular unix error
1330  * code will be returned.
1331  */
1332 int
1333 bhnd_sprom_opcode_next_binding(bhnd_sprom_opcode_state *state)
1334 {
1335         uint8_t opcode;
1336         int     error;
1337
1338         if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN)
1339                 return (EINVAL);
1340
1341         /* Step until we hit a bind opcode, or a new variable */
1342         while ((error = bhnd_sprom_opcode_step(state, &opcode)) == 0) {
1343                 switch (SPROM_OPCODE_OP(opcode)) {
1344                 case SPROM_OPCODE_DO_BIND:
1345                 case SPROM_OPCODE_DO_BINDN:
1346                 case SPROM_OPCODE_DO_BINDN_IMM:
1347                         /* Found next bind */
1348                         BHND_NV_ASSERT(
1349                             state->var_state == SPROM_OPCODE_VAR_STATE_OPEN,
1350                             ("missing variable definition"));
1351                         BHND_NV_ASSERT(state->var.have_bind, ("missing bind"));
1352
1353                         return (0);
1354
1355                 case SPROM_OPCODE_VAR_END:
1356                         /* No further binding opcodes */ 
1357                         BHND_NV_ASSERT(
1358                             state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
1359                             ("variable definition still available"));
1360                         return (ENOENT);
1361                 }
1362         }
1363
1364         /* Not found, or evaluation failed */
1365         return (error);
1366 }