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