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