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