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