xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c (revision 7f9dff23d3092aa33ad45b2b63e52469b3c13a6e)
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/endian.h>
34 
35 #ifdef _KERNEL
36 #include <sys/param.h>
37 #include <sys/ctype.h>
38 #include <sys/malloc.h>
39 #include <sys/systm.h>
40 
41 #include <machine/_inttypes.h>
42 #else /* !_KERNEL */
43 #include <ctype.h>
44 #include <errno.h>
45 #include <inttypes.h>
46 #include <stdint.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #endif /* _KERNEL */
51 
52 #include "bhnd_nvram_private.h"
53 
54 #include "bhnd_nvram_datavar.h"
55 
56 #include "bhnd_nvram_data_spromvar.h"
57 
58 /*
59  * BHND SPROM NVRAM data class
60  *
61  * The SPROM data format is a fixed-layout, non-self-descriptive binary format,
62  * used on Broadcom wireless and wired adapters, that provides a subset of the
63  * variables defined by Broadcom SoC NVRAM formats.
64  */
65 BHND_NVRAM_DATA_CLASS_DEFN(sprom, "Broadcom SPROM",
66    sizeof(struct bhnd_nvram_sprom))
67 
68 static int	sprom_sort_idx(const void *lhs, const void *rhs);
69 
70 static int	sprom_opcode_state_init(struct sprom_opcode_state *state,
71 		    const struct bhnd_sprom_layout *layout);
72 static int	sprom_opcode_state_reset(struct sprom_opcode_state *state);
73 static int	sprom_opcode_state_seek(struct sprom_opcode_state *state,
74 		    struct sprom_opcode_idx *indexed);
75 
76 static int	sprom_opcode_next_var(struct sprom_opcode_state *state);
77 static int	sprom_opcode_parse_var(struct sprom_opcode_state *state,
78 		    struct sprom_opcode_idx *indexed);
79 
80 static int	sprom_opcode_next_binding(struct sprom_opcode_state *state);
81 
82 static int	sprom_opcode_set_type(struct sprom_opcode_state *state,
83 		    bhnd_nvram_type type);
84 
85 static int	sprom_opcode_set_var(struct sprom_opcode_state *state,
86 		    size_t vid);
87 static int	sprom_opcode_clear_var(struct sprom_opcode_state *state);
88 static int	sprom_opcode_flush_bind(struct sprom_opcode_state *state);
89 static int	sprom_opcode_read_opval32(struct sprom_opcode_state *state,
90 		    uint8_t type, uint32_t *opval);
91 static int	sprom_opcode_apply_scale(struct sprom_opcode_state *state,
92 		    uint32_t *value);
93 
94 static int	sprom_opcode_step(struct sprom_opcode_state *state,
95 		    uint8_t *opcode);
96 
97 #define	SPROM_OP_BAD(_state, _fmt, ...)					\
98 	BHND_NV_LOG("bad encoding at %td: " _fmt,			\
99 	    (_state)->input - (_state)->layout->bindings, ##__VA_ARGS__)
100 
101 #define	SPROM_COOKIE_TO_NVRAM(_cookie)	\
102 	bhnd_nvram_get_vardefn(((struct sprom_opcode_idx *)_cookie)->vid)
103 
104 /**
105  * Read the magic value from @p io, and verify that it matches
106  * the @p layout's expected magic value.
107  *
108  * If @p layout does not defined a magic value, @p magic is set to 0x0
109  * and success is returned.
110  *
111  * @param	io	An I/O context mapping the SPROM data to be identified.
112  * @param	layout	The SPROM layout against which @p io should be verified.
113  * @param[out]	magic	On success, the SPROM magic value.
114  *
115  * @retval 0		success
116  * @retval non-zero	If checking @p io otherwise fails, a regular unix
117  *			error code will be returned.
118  */
119 static int
120 bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io *io,
121     const struct bhnd_sprom_layout *layout, uint16_t *magic)
122 {
123 	int error;
124 
125 	/* Skip if layout does not define a magic value */
126 	if (layout->flags & SPROM_LAYOUT_MAGIC_NONE)
127 		return (0);
128 
129 	/* Read the magic value */
130 	error = bhnd_nvram_io_read(io, layout->magic_offset, magic,
131 	    sizeof(*magic));
132 	if (error)
133 		return (error);
134 
135 	*magic = le16toh(*magic);
136 
137 	/* If the signature does not match, skip to next layout */
138 	if (*magic != layout->magic_value)
139 		return (ENXIO);
140 
141 	return (0);
142 }
143 
144 /**
145  * Attempt to identify the format of the SPROM data mapped by @p io.
146  *
147  * The SPROM data format does not provide any identifying information at a
148  * known offset, instead requiring that we iterate over the known SPROM image
149  * sizes until we are able to compute a valid checksum (and, for later
150  * revisions, validate a signature at a revision-specific offset).
151  *
152  * @param	io	An I/O context mapping the SPROM data to be identified.
153  * @param[out]	ident	On success, the identified SPROM layout.
154  * @param[out]	shadow	On success, a correctly sized iobuf instance mapping
155  *			a copy of the identified SPROM image. The caller is
156  *			responsible for deallocating this instance via
157  *			bhnd_nvram_io_free()
158  *
159  * @retval 0		success
160  * @retval non-zero	If identifying @p io otherwise fails, a regular unix
161  *			error code will be returned.
162  */
163 static int
164 bhnd_nvram_sprom_ident(struct bhnd_nvram_io *io,
165     const struct bhnd_sprom_layout **ident, struct bhnd_nvram_io **shadow)
166 {
167 	struct bhnd_nvram_io	*buf;
168 	uint8_t			 crc;
169 	size_t			 crc_errors;
170 	size_t			 sprom_sz_max;
171 	int			 error;
172 
173 	/* Find the largest SPROM layout size */
174 	sprom_sz_max = 0;
175 	for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
176 		sprom_sz_max = bhnd_nv_ummax(sprom_sz_max,
177 		    bhnd_sprom_layouts[i].size);
178 	}
179 
180 	/* Allocate backing buffer and initialize CRC state */
181 	buf = bhnd_nvram_iobuf_empty(0, sprom_sz_max);
182 	crc = BHND_NVRAM_CRC8_INITIAL;
183 	crc_errors = 0;
184 
185 	/* We iterate the SPROM layouts smallest to largest, allowing us to
186 	 * perform incremental checksum calculation */
187 	for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
188 		const struct bhnd_sprom_layout	*layout;
189 		void				*ptr;
190 		size_t				 nbytes, nr;
191 		uint16_t			 magic;
192 		uint8_t				 srev;
193 		bool				 crc_valid;
194 		bool				 have_magic;
195 
196 		layout = &bhnd_sprom_layouts[i];
197 		nbytes = bhnd_nvram_io_getsize(buf);
198 
199 		if ((layout->flags & SPROM_LAYOUT_MAGIC_NONE)) {
200 			have_magic = false;
201 		} else {
202 			have_magic = true;
203 		}
204 
205 		/* Layout instances must be ordered from smallest to largest by
206 		 * the nvram_map compiler */
207 		if (nbytes > layout->size)
208 			BHND_NV_PANIC("SPROM layout is defined out-of-order");
209 
210 		/* Calculate number of additional bytes to be read */
211 		nr = layout->size - nbytes;
212 
213 		/* Adjust the buffer size and fetch a write pointer */
214 		if ((error = bhnd_nvram_io_setsize(buf, layout->size)))
215 			goto failed;
216 
217 		error = bhnd_nvram_io_write_ptr(buf, nbytes, &ptr, nr, NULL);
218 		if (error)
219 			goto failed;
220 
221 		/* Read image data and update CRC (errors are reported
222 		 * after the signature check) */
223 		if ((error = bhnd_nvram_io_read(io, nbytes, ptr, nr)))
224 			goto failed;
225 
226 		crc = bhnd_nvram_crc8(ptr, nr, crc);
227 		crc_valid = (crc == BHND_NVRAM_CRC8_VALID);
228 		if (!crc_valid)
229 			crc_errors++;
230 
231 		/* Fetch SPROM revision */
232 		error = bhnd_nvram_io_read(buf, layout->srev_offset, &srev,
233 		    sizeof(srev));
234 		if (error)
235 			goto failed;
236 
237 		/* Early sromrev 1 devices (specifically some BCM440x enet
238 		 * cards) are reported to have been incorrectly programmed
239 		 * with a revision of 0x10. */
240 		if (layout->rev == 1 && srev == 0x10)
241 			srev = 0x1;
242 
243 		/* Check revision against the layout definition */
244 		if (srev != layout->rev)
245 			continue;
246 
247 		/* Check the magic value, skipping to the next layout on
248 		 * failure. */
249 		error = bhnd_nvram_sprom_check_magic(buf, layout, &magic);
250 		if (error) {
251 			/* If the CRC is was valid, log the mismatch */
252 			if (crc_valid || BHND_NV_VERBOSE) {
253 				BHND_NV_LOG("invalid sprom %hhu signature: "
254 					    "0x%hx (expected 0x%hx)\n", srev,
255 					    magic, layout->magic_value);
256 
257 					error = ENXIO;
258 					goto failed;
259 			}
260 
261 			continue;
262 		}
263 
264 		/* Check for an earlier CRC error */
265 		if (!crc_valid) {
266 			/* If the magic check succeeded, then we may just have
267 			 * data corruption -- log the CRC error */
268 			if (have_magic || BHND_NV_VERBOSE) {
269 				BHND_NV_LOG("sprom %hhu CRC error (crc=%#hhx, "
270 					    "expected=%#x)\n", srev, crc,
271 					    BHND_NVRAM_CRC8_VALID);
272 			}
273 
274 			continue;
275 		}
276 
277 		/* Identified */
278 		*shadow = buf;
279 		*ident = layout;
280 		return (0);
281 	}
282 
283 	/* No match -- set error and fallthrough */
284 	error = ENXIO;
285 	if (crc_errors > 0 && BHND_NV_VERBOSE) {
286 		BHND_NV_LOG("sprom parsing failed with %zu CRC errors\n",
287 		    crc_errors);
288 	}
289 
290 failed:
291 	bhnd_nvram_io_free(buf);
292 	return (error);
293 }
294 
295 static int
296 bhnd_nvram_sprom_probe(struct bhnd_nvram_io *io)
297 {
298 	const struct bhnd_sprom_layout	*layout;
299 	struct bhnd_nvram_io		*shadow;
300 	int				 error;
301 
302 	/* Try to parse the input */
303 	if ((error = bhnd_nvram_sprom_ident(io, &layout, &shadow)))
304 		return (error);
305 
306 	/* Clean up the shadow iobuf */
307 	bhnd_nvram_io_free(shadow);
308 
309 	return (BHND_NVRAM_DATA_PROBE_DEFAULT);
310 }
311 
312 static int
313 bhnd_nvram_sprom_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
314 {
315 	struct bhnd_nvram_sprom		*sp;
316 	size_t				 num_vars;
317 	int				 error;
318 
319 	sp = (struct bhnd_nvram_sprom *)nv;
320 
321 	/* Identify the SPROM input data */
322 	if ((error = bhnd_nvram_sprom_ident(io, &sp->layout, &sp->data)))
323 		goto failed;
324 
325 	/* Initialize SPROM binding eval state */
326 	if ((error = sprom_opcode_state_init(&sp->state, sp->layout)))
327 		goto failed;
328 
329 	/* Allocate our opcode index */
330 	sp->num_idx = sp->layout->num_vars;
331 	if ((sp->idx = bhnd_nv_calloc(sp->num_idx, sizeof(*sp->idx))) == NULL)
332 		goto failed;
333 
334 	/* Parse out index entries from our stateful opcode stream */
335 	for (num_vars = 0; num_vars < sp->num_idx; num_vars++) {
336 		size_t opcodes;
337 
338 		/* Seek to next entry */
339 		if ((error = sprom_opcode_next_var(&sp->state))) {
340 			SPROM_OP_BAD(&sp->state,
341 			    "error reading expected variable entry: %d\n",
342 			    error);
343 			goto failed;
344 		}
345 
346 		/* We limit the SPROM index representations to the minimal
347 		 * type widths capable of covering all known layouts */
348 
349 		/* Save SPROM image offset */
350 		if (sp->state.offset > UINT16_MAX) {
351 			SPROM_OP_BAD(&sp->state,
352 			    "cannot index large offset %u\n", sp->state.offset);
353 		}
354 		sp->idx[num_vars].offset = sp->state.offset;
355 
356 		/* Save current variable ID */
357 		if (sp->state.vid > UINT16_MAX) {
358 			SPROM_OP_BAD(&sp->state,
359 			    "cannot index large vid %zu\n",  sp->state.vid);
360 		}
361 		sp->idx[num_vars].vid = sp->state.vid;
362 
363 		/* Save opcode position */
364 		opcodes = (sp->state.input - sp->layout->bindings);
365 		if (opcodes > UINT16_MAX) {
366 			SPROM_OP_BAD(&sp->state,
367 			    "cannot index large opcode offset %zu\n", opcodes);
368 		}
369 		sp->idx[num_vars].opcodes = opcodes;
370 	}
371 
372 	/* Should have reached end of binding table; next read must return
373 	 * ENOENT */
374 	if ((error = sprom_opcode_next_var(&sp->state)) != ENOENT) {
375 		BHND_NV_LOG("expected EOF parsing binding table: %d\n", error);
376 		goto failed;
377 	}
378 
379         /* Sort index by variable ID, ascending */
380         qsort(sp->idx, sp->num_idx, sizeof(sp->idx[0]), sprom_sort_idx);
381 
382 	return (0);
383 
384 failed:
385 	if (sp->data != NULL)
386 		bhnd_nvram_io_free(sp->data);
387 
388 	if (sp->idx != NULL)
389 		bhnd_nv_free(sp->idx);
390 
391 	return (error);
392 }
393 
394 /* sort function for sprom_opcode_idx values */
395 static int
396 sprom_sort_idx(const void *lhs, const void *rhs)
397 {
398 	const struct sprom_opcode_idx	*l, *r;
399 
400 	l = lhs;
401 	r = rhs;
402 
403 	if (l->vid < r->vid)
404 		return (-1);
405 	if (l->vid > r->vid)
406 		return (1);
407 	return (0);
408 }
409 
410 static void
411 bhnd_nvram_sprom_free(struct bhnd_nvram_data *nv)
412 {
413 	struct bhnd_nvram_sprom *sp = (struct bhnd_nvram_sprom *)nv;
414 
415 	bhnd_nvram_io_free(sp->data);
416 	bhnd_nv_free(sp->idx);
417 }
418 
419 size_t
420 bhnd_nvram_sprom_count(struct bhnd_nvram_data *nv)
421 {
422 	struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv;
423 	return (sprom->layout->num_vars);
424 }
425 
426 static int
427 bhnd_nvram_sprom_size(struct bhnd_nvram_data *nv, size_t *size)
428 {
429 	struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv;
430 
431 	/* The serialized form will be identical in length
432 	 * to our backing buffer representation */
433 	*size = bhnd_nvram_io_getsize(sprom->data);
434 	return (0);
435 }
436 
437 static int
438 bhnd_nvram_sprom_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len)
439 {
440 	struct bhnd_nvram_sprom	*sprom;
441 	size_t			 limit, req_len;
442 	int			 error;
443 
444 	sprom = (struct bhnd_nvram_sprom *)nv;
445 	limit = *len;
446 
447 	/* Provide the required size */
448 	if ((error = bhnd_nvram_sprom_size(nv, &req_len)))
449 		return (error);
450 
451 	*len = req_len;
452 
453 	if (buf == NULL) {
454 		return (0);
455 	} else if (*len > limit) {
456 		return (ENOMEM);
457 	}
458 
459 	/* Write to the output buffer */
460 	return (bhnd_nvram_io_read(sprom->data, 0x0, buf, *len));
461 }
462 
463 static uint32_t
464 bhnd_nvram_sprom_caps(struct bhnd_nvram_data *nv)
465 {
466 	return (BHND_NVRAM_DATA_CAP_INDEXED);
467 }
468 
469 static const char *
470 bhnd_nvram_sprom_next(struct bhnd_nvram_data *nv, void **cookiep)
471 {
472 	struct bhnd_nvram_sprom		*sp;
473 	struct sprom_opcode_idx		*idx_entry;
474 	size_t				 idx_next;
475 	const struct bhnd_nvram_vardefn	*var;
476 
477 	sp = (struct bhnd_nvram_sprom *)nv;
478 
479 	/* Seek to appropriate starting point */
480 	if (*cookiep == NULL) {
481 		/* Start search at first index entry */
482 		idx_next = 0;
483 	} else {
484 		/* Determine current index position */
485 		idx_entry = *cookiep;
486 		idx_next = (size_t)(idx_entry - sp->idx);
487 		BHND_NV_ASSERT(idx_next < sp->num_idx,
488 		    ("invalid index %zu; corrupt cookie?", idx_next));
489 
490 		/* Advance to next entry */
491 		idx_next++;
492 
493 		/* Check for EOF */
494 		if (idx_next == sp->num_idx)
495 			return (NULL);
496 	}
497 
498 	/* Skip entries that are disabled by virtue of IGNALL1 */
499 	for (; idx_next < sp->num_idx; idx_next++) {
500 		/* Fetch index entry and update cookiep  */
501 		idx_entry = &sp->idx[idx_next];
502 		*cookiep = idx_entry;
503 
504 		/* Fetch variable definition */
505 		var = bhnd_nvram_get_vardefn(idx_entry->vid);
506 
507 		/* We might need to parse the variable's value to determine
508 		 * whether it should be treated as unset */
509 		if (var->flags & BHND_NVRAM_VF_IGNALL1) {
510 			int     error;
511 			size_t  len;
512 
513 			error = bhnd_nvram_sprom_getvar(nv, *cookiep, NULL,
514 			    &len, var->type);
515 			if (error) {
516 				BHND_NV_ASSERT(error == ENOENT, ("unexpected "
517 				    "error parsing variable: %d", error));
518 
519 				continue;
520 			}
521 		}
522 
523 		/* Found! */
524 		return (var->name);
525 	}
526 
527 	/* Reached end of index entries */
528 	return (NULL);
529 }
530 
531 /* bsearch function used by bhnd_nvram_sprom_find() */
532 static int
533 bhnd_nvram_sprom_find_vid_compare(const void *key, const void *rhs)
534 {
535 	const struct sprom_opcode_idx	*r;
536 	size_t				 l;
537 
538 	l = *(const size_t *)key;
539 	r = rhs;
540 
541 	if (l < r->vid)
542 		return (-1);
543 	if (l > r->vid)
544 		return (1);
545 	return (0);
546 }
547 
548 static void *
549 bhnd_nvram_sprom_find(struct bhnd_nvram_data *nv, const char *name)
550 {
551 	struct bhnd_nvram_sprom		*sp;
552 	const struct bhnd_nvram_vardefn	*var;
553 	size_t				 vid;
554 
555 	sp = (struct bhnd_nvram_sprom *)nv;
556 
557 	/* Determine the variable ID for the given name */
558 	if ((var = bhnd_nvram_find_vardefn(name)) == NULL)
559 		return (NULL);
560 
561 	vid = bhnd_nvram_get_vardefn_id(var);
562 
563 	/* Search our index for the variable ID */
564 	return (bsearch(&vid, sp->idx, sp->num_idx, sizeof(sp->idx[0]),
565 	    bhnd_nvram_sprom_find_vid_compare));
566 }
567 
568 /**
569  * Read the value of @p type from the SPROM data at @p offset, apply @p mask
570  * and @p shift, and OR with the existing @p value.
571  *
572  * @param sp The SPROM data instance.
573  * @param var The NVRAM variable definition
574  * @param type The type to read at @p offset
575  * @param offset The data offset to be read.
576  * @param mask The mask to be applied to the value read at @p offset.
577  * @param shift The shift to be applied after masking; if positive, a right
578  * shift will be applied, if negative, a left shift.
579  * @param value The read destination; the parsed value will be OR'd with the
580  * current contents of @p value.
581  */
582 static int
583 bhnd_nvram_sprom_read_offset(struct bhnd_nvram_sprom *sp,
584     const struct bhnd_nvram_vardefn *var, bhnd_nvram_type type,
585     size_t offset, uint32_t mask, int8_t shift,
586     union bhnd_nvram_sprom_intv *value)
587 {
588 	size_t	sp_width;
589 	int	error;
590 	union {
591 		uint8_t		u8;
592 		uint16_t	u16;
593 		uint32_t	u32;
594 		int8_t		s8;
595 		int16_t		s16;
596 		int32_t		s32;
597 	} sp_value;
598 
599 	/* Determine type width */
600 	sp_width = bhnd_nvram_value_size(type, NULL, 0, 1);
601 	if (sp_width == 0) {
602 		/* Variable-width types are unsupported */
603 		BHND_NV_LOG("invalid %s SPROM offset type %d\n", var->name,
604 		    type);
605 		return (EFTYPE);
606 	}
607 
608 	/* Perform read */
609 	error = bhnd_nvram_io_read(sp->data, offset, &sp_value,
610 	    sp_width);
611 	if (error) {
612 		BHND_NV_LOG("error reading %s SPROM offset %#zx: %d\n",
613 		    var->name, offset, error);
614 		return (EFTYPE);
615 	}
616 
617 #define	NV_PARSE_INT(_type, _src, _dest, _swap)	do {			\
618 	/* Swap to host byte order */					\
619 	sp_value. _src = (_type) _swap(sp_value. _src);			\
620 									\
621 	/* Mask and shift the value */					\
622 	sp_value. _src &= mask;				\
623 	if (shift > 0) {					\
624 		sp_value. _src >>= shift;			\
625 	} else if (shift < 0) {				\
626 		sp_value. _src <<= -shift;			\
627 	}								\
628 									\
629 	/* Emit output, widening to 32-bit representation  */		\
630 	value-> _dest |= sp_value. _src;				\
631 } while(0)
632 
633 	/* Apply mask/shift and widen to a common 32bit representation */
634 	switch (type) {
635 	case BHND_NVRAM_TYPE_UINT8:
636 		NV_PARSE_INT(uint8_t,	u8,	u32,	);
637 		break;
638 	case BHND_NVRAM_TYPE_UINT16:
639 		NV_PARSE_INT(uint16_t,	u16,	u32,	le16toh);
640 		break;
641 	case BHND_NVRAM_TYPE_UINT32:
642 		NV_PARSE_INT(uint32_t,	u32,	u32,	le32toh);
643 		break;
644 	case BHND_NVRAM_TYPE_INT8:
645 		NV_PARSE_INT(int8_t,	s8,	s32,	);
646 		break;
647 	case BHND_NVRAM_TYPE_INT16:
648 		NV_PARSE_INT(int16_t,	s16,	s32,	le16toh);
649 		break;
650 	case BHND_NVRAM_TYPE_INT32:
651 		NV_PARSE_INT(int32_t,	s32,	s32,	le32toh);
652 		break;
653 	case BHND_NVRAM_TYPE_CHAR:
654 		NV_PARSE_INT(uint8_t,	u8,	u32,	);
655 		break;
656 
657 	case BHND_NVRAM_TYPE_UINT64:
658 	case BHND_NVRAM_TYPE_INT64:
659 	case BHND_NVRAM_TYPE_STRING:
660 		/* fallthrough (unused by SPROM) */
661 	default:
662 		BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
663 		return (EFTYPE);
664 	}
665 
666 	return (0);
667 }
668 
669 static int
670 bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
671     size_t *len, bhnd_nvram_type otype)
672 {
673 	bhnd_nvram_val_t		 val;
674 	struct bhnd_nvram_sprom		*sp;
675 	struct sprom_opcode_idx		*idx;
676 	const struct bhnd_nvram_vardefn	*var;
677 	union bhnd_nvram_sprom_storage	 storage;
678 	union bhnd_nvram_sprom_storage	*inp;
679 	union bhnd_nvram_sprom_intv	 intv;
680 	bhnd_nvram_type			 var_btype;
681 	size_t				 ilen, ipos, iwidth;
682 	size_t				 nelem;
683 	bool				 all_bits_set;
684 	int				 error;
685 
686 	sp = (struct bhnd_nvram_sprom *)nv;
687 	idx = cookiep;
688 
689 	BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
690 
691 	/* Fetch canonical variable definition */
692 	var = SPROM_COOKIE_TO_NVRAM(cookiep);
693 	BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
694 
695 	/*
696 	 * Fetch the array length from the SPROM variable definition.
697 	 *
698 	 * This generally be identical to the array length provided by the
699 	 * canonical NVRAM variable definition, but some SPROM layouts may
700 	 * define a smaller element count.
701 	 */
702 	if ((error = sprom_opcode_parse_var(&sp->state, idx))) {
703 		BHND_NV_LOG("variable evaluation failed: %d\n", error);
704 		return (error);
705 	}
706 
707 	nelem = sp->state.var.nelem;
708 	if (nelem > var->nelem) {
709 		BHND_NV_LOG("SPROM array element count %zu cannot be "
710 		    "represented by '%s' element count of %hhu\n", nelem,
711 		    var->name, var->nelem);
712 		return (EFTYPE);
713 	}
714 
715 	/* Fetch the var's base element type */
716 	var_btype = bhnd_nvram_base_type(var->type);
717 
718 	/* Calculate total byte length of the native encoding */
719 	if ((iwidth = bhnd_nvram_value_size(var_btype, NULL, 0, 1)) == 0) {
720 		/* SPROM does not use (and we do not support) decoding of
721 		 * variable-width data types */
722 		BHND_NV_LOG("invalid SPROM data type: %d", var->type);
723 		return (EFTYPE);
724 	}
725 	ilen = nelem * iwidth;
726 
727 	/* Decode into our own local storage. */
728 	inp = &storage;
729 	if (ilen > sizeof(storage)) {
730 		BHND_NV_LOG("error decoding '%s', SPROM_ARRAY_MAXLEN "
731 		    "incorrect\n", var->name);
732 		return (EFTYPE);
733 	}
734 
735 	/* Zero-initialize our decode buffer; any output elements skipped
736 	 * during decode should default to zero. */
737 	memset(inp, 0, ilen);
738 
739 	/*
740 	 * Decode the SPROM data, iteratively decoding up to nelem values.
741 	 */
742 	if ((error = sprom_opcode_state_seek(&sp->state, idx))) {
743 		BHND_NV_LOG("variable seek failed: %d\n", error);
744 		return (error);
745 	}
746 
747 	ipos = 0;
748 	intv.u32 = 0x0;
749 	if (var->flags & BHND_NVRAM_VF_IGNALL1)
750 		all_bits_set = true;
751 	else
752 		all_bits_set = false;
753 	while ((error = sprom_opcode_next_binding(&sp->state)) == 0) {
754 		struct sprom_opcode_bind	*binding;
755 		struct sprom_opcode_var		*binding_var;
756 		bhnd_nvram_type			 intv_type;
757 		size_t				 offset;
758 		size_t				 nbyte;
759 		uint32_t			 skip_in_bytes;
760 		void				*ptr;
761 
762 		BHND_NV_ASSERT(
763 		    sp->state.var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
764 		    ("invalid var state"));
765 		BHND_NV_ASSERT(sp->state.var.have_bind, ("invalid bind state"));
766 
767 		binding_var = &sp->state.var;
768 		binding = &sp->state.var.bind;
769 
770 		if (ipos >= nelem) {
771 			BHND_NV_LOG("output skip %u positioned "
772 			    "%zu beyond nelem %zu\n",
773 			    binding->skip_out, ipos, nelem);
774 			return (EINVAL);
775 		}
776 
777 		/* Calculate input skip bytes for this binding */
778 		skip_in_bytes = binding->skip_in;
779 		error = sprom_opcode_apply_scale(&sp->state, &skip_in_bytes);
780 		if (error)
781 			return (error);
782 
783 		/* Bind */
784 		offset = sp->state.offset;
785 		for (size_t i = 0; i < binding->count; i++) {
786 			/* Read the offset value, OR'ing with the current
787 			 * value of intv */
788 			error = bhnd_nvram_sprom_read_offset(sp, var,
789 			    binding_var->base_type,
790 			    offset,
791 			    binding_var->mask,
792 			    binding_var->shift,
793 			    &intv);
794 			if (error)
795 				return (error);
796 
797 			/* If IGNALL1, record whether value does not have
798 			 * all bits set. */
799 			if (var->flags & BHND_NVRAM_VF_IGNALL1 &&
800 			    all_bits_set)
801 			{
802 				uint32_t all1;
803 
804 				all1 = binding_var->mask;
805 				if (binding_var->shift > 0)
806 					all1 >>= binding_var->shift;
807 				else if (binding_var->shift < 0)
808 					all1 <<= -binding_var->shift;
809 
810 				if ((intv.u32 & all1) != all1)
811 					all_bits_set = false;
812 			}
813 
814 			/* Adjust input position; this was already verified to
815 			 * not overflow/underflow during SPROM opcode
816 			 * evaluation */
817 			if (binding->skip_in_negative) {
818 				offset -= skip_in_bytes;
819 			} else {
820 				offset += skip_in_bytes;
821 			}
822 
823 			/* Skip writing to inp if additional bindings are
824 			 * required to fully populate intv */
825 			if (binding->skip_out == 0)
826 				continue;
827 
828 			/* We use bhnd_nvram_value_coerce() to perform
829 			 * overflow-checked coercion from the widened
830 			 * uint32/int32 intv value to the requested output
831 			 * type */
832 			if (bhnd_nvram_is_signed_type(var_btype))
833 				intv_type = BHND_NVRAM_TYPE_INT32;
834 			else
835 				intv_type = BHND_NVRAM_TYPE_UINT32;
836 
837 			/* Calculate address of the current element output
838 			 * position */
839 			ptr = (uint8_t *)inp + (iwidth * ipos);
840 
841 			/* Perform coercion of the array element */
842 			nbyte = iwidth;
843 			error = bhnd_nvram_value_coerce(&intv, sizeof(intv),
844 			    intv_type, ptr, &nbyte, var_btype);
845 			if (error)
846 				return (error);
847 
848 			/* Clear temporary state */
849 			intv.u32 = 0x0;
850 
851 			/* Advance output position */
852 			if (SIZE_MAX - binding->skip_out < ipos) {
853 				BHND_NV_LOG("output skip %u would overflow "
854 				    "%zu\n", binding->skip_out, ipos);
855 				return (EINVAL);
856 			}
857 
858 			ipos += binding->skip_out;
859 		}
860 	}
861 
862 	/* Did we iterate all bindings until hitting end of the variable
863 	 * definition? */
864 	BHND_NV_ASSERT(error != 0, ("loop terminated early"));
865 	if (error != ENOENT) {
866 		return (error);
867 	}
868 
869 	/* If marked IGNALL1 and all bits are set, treat variable as
870 	 * unavailable */
871 	if ((var->flags & BHND_NVRAM_VF_IGNALL1) && all_bits_set)
872 		return (ENOENT);
873 
874 
875 	/* Perform value coercion from our local representation */
876 	error = bhnd_nvram_val_init(&val, var->fmt, inp, ilen, var->type,
877 	    BHND_NVRAM_VAL_BORROW_DATA);
878 	if (error)
879 		return (error);
880 
881 	error = bhnd_nvram_val_encode(&val, buf, len, otype);
882 
883 	/* Clean up */
884 	bhnd_nvram_val_release(&val);
885 	return (error);
886 }
887 
888 static const void *
889 bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
890     size_t *len, bhnd_nvram_type *type)
891 {
892 	/* Unsupported */
893 	return (NULL);
894 }
895 
896 static const char *
897 bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
898 {
899 	const struct bhnd_nvram_vardefn	*var;
900 
901 	BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
902 
903 	var = SPROM_COOKIE_TO_NVRAM(cookiep);
904 	BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
905 
906 	return (var->name);
907 }
908 
909 /**
910  * Initialize SPROM opcode evaluation state.
911  *
912  * @param state The opcode state to be initialized.
913  * @param layout The SPROM layout to be parsed by this instance.
914  *
915  *
916  * @retval 0 success
917  * @retval non-zero If initialization fails, a regular unix error code will be
918  * returned.
919  */
920 static int
921 sprom_opcode_state_init(struct sprom_opcode_state *state,
922     const struct bhnd_sprom_layout *layout)
923 {
924 	memset(state, 0, sizeof(*state));
925 
926 	state->layout = layout;
927 	state->input = layout->bindings;
928 	state->var_state = SPROM_OPCODE_VAR_STATE_NONE;
929 
930 	bit_set(state->revs, layout->rev);
931 
932 	return (0);
933 }
934 
935 /**
936  * Reset SPROM opcode evaluation state; future evaluation will be performed
937  * starting at the first opcode.
938  *
939  * @param state The opcode state to be reset.
940  *
941  * @retval 0 success
942  * @retval non-zero If reset fails, a regular unix error code will be returned.
943  */
944 static int
945 sprom_opcode_state_reset(struct sprom_opcode_state *state)
946 {
947 	return (sprom_opcode_state_init(state, state->layout));
948 }
949 
950 /**
951  * Reset SPROM opcode evaluation state and seek to the @p indexed position.
952  *
953  * @param state The opcode state to be reset.
954  * @param indexed The indexed location to which we'll seek the opcode state.
955  */
956 static int
957 sprom_opcode_state_seek(struct sprom_opcode_state *state,
958     struct sprom_opcode_idx *indexed)
959 {
960 	int error;
961 
962 	BHND_NV_ASSERT(indexed->opcodes < state->layout->bindings_size,
963 	    ("index entry references invalid opcode position"));
964 
965 	/* Reset state */
966 	if ((error = sprom_opcode_state_reset(state)))
967 		return (error);
968 
969 	/* Seek to the indexed sprom opcode offset */
970 	state->input = state->layout->bindings + indexed->opcodes;
971 
972 	/* Restore the indexed sprom data offset and VID */
973 	state->offset = indexed->offset;
974 
975 	/* Restore the indexed sprom variable ID */
976 	if ((error = sprom_opcode_set_var(state, indexed->vid)))
977 		return (error);
978 
979 	return (0);
980 }
981 
982 /**
983  * Set the current revision range for @p state. This also resets
984  * variable state.
985  *
986  * @param state The opcode state to update
987  * @param start The first revision in the range.
988  * @param end The last revision in the range.
989  *
990  * @retval 0 success
991  * @retval non-zero If updating @p state fails, a regular unix error code will
992  * be returned.
993  */
994 static inline int
995 sprom_opcode_set_revs(struct sprom_opcode_state *state, uint8_t start,
996     uint8_t end)
997 {
998 	int error;
999 
1000 	/* Validate the revision range */
1001 	if (start > SPROM_OP_REV_MAX ||
1002 	    end > SPROM_OP_REV_MAX ||
1003 	    end < start)
1004 	{
1005 		SPROM_OP_BAD(state, "invalid revision range: %hhu-%hhu\n",
1006 		    start, end);
1007 		return (EINVAL);
1008 	}
1009 
1010 	/* Clear variable state */
1011 	if ((error = sprom_opcode_clear_var(state)))
1012 		return (error);
1013 
1014 	/* Reset revision mask */
1015 	memset(state->revs, 0x0, sizeof(state->revs));
1016 	bit_nset(state->revs, start, end);
1017 
1018 	return (0);
1019 }
1020 
1021 /**
1022  * Set the current variable's value mask for @p state.
1023  *
1024  * @param state The opcode state to update
1025  * @param mask The mask to be set
1026  *
1027  * @retval 0 success
1028  * @retval non-zero If updating @p state fails, a regular unix error code will
1029  * be returned.
1030  */
1031 static inline int
1032 sprom_opcode_set_mask(struct sprom_opcode_state *state, uint32_t mask)
1033 {
1034 	if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
1035 		SPROM_OP_BAD(state, "no open variable definition\n");
1036 		return (EINVAL);
1037 	}
1038 
1039 	state->var.mask = mask;
1040 	return (0);
1041 }
1042 
1043 /**
1044  * Set the current variable's value shift for @p state.
1045  *
1046  * @param state The opcode state to update
1047  * @param shift The shift to be set
1048  *
1049  * @retval 0 success
1050  * @retval non-zero If updating @p state fails, a regular unix error code will
1051  * be returned.
1052  */
1053 static inline int
1054 sprom_opcode_set_shift(struct sprom_opcode_state *state, int8_t shift)
1055 {
1056 	if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
1057 		SPROM_OP_BAD(state, "no open variable definition\n");
1058 		return (EINVAL);
1059 	}
1060 
1061 	state->var.shift = shift;
1062 	return (0);
1063 }
1064 
1065 /**
1066  * Register a new BIND/BINDN operation with @p state.
1067  *
1068  * @param state The opcode state to update.
1069  * @param count The number of elements to be bound.
1070  * @param skip_in The number of input elements to skip after each bind.
1071  * @param skip_in_negative If true, the input skip should be subtracted from
1072  * the current offset after each bind. If false, the input skip should be
1073  * added.
1074  * @param skip_out The number of output elements to skip after each bind.
1075  *
1076  * @retval 0 success
1077  * @retval EINVAL if a variable definition is not open.
1078  * @retval EINVAL if @p skip_in and @p count would trigger an overflow or
1079  * underflow when applied to the current input offset.
1080  * @retval ERANGE if @p skip_in would overflow uint32_t when multiplied by
1081  * @p count and the scale value.
1082  * @retval ERANGE if @p skip_out would overflow uint32_t when multiplied by
1083  * @p count and the scale value.
1084  * @retval non-zero If updating @p state otherwise fails, a regular unix error
1085  * code will be returned.
1086  */
1087 static inline int
1088 sprom_opcode_set_bind(struct sprom_opcode_state *state, uint8_t count,
1089     uint8_t skip_in, bool skip_in_negative, uint8_t skip_out)
1090 {
1091 	uint32_t	iskip_total;
1092 	uint32_t	iskip_scaled;
1093 	int		error;
1094 
1095 	/* Must have an open variable */
1096 	if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
1097 		SPROM_OP_BAD(state, "no open variable definition\n");
1098 		SPROM_OP_BAD(state, "BIND outside of variable definition\n");
1099 		return (EINVAL);
1100 	}
1101 
1102 	/* Cannot overwite an existing bind definition */
1103 	if (state->var.have_bind) {
1104 		SPROM_OP_BAD(state, "BIND overwrites existing definition\n");
1105 		return (EINVAL);
1106 	}
1107 
1108 	/* Must have a count of at least 1 */
1109 	if (count == 0) {
1110 		SPROM_OP_BAD(state, "BIND with zero count\n");
1111 		return (EINVAL);
1112 	}
1113 
1114 	/* Scale skip_in by the current type width */
1115 	iskip_scaled = skip_in;
1116 	if ((error = sprom_opcode_apply_scale(state, &iskip_scaled)))
1117 		return (error);
1118 
1119 	/* Calculate total input bytes skipped: iskip_scaled * count) */
1120 	if (iskip_scaled > 0 && UINT32_MAX / iskip_scaled < count) {
1121 		SPROM_OP_BAD(state, "skip_in %hhu would overflow", skip_in);
1122 		return (EINVAL);
1123 	}
1124 
1125 	iskip_total = iskip_scaled * count;
1126 
1127 	/* Verify that the skip_in value won't under/overflow the current
1128 	 * input offset. */
1129 	if (skip_in_negative) {
1130 		if (iskip_total > state->offset) {
1131 			SPROM_OP_BAD(state, "skip_in %hhu would underflow "
1132 			    "offset %u\n", skip_in, state->offset);
1133 			return (EINVAL);
1134 		}
1135 	} else {
1136 		if (UINT32_MAX - iskip_total < state->offset) {
1137 			SPROM_OP_BAD(state, "skip_in %hhu would overflow "
1138 			    "offset %u\n", skip_in, state->offset);
1139 			return (EINVAL);
1140 		}
1141 	}
1142 
1143 	/* Set the actual count and skip values */
1144 	state->var.have_bind = true;
1145 	state->var.bind.count = count;
1146 	state->var.bind.skip_in = skip_in;
1147 	state->var.bind.skip_out = skip_out;
1148 
1149 	state->var.bind.skip_in_negative = skip_in_negative;
1150 
1151 	/* Update total bind count for the current variable */
1152 	state->var.bind_total++;
1153 
1154 	return (0);
1155 }
1156 
1157 
1158 /**
1159  * Apply and clear the current opcode bind state, if any.
1160  *
1161  * @param state The opcode state to update.
1162  *
1163  * @retval 0 success
1164  * @retval non-zero If updating @p state otherwise fails, a regular unix error
1165  * code will be returned.
1166  */
1167 static int
1168 sprom_opcode_flush_bind(struct sprom_opcode_state *state)
1169 {
1170 	int		error;
1171 	uint32_t	skip;
1172 
1173 	/* Nothing to do? */
1174 	if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN ||
1175 	    !state->var.have_bind)
1176 		return (0);
1177 
1178 	/* Apply SPROM offset adjustment */
1179 	if (state->var.bind.count > 0) {
1180 		skip = state->var.bind.skip_in * state->var.bind.count;
1181 		if ((error = sprom_opcode_apply_scale(state, &skip)))
1182 			return (error);
1183 
1184 		if (state->var.bind.skip_in_negative) {
1185 			state->offset -= skip;
1186 		} else {
1187 			state->offset += skip;
1188 		}
1189 	}
1190 
1191 	/* Clear bind state */
1192 	memset(&state->var.bind, 0, sizeof(state->var.bind));
1193 	state->var.have_bind = false;
1194 
1195 	return (0);
1196 }
1197 
1198 /**
1199  * Set the current type to @p type, and reset type-specific
1200  * stream state.
1201  *
1202  * @param state The opcode state to update.
1203  * @param type The new type.
1204  *
1205  * @retval 0 success
1206  * @retval EINVAL if @p vid is not a valid variable ID.
1207  */
1208 static int
1209 sprom_opcode_set_type(struct sprom_opcode_state *state, bhnd_nvram_type type)
1210 {
1211 	bhnd_nvram_type	base_type;
1212 	size_t		width;
1213 	uint32_t	mask;
1214 
1215 	/* Must have an open variable definition */
1216 	if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
1217 		SPROM_OP_BAD(state, "type set outside variable definition\n");
1218 		return (EINVAL);
1219 	}
1220 
1221 	/* Fetch type width for use as our scale value */
1222 	width = bhnd_nvram_value_size(type, NULL, 0, 1);
1223 	if (width == 0) {
1224 		SPROM_OP_BAD(state, "unsupported variable-width type: %d\n",
1225 		    type);
1226 		return (EINVAL);
1227 	} else if (width > UINT32_MAX) {
1228 		SPROM_OP_BAD(state, "invalid type width %zu for type: %d\n",
1229 		    width, type);
1230 		return (EINVAL);
1231 	}
1232 
1233 	/* Determine default mask value for the element type */
1234 	base_type = bhnd_nvram_base_type(type);
1235 	switch (base_type) {
1236 	case BHND_NVRAM_TYPE_UINT8:
1237 	case BHND_NVRAM_TYPE_INT8:
1238 	case BHND_NVRAM_TYPE_CHAR:
1239 		mask = UINT8_MAX;
1240 		break;
1241 	case BHND_NVRAM_TYPE_UINT16:
1242 	case BHND_NVRAM_TYPE_INT16:
1243 		mask = UINT16_MAX;
1244 		break;
1245 	case BHND_NVRAM_TYPE_UINT32:
1246 	case BHND_NVRAM_TYPE_INT32:
1247 		mask = UINT32_MAX;
1248 		break;
1249 	case BHND_NVRAM_TYPE_STRING:
1250 		/* fallthrough (unused by SPROM) */
1251 	default:
1252 		SPROM_OP_BAD(state, "unsupported type: %d\n", type);
1253 		return (EINVAL);
1254 	}
1255 
1256 	/* Update state */
1257 	state->var.base_type = base_type;
1258 	state->var.mask = mask;
1259 	state->var.scale = (uint32_t)width;
1260 
1261 	return (0);
1262 }
1263 
1264 /**
1265  * Clear current variable state, if any.
1266  *
1267  * @param state The opcode state to update.
1268  */
1269 static int
1270 sprom_opcode_clear_var(struct sprom_opcode_state *state)
1271 {
1272 	if (state->var_state == SPROM_OPCODE_VAR_STATE_NONE)
1273 		return (0);
1274 
1275 	BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
1276 	    ("incomplete variable definition"));
1277 	BHND_NV_ASSERT(!state->var.have_bind, ("stale bind state"));
1278 
1279 	memset(&state->var, 0, sizeof(state->var));
1280 	state->var_state = SPROM_OPCODE_VAR_STATE_NONE;
1281 
1282 	return (0);
1283 }
1284 
1285 /**
1286  * Set the current variable's array element count to @p nelem.
1287  *
1288  * @param state The opcode state to update.
1289  * @param nelem The new array length.
1290  *
1291  * @retval 0 success
1292  * @retval EINVAL if no open variable definition exists.
1293  * @retval EINVAL if @p nelem is zero.
1294  * @retval ENXIO if @p nelem is greater than one, and the current variable does
1295  * not have an array type.
1296  * @retval ENXIO if @p nelem exceeds the array length of the NVRAM variable
1297  * definition.
1298  */
1299 static int
1300 sprom_opcode_set_nelem(struct sprom_opcode_state *state, uint8_t nelem)
1301 {
1302 	const struct bhnd_nvram_vardefn	*var;
1303 
1304 	/* Must have a defined variable */
1305 	if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
1306 		SPROM_OP_BAD(state, "array length set without open variable "
1307 		    "state");
1308 		return (EINVAL);
1309 	}
1310 
1311 	/* Locate the actual variable definition */
1312 	if ((var = bhnd_nvram_get_vardefn(state->vid)) == NULL) {
1313 		SPROM_OP_BAD(state, "unknown variable ID: %zu\n", state->vid);
1314 		return (EINVAL);
1315 	}
1316 
1317 	/* Must be greater than zero */
1318 	if (nelem == 0) {
1319 		SPROM_OP_BAD(state, "invalid nelem: %hhu\n", nelem);
1320 		return (EINVAL);
1321 	}
1322 
1323 	/* If the variable is not an array-typed value, the array length
1324 	 * must be 1 */
1325 	if (!bhnd_nvram_is_array_type(var->type) && nelem != 1) {
1326 		SPROM_OP_BAD(state, "nelem %hhu on non-array %zu\n", nelem,
1327 		    state->vid);
1328 		return (ENXIO);
1329 	}
1330 
1331 	/* Cannot exceed the variable's defined array length */
1332 	if (nelem > var->nelem) {
1333 		SPROM_OP_BAD(state, "nelem %hhu exceeds %zu length %hhu\n",
1334 		    nelem, state->vid, var->nelem);
1335 		return (ENXIO);
1336 	}
1337 
1338 	/* Valid length; update state */
1339 	state->var.nelem = nelem;
1340 
1341 	return (0);
1342 }
1343 
1344 /**
1345  * Set the current variable ID to @p vid, and reset variable-specific
1346  * stream state.
1347  *
1348  * @param state The opcode state to update.
1349  * @param vid The new variable ID.
1350  *
1351  * @retval 0 success
1352  * @retval EINVAL if @p vid is not a valid variable ID.
1353  */
1354 static int
1355 sprom_opcode_set_var(struct sprom_opcode_state *state, size_t vid)
1356 {
1357 	const struct bhnd_nvram_vardefn	*var;
1358 	int				 error;
1359 
1360 	BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_NONE,
1361 	    ("overwrite of open variable definition"));
1362 
1363 	/* Locate the variable definition */
1364 	if ((var = bhnd_nvram_get_vardefn(vid)) == NULL) {
1365 		SPROM_OP_BAD(state, "unknown variable ID: %zu\n", vid);
1366 		return (EINVAL);
1367 	}
1368 
1369 	/* Update vid and var state */
1370 	state->vid = vid;
1371 	state->var_state = SPROM_OPCODE_VAR_STATE_OPEN;
1372 
1373 	/* Initialize default variable record values */
1374 	memset(&state->var, 0x0, sizeof(state->var));
1375 
1376 	/* Set initial base type */
1377 	if ((error = sprom_opcode_set_type(state, var->type)))
1378 		return (error);
1379 
1380 	/* Set default array length */
1381 	if ((error = sprom_opcode_set_nelem(state, var->nelem)))
1382 		return (error);
1383 
1384 	return (0);
1385 }
1386 
1387 /**
1388  * Mark the currently open variable definition as complete.
1389  *
1390  * @param state The opcode state to update.
1391  *
1392  * @retval 0 success
1393  * @retval EINVAL if no incomplete open variable definition exists.
1394  */
1395 static int
1396 sprom_opcode_end_var(struct sprom_opcode_state *state)
1397 {
1398 	if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
1399 		SPROM_OP_BAD(state, "no open variable definition\n");
1400 		return (EINVAL);
1401 	}
1402 
1403 	state->var_state = SPROM_OPCODE_VAR_STATE_DONE;
1404 	return (0);
1405 }
1406 
1407 /**
1408  * Apply the current scale to @p value.
1409  *
1410  * @param state The SPROM opcode state.
1411  * @param[in,out] value The value to scale
1412  *
1413  * @retval 0 success
1414  * @retval EINVAL if no open variable definition exists.
1415  * @retval EINVAL if applying the current scale would overflow.
1416  */
1417 static int
1418 sprom_opcode_apply_scale(struct sprom_opcode_state *state, uint32_t *value)
1419 {
1420 	/* Must have a defined variable (and thus, scale) */
1421 	if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
1422 		SPROM_OP_BAD(state, "scaled value encoded without open "
1423 		    "variable state");
1424 		return (EINVAL);
1425 	}
1426 
1427 	/* Applying the scale value must not overflow */
1428 	if (UINT32_MAX / state->var.scale < *value) {
1429 		SPROM_OP_BAD(state, "cannot represent %" PRIu32 " * %" PRIu32
1430 		    "\n", *value, state->var.scale);
1431 		return (EINVAL);
1432 	}
1433 
1434 	*value = (*value) * state->var.scale;
1435 	return (0);
1436 }
1437 
1438 /**
1439  * Read a SPROM_OP_DATA_* value from @p opcodes.
1440  *
1441  * @param state The SPROM opcode state.
1442  * @param type The SROM_OP_DATA_* type to be read.
1443  * @param opval On success, the 32bit data representation. If @p type is signed,
1444  * the value will be appropriately sign extended and may be directly cast to
1445  * int32_t.
1446  *
1447  * @retval 0 success
1448  * @retval non-zero If reading the value otherwise fails, a regular unix error
1449  * code will be returned.
1450  */
1451 static int
1452 sprom_opcode_read_opval32(struct sprom_opcode_state *state, uint8_t type,
1453    uint32_t *opval)
1454 {
1455 	const uint8_t	*p;
1456 	int		 error;
1457 
1458 	p = state->input;
1459 	switch (type) {
1460 	case SPROM_OP_DATA_I8:
1461 		/* Convert to signed value first, then sign extend */
1462 		*opval = (int32_t)(int8_t)(*p);
1463 		p += 1;
1464 		break;
1465 	case SPROM_OP_DATA_U8:
1466 		*opval = *p;
1467 		p += 1;
1468 		break;
1469 	case SPROM_OP_DATA_U8_SCALED:
1470 		*opval = *p;
1471 
1472 		if ((error = sprom_opcode_apply_scale(state, opval)))
1473 			return (error);
1474 
1475 		p += 1;
1476 		break;
1477 	case SPROM_OP_DATA_U16:
1478 		*opval = le16dec(p);
1479 		p += 2;
1480 		break;
1481 	case SPROM_OP_DATA_U32:
1482 		*opval = le32dec(p);
1483 		p += 4;
1484 		break;
1485 	default:
1486 		SPROM_OP_BAD(state, "unsupported data type: %hhu\n", type);
1487 		return (EINVAL);
1488 	}
1489 
1490 	/* Update read address */
1491 	state->input = p;
1492 
1493 	return (0);
1494 }
1495 
1496 /**
1497  * Return true if our layout revision is currently defined by the SPROM
1498  * opcode state.
1499  *
1500  * This may be used to test whether the current opcode stream state applies
1501  * to the layout that we are actually parsing.
1502  *
1503  * A given opcode stream may cover multiple layout revisions, switching
1504  * between them prior to defining a set of variables.
1505  */
1506 static inline bool
1507 sprom_opcode_matches_layout_rev(struct sprom_opcode_state *state)
1508 {
1509 	return (bit_test(state->revs, state->layout->rev));
1510 }
1511 
1512 /**
1513  * When evaluating @p state and @p opcode, rewrite @p opcode and the current
1514  * evaluation state, as required.
1515  *
1516  * If @p opcode is rewritten, it should be returned from
1517  * sprom_opcode_step() instead of the opcode parsed from @p state's opcode
1518  * stream.
1519  *
1520  * If @p opcode remains unmodified, then sprom_opcode_step() should proceed
1521  * to standard evaluation.
1522  */
1523 static int
1524 sprom_opcode_rewrite_opcode(struct sprom_opcode_state *state, uint8_t *opcode)
1525 {
1526 	uint8_t	op;
1527 	int	error;
1528 
1529 	op = SPROM_OPCODE_OP(*opcode);
1530 	switch (state->var_state) {
1531 	case SPROM_OPCODE_VAR_STATE_NONE:
1532 		/* No open variable definition */
1533 		return (0);
1534 
1535 	case SPROM_OPCODE_VAR_STATE_OPEN:
1536 		/* Open variable definition; check for implicit closure. */
1537 
1538 		/*
1539 		 * If a variable definition contains no explicit bind
1540 		 * instructions prior to closure, we must generate a DO_BIND
1541 		 * instruction with count and skip values of 1.
1542 		 */
1543 		if (SPROM_OP_IS_VAR_END(op) &&
1544 		    state->var.bind_total == 0)
1545 		{
1546 			uint8_t	count, skip_in, skip_out;
1547 			bool	skip_in_negative;
1548 
1549 			/* Create bind with skip_in/skip_out of 1, count of 1 */
1550 			count = 1;
1551 			skip_in = 1;
1552 			skip_out = 1;
1553 			skip_in_negative = false;
1554 
1555 			error = sprom_opcode_set_bind(state, count, skip_in,
1556 			    skip_in_negative, skip_out);
1557 			if (error)
1558 				return (error);
1559 
1560 			/* Return DO_BIND */
1561 			*opcode = SPROM_OPCODE_DO_BIND |
1562 			    (0 << SPROM_OP_BIND_SKIP_IN_SIGN) |
1563 			    (1 << SPROM_OP_BIND_SKIP_IN_SHIFT) |
1564 			    (1 << SPROM_OP_BIND_SKIP_OUT_SHIFT);
1565 
1566 			return (0);
1567 		}
1568 
1569 		/*
1570 		 * If a variable is implicitly closed (e.g. by a new variable
1571 		 * definition), we must generate a VAR_END instruction.
1572 		 */
1573 		if (SPROM_OP_IS_IMPLICIT_VAR_END(op)) {
1574 			/* Mark as complete */
1575 			if ((error = sprom_opcode_end_var(state)))
1576 				return (error);
1577 
1578 			/* Return VAR_END */
1579 			*opcode = SPROM_OPCODE_VAR_END;
1580 			return (0);
1581 		}
1582 		break;
1583 
1584 
1585 	case SPROM_OPCODE_VAR_STATE_DONE:
1586 		/* Previously completed variable definition. Discard variable
1587 		 * state */
1588 		return (sprom_opcode_clear_var(state));
1589 	}
1590 
1591 	/* Nothing to do */
1592 	return (0);
1593 }
1594 
1595 /**
1596  * Evaluate one opcode from @p state.
1597  *
1598  * @param state The opcode state to be evaluated.
1599  * @param[out] opcode On success, the evaluated opcode
1600  *
1601  * @retval 0 success
1602  * @retval ENOENT if EOF is reached
1603  * @retval non-zero if evaluation otherwise fails, a regular unix error
1604  * code will be returned.
1605  */
1606 static int
1607 sprom_opcode_step(struct sprom_opcode_state *state, uint8_t *opcode)
1608 {
1609 	int error;
1610 
1611 	while (*state->input != SPROM_OPCODE_EOF) {
1612 		uint32_t	val;
1613 		uint8_t		op, rewrite, immd;
1614 
1615 		/* Fetch opcode */
1616 		*opcode = *state->input;
1617 		op = SPROM_OPCODE_OP(*opcode);
1618 		immd = SPROM_OPCODE_IMM(*opcode);
1619 
1620 		/* Clear any existing bind state */
1621 		if ((error = sprom_opcode_flush_bind(state)))
1622 			return (error);
1623 
1624 		/* Insert local opcode based on current state? */
1625 		rewrite = *opcode;
1626 		if ((error = sprom_opcode_rewrite_opcode(state, &rewrite)))
1627 			return (error);
1628 
1629 		if (rewrite != *opcode) {
1630 			/* Provide rewritten opcode */
1631 			*opcode = rewrite;
1632 
1633 			/* We must keep evaluating until we hit a state
1634 			 * applicable to the SPROM revision we're parsing */
1635 			if (!sprom_opcode_matches_layout_rev(state))
1636 				continue;
1637 
1638 			return (0);
1639 		}
1640 
1641 		/* Advance input */
1642 		state->input++;
1643 
1644 		switch (op) {
1645 		case SPROM_OPCODE_VAR_IMM:
1646 			if ((error = sprom_opcode_set_var(state, immd)))
1647 				return (error);
1648 			break;
1649 
1650 		case SPROM_OPCODE_VAR_REL_IMM:
1651 			error = sprom_opcode_set_var(state, state->vid + immd);
1652 			if (error)
1653 				return (error);
1654 			break;
1655 
1656 		case SPROM_OPCODE_VAR:
1657 			error = sprom_opcode_read_opval32(state, immd, &val);
1658 			if (error)
1659 				return (error);
1660 
1661 			if ((error = sprom_opcode_set_var(state, val)))
1662 				return (error);
1663 
1664 			break;
1665 
1666 		case SPROM_OPCODE_VAR_END:
1667 			if ((error = sprom_opcode_end_var(state)))
1668 				return (error);
1669 			break;
1670 
1671 		case SPROM_OPCODE_NELEM:
1672 			immd = *state->input;
1673 			if ((error = sprom_opcode_set_nelem(state, immd)))
1674 				return (error);
1675 
1676 			state->input++;
1677 			break;
1678 
1679 		case SPROM_OPCODE_DO_BIND:
1680 		case SPROM_OPCODE_DO_BINDN: {
1681 			uint8_t	count, skip_in, skip_out;
1682 			bool	skip_in_negative;
1683 
1684 			/* Fetch skip arguments */
1685 			skip_in = (immd & SPROM_OP_BIND_SKIP_IN_MASK) >>
1686 			    SPROM_OP_BIND_SKIP_IN_SHIFT;
1687 
1688 			skip_in_negative =
1689 			    ((immd & SPROM_OP_BIND_SKIP_IN_SIGN) != 0);
1690 
1691 			skip_out = (immd & SPROM_OP_BIND_SKIP_OUT_MASK) >>
1692 			      SPROM_OP_BIND_SKIP_OUT_SHIFT;
1693 
1694 			/* Fetch count argument (if any) */
1695 			if (op == SPROM_OPCODE_DO_BINDN) {
1696 				/* Count is provided as trailing U8 */
1697 				count = *state->input;
1698 				state->input++;
1699 			} else {
1700 				count = 1;
1701 			}
1702 
1703 			/* Set BIND state */
1704 			error = sprom_opcode_set_bind(state, count, skip_in,
1705 			    skip_in_negative, skip_out);
1706 			if (error)
1707 				return (error);
1708 
1709 			break;
1710 		}
1711 		case SPROM_OPCODE_DO_BINDN_IMM: {
1712 			uint8_t	count, skip_in, skip_out;
1713 			bool	skip_in_negative;
1714 
1715 			/* Implicit skip_in/skip_out of 1, count encoded as immd
1716 			 * value */
1717 			count = immd;
1718 			skip_in = 1;
1719 			skip_out = 1;
1720 			skip_in_negative = false;
1721 
1722 			error = sprom_opcode_set_bind(state, count, skip_in,
1723 			    skip_in_negative, skip_out);
1724 			if (error)
1725 				return (error);
1726 			break;
1727 		}
1728 
1729 		case SPROM_OPCODE_REV_IMM:
1730 			if ((error = sprom_opcode_set_revs(state, immd, immd)))
1731 				return (error);
1732 			break;
1733 
1734 		case SPROM_OPCODE_REV_RANGE: {
1735 			uint8_t range;
1736 			uint8_t rstart, rend;
1737 
1738 			/* Revision range is encoded in next byte, as
1739 			 * { uint8_t start:4, uint8_t end:4 } */
1740 			range = *state->input;
1741 			rstart = (range & SPROM_OP_REV_START_MASK) >>
1742 			    SPROM_OP_REV_START_SHIFT;
1743 			rend = (range & SPROM_OP_REV_END_MASK) >>
1744 			    SPROM_OP_REV_END_SHIFT;
1745 
1746 			/* Update revision bitmask */
1747 			error = sprom_opcode_set_revs(state, rstart, rend);
1748 			if (error)
1749 				return (error);
1750 
1751 			/* Advance input */
1752 			state->input++;
1753 			break;
1754 		}
1755 		case SPROM_OPCODE_MASK_IMM:
1756 			if ((error = sprom_opcode_set_mask(state, immd)))
1757 				return (error);
1758 			break;
1759 
1760 		case SPROM_OPCODE_MASK:
1761 			error = sprom_opcode_read_opval32(state, immd, &val);
1762 			if (error)
1763 				return (error);
1764 
1765 			if ((error = sprom_opcode_set_mask(state, val)))
1766 				return (error);
1767 			break;
1768 
1769 		case SPROM_OPCODE_SHIFT_IMM:
1770 			if ((error = sprom_opcode_set_shift(state, immd * 2)))
1771 				return (error);
1772 			break;
1773 
1774 		case SPROM_OPCODE_SHIFT: {
1775 			int8_t shift;
1776 
1777 			if (immd == SPROM_OP_DATA_I8) {
1778 				shift = (int8_t)(*state->input);
1779 			} else if (immd == SPROM_OP_DATA_U8) {
1780 				val = *state->input;
1781 				if (val > INT8_MAX) {
1782 					SPROM_OP_BAD(state, "invalid shift "
1783 					    "value: %#x\n", val);
1784 				}
1785 
1786 				shift = val;
1787 			} else {
1788 				SPROM_OP_BAD(state, "unsupported shift data "
1789 				    "type: %#hhx\n", immd);
1790 				return (EINVAL);
1791 			}
1792 
1793 			if ((error = sprom_opcode_set_shift(state, shift)))
1794 				return (error);
1795 
1796 			state->input++;
1797 			break;
1798 		}
1799 		case SPROM_OPCODE_OFFSET_REL_IMM:
1800 			/* Fetch unscaled relative offset */
1801 			val = immd;
1802 
1803 			/* Apply scale */
1804 			if ((error = sprom_opcode_apply_scale(state, &val)))
1805 				return (error);
1806 
1807 			/* Adding val must not overflow our offset */
1808 			if (UINT32_MAX - state->offset < val) {
1809 				BHND_NV_LOG("offset out of range\n");
1810 				return (EINVAL);
1811 			}
1812 
1813 			/* Adjust offset */
1814 			state->offset += val;
1815 			break;
1816 		case SPROM_OPCODE_OFFSET:
1817 			error = sprom_opcode_read_opval32(state, immd, &val);
1818 			if (error)
1819 				return (error);
1820 
1821 			state->offset = val;
1822 			break;
1823 
1824 		case SPROM_OPCODE_TYPE:
1825 			/* Type follows as U8 */
1826 			immd = *state->input;
1827 			state->input++;
1828 
1829 			/* fall through */
1830 		case SPROM_OPCODE_TYPE_IMM:
1831 			switch (immd) {
1832 			case BHND_NVRAM_TYPE_UINT8:
1833 			case BHND_NVRAM_TYPE_UINT16:
1834 			case BHND_NVRAM_TYPE_UINT32:
1835 			case BHND_NVRAM_TYPE_UINT64:
1836 			case BHND_NVRAM_TYPE_INT8:
1837 			case BHND_NVRAM_TYPE_INT16:
1838 			case BHND_NVRAM_TYPE_INT32:
1839 			case BHND_NVRAM_TYPE_INT64:
1840 			case BHND_NVRAM_TYPE_CHAR:
1841 			case BHND_NVRAM_TYPE_STRING:
1842 				error = sprom_opcode_set_type(state,
1843 				    (bhnd_nvram_type)immd);
1844 				if (error)
1845 					return (error);
1846 				break;
1847 			default:
1848 				BHND_NV_LOG("unrecognized type %#hhx\n", immd);
1849 				return (EINVAL);
1850 			}
1851 			break;
1852 
1853 		default:
1854 			BHND_NV_LOG("unrecognized opcode %#hhx\n", *opcode);
1855 			return (EINVAL);
1856 		}
1857 
1858 		/* We must keep evaluating until we hit a state applicable to
1859 		 * the SPROM revision we're parsing */
1860 		if (sprom_opcode_matches_layout_rev(state))
1861 			return (0);
1862 	}
1863 
1864 	/* End of opcode stream */
1865 	return (ENOENT);
1866 }
1867 
1868 /**
1869  * Reset SPROM opcode evaluation state, seek to the @p indexed position,
1870  * and perform complete evaluation of the variable's opcodes.
1871  *
1872  * @param state The opcode state to be to be evaluated.
1873  * @param indexed The indexed variable location.
1874  *
1875  * @retval 0 success
1876  * @retval non-zero If evaluation fails, a regular unix error code will be
1877  * returned.
1878  */
1879 static int
1880 sprom_opcode_parse_var(struct sprom_opcode_state *state,
1881     struct sprom_opcode_idx *indexed)
1882 {
1883 	uint8_t	opcode;
1884 	int	error;
1885 
1886 	/* Seek to entry */
1887 	if ((error = sprom_opcode_state_seek(state, indexed)))
1888 		return (error);
1889 
1890 	/* Parse full variable definition */
1891 	while ((error = sprom_opcode_step(state, &opcode)) == 0) {
1892 		/* Iterate until VAR_END */
1893 		if (SPROM_OPCODE_OP(opcode) != SPROM_OPCODE_VAR_END)
1894 			continue;
1895 
1896 		BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
1897 		    ("incomplete variable definition"));
1898 
1899 		return (0);
1900 	}
1901 
1902 	/* Error parsing definition */
1903 	return (error);
1904 }
1905 
1906 /**
1907  * Evaluate @p state until the next variable definition is found.
1908  *
1909  * @param state The opcode state to be evaluated.
1910  *
1911  * @retval 0 success
1912  * @retval ENOENT if no additional variable definitions are available.
1913  * @retval non-zero if evaluation otherwise fails, a regular unix error
1914  * code will be returned.
1915  */
1916 static int
1917 sprom_opcode_next_var(struct sprom_opcode_state *state)
1918 {
1919 	uint8_t	opcode;
1920 	int	error;
1921 
1922 	/* Step until we hit a variable opcode */
1923 	while ((error = sprom_opcode_step(state, &opcode)) == 0) {
1924 		switch (SPROM_OPCODE_OP(opcode)) {
1925 		case SPROM_OPCODE_VAR:
1926 		case SPROM_OPCODE_VAR_IMM:
1927 		case SPROM_OPCODE_VAR_REL_IMM:
1928 			BHND_NV_ASSERT(
1929 			    state->var_state == SPROM_OPCODE_VAR_STATE_OPEN,
1930 			    ("missing variable definition"));
1931 
1932 			return (0);
1933 		default:
1934 			continue;
1935 		}
1936 	}
1937 
1938 	/* Reached EOF, or evaluation failed */
1939 	return (error);
1940 }
1941 
1942 /**
1943  * Evaluate @p state until the next binding for the current variable definition
1944  * is found.
1945  *
1946  * @param state The opcode state to be evaluated.
1947  *
1948  * @retval 0 success
1949  * @retval ENOENT if no additional binding opcodes are found prior to reaching
1950  * a new variable definition, or the end of @p state's binding opcodes.
1951  * @retval non-zero if evaluation otherwise fails, a regular unix error
1952  * code will be returned.
1953  */
1954 static int
1955 sprom_opcode_next_binding(struct sprom_opcode_state *state)
1956 {
1957 	uint8_t	opcode;
1958 	int	error;
1959 
1960 	if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN)
1961 		return (EINVAL);
1962 
1963 	/* Step until we hit a bind opcode, or a new variable */
1964 	while ((error = sprom_opcode_step(state, &opcode)) == 0) {
1965 		switch (SPROM_OPCODE_OP(opcode)) {
1966 		case SPROM_OPCODE_DO_BIND:
1967 		case SPROM_OPCODE_DO_BINDN:
1968 		case SPROM_OPCODE_DO_BINDN_IMM:
1969 			/* Found next bind */
1970 			BHND_NV_ASSERT(
1971 			    state->var_state == SPROM_OPCODE_VAR_STATE_OPEN,
1972 			    ("missing variable definition"));
1973 			BHND_NV_ASSERT(state->var.have_bind, ("missing bind"));
1974 
1975 			return (0);
1976 
1977 		case SPROM_OPCODE_VAR_END:
1978 			/* No further binding opcodes */
1979 			BHND_NV_ASSERT(
1980 			    state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
1981 			    ("variable definition still available"));
1982 			return (ENOENT);
1983 		}
1984 	}
1985 
1986 	/* Not found, or evaluation failed */
1987 	return (error);
1988 }
1989