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_map.h"
53
54#include "bhnd_nvram_private.h"
55#include "bhnd_nvram_datavar.h"
56
57#include "bhnd_nvram_data_spromvar.h"
58
59/*
60 * BHND SPROM NVRAM data class
61 *
62 * The SPROM data format is a fixed-layout, non-self-descriptive binary format,
63 * used on Broadcom wireless and wired adapters, that provides a subset of the
64 * variables defined by Broadcom SoC NVRAM formats.
65 */
66
67static const bhnd_sprom_layout  *bhnd_nvram_sprom_get_layout(uint8_t sromrev);
68
69static int			 bhnd_nvram_sprom_ident(
70				     struct bhnd_nvram_io *io,
71				     const bhnd_sprom_layout **ident);
72
73static int			 bhnd_nvram_sprom_write_var(
74				     bhnd_sprom_opcode_state *state,
75				     bhnd_sprom_opcode_idx_entry *entry,
76				     bhnd_nvram_val *value,
77				     struct bhnd_nvram_io *io);
78
79static int			 bhnd_nvram_sprom_read_var(
80				     struct bhnd_sprom_opcode_state *state,
81				     struct bhnd_sprom_opcode_idx_entry *entry,
82				     struct bhnd_nvram_io *io,
83				     union bhnd_nvram_sprom_storage *storage,
84				     bhnd_nvram_val *val);
85
86static int			 bhnd_nvram_sprom_write_offset(
87				     const struct bhnd_nvram_vardefn *var,
88				     struct bhnd_nvram_io *data,
89				     bhnd_nvram_type type, size_t offset,
90				     uint32_t mask, int8_t shift,
91				     uint32_t value);
92
93static int			 bhnd_nvram_sprom_read_offset(
94				     const struct bhnd_nvram_vardefn *var,
95				     struct bhnd_nvram_io *data,
96				     bhnd_nvram_type type, size_t offset,
97				     uint32_t mask, int8_t shift,
98				     uint32_t *value);
99
100static bool			 bhnd_sprom_is_external_immutable(
101				     const char *name);
102
103BHND_NVRAM_DATA_CLASS_DEFN(sprom, "Broadcom SPROM",
104    BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_sprom))
105
106#define	SPROM_COOKIE_TO_VID(_cookie)	\
107	(((struct bhnd_sprom_opcode_idx_entry *)(_cookie))->vid)
108
109#define	SPROM_COOKIE_TO_NVRAM_VAR(_cookie)	\
110	bhnd_nvram_get_vardefn(SPROM_COOKIE_TO_VID(_cookie))
111
112/**
113 * Read the magic value from @p io, and verify that it matches
114 * the @p layout's expected magic value.
115 *
116 * If @p layout does not defined a magic value, @p magic is set to 0x0
117 * and success is returned.
118 *
119 * @param	io	An I/O context mapping the SPROM data to be identified.
120 * @param	layout	The SPROM layout against which @p io should be verified.
121 * @param[out]	magic	On success, the SPROM magic value.
122 *
123 * @retval 0		success
124 * @retval non-zero	If checking @p io otherwise fails, a regular unix
125 *			error code will be returned.
126 */
127static int
128bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io *io,
129    const bhnd_sprom_layout *layout, uint16_t *magic)
130{
131	int error;
132
133	/* Skip if layout does not define a magic value */
134	if (layout->flags & SPROM_LAYOUT_MAGIC_NONE)
135		return (0);
136
137	/* Read the magic value */
138	error = bhnd_nvram_io_read(io, layout->magic_offset, magic,
139	    sizeof(*magic));
140	if (error)
141		return (error);
142
143	*magic = le16toh(*magic);
144
145	/* If the signature does not match, skip to next layout */
146	if (*magic != layout->magic_value)
147		return (ENXIO);
148
149	return (0);
150}
151
152/**
153 * Attempt to identify the format of the SPROM data mapped by @p io.
154 *
155 * The SPROM data format does not provide any identifying information at a
156 * known offset, instead requiring that we iterate over the known SPROM image
157 * sizes until we are able to compute a valid checksum (and, for later
158 * revisions, validate a signature at a revision-specific offset).
159 *
160 * @param	io	An I/O context mapping the SPROM data to be identified.
161 * @param[out]	ident	On success, the identified SPROM layout.
162 *
163 * @retval 0		success
164 * @retval non-zero	If identifying @p io otherwise fails, a regular unix
165 *			error code will be returned.
166 */
167static int
168bhnd_nvram_sprom_ident(struct bhnd_nvram_io *io,
169    const bhnd_sprom_layout **ident)
170{
171	uint8_t	crc;
172	size_t	crc_errors;
173	size_t	nbytes;
174	int	error;
175
176	crc = BHND_NVRAM_CRC8_INITIAL;
177	crc_errors = 0;
178	nbytes = 0;
179
180	/* We iterate the SPROM layouts smallest to largest, allowing us to
181	 * perform incremental checksum calculation */
182	for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
183		const bhnd_sprom_layout	*layout;
184		u_char			 buf[512];
185		size_t			 nread;
186		uint16_t		 magic;
187		uint8_t			 srevcrc[2];
188		uint8_t			 srev;
189		bool			 crc_valid;
190		bool			 have_magic;
191
192		layout = &bhnd_sprom_layouts[i];
193		crc_valid = true;
194
195		have_magic = true;
196		if ((layout->flags & SPROM_LAYOUT_MAGIC_NONE))
197			have_magic = false;
198
199		/*
200		 * Read image data and update CRC (errors are reported
201		 * after the signature check)
202		 *
203		 * Layout instances must be ordered from smallest to largest by
204		 * the nvram_map compiler, allowing us to incrementally update
205		 * our CRC.
206		 */
207		if (nbytes > layout->size)
208			BHND_NV_PANIC("SPROM layout defined out-of-order");
209
210		nread = layout->size - nbytes;
211
212		while (nread > 0) {
213			size_t nr;
214
215			nr = bhnd_nv_ummin(nread, sizeof(buf));
216
217			if ((error = bhnd_nvram_io_read(io, nbytes, buf, nr)))
218				return (error);
219
220			crc = bhnd_nvram_crc8(buf, nr, crc);
221			crc_valid = (crc == BHND_NVRAM_CRC8_VALID);
222			if (!crc_valid)
223				crc_errors++;
224
225			nread -= nr;
226			nbytes += nr;
227		}
228
229		/* Read 8-bit SPROM revision, maintaining 16-bit size alignment
230		 * required by some OTP/SPROM chipsets. */
231		error = bhnd_nvram_io_read(io, layout->srev_offset, &srevcrc,
232		    sizeof(srevcrc));
233		if (error)
234			return (error);
235
236		srev = srevcrc[0];
237
238		/* Early sromrev 1 devices (specifically some BCM440x enet
239		 * cards) are reported to have been incorrectly programmed
240		 * with a revision of 0x10. */
241		if (layout->rev == 1 && srev == 0x10)
242			srev = 0x1;
243
244		/* Check revision against the layout definition */
245		if (srev != layout->rev)
246			continue;
247
248		/* Check the magic value, skipping to the next layout on
249		 * failure. */
250		error = bhnd_nvram_sprom_check_magic(io, layout, &magic);
251		if (error) {
252			/* If the CRC is was valid, log the mismatch */
253			if (crc_valid || BHND_NV_VERBOSE) {
254				BHND_NV_LOG("invalid sprom %hhu signature: "
255					    "0x%hx (expected 0x%hx)\n", srev,
256					    magic, layout->magic_value);
257
258					return (ENXIO);
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		*ident = layout;
279		return (0);
280	}
281
282	/* No match */
283	if (crc_errors > 0 && BHND_NV_VERBOSE) {
284		BHND_NV_LOG("sprom parsing failed with %zu CRC errors\n",
285		    crc_errors);
286	}
287
288	return (ENXIO);
289}
290
291static int
292bhnd_nvram_sprom_probe(struct bhnd_nvram_io *io)
293{
294	const bhnd_sprom_layout	*layout;
295	int			 error;
296
297	/* Try to parse the input */
298	if ((error = bhnd_nvram_sprom_ident(io, &layout)))
299		return (error);
300
301	return (BHND_NVRAM_DATA_PROBE_DEFAULT);
302}
303
304static int
305bhnd_nvram_sprom_getvar_direct(struct bhnd_nvram_io *io, const char *name,
306    void *buf, size_t *len, bhnd_nvram_type type)
307{
308	const bhnd_sprom_layout		*layout;
309	bhnd_sprom_opcode_state		 state;
310	const struct bhnd_nvram_vardefn	*var;
311	size_t				 vid;
312	int				 error;
313
314	/* Look up the variable definition and ID */
315	if ((var = bhnd_nvram_find_vardefn(name)) == NULL)
316		return (ENOENT);
317
318	vid = bhnd_nvram_get_vardefn_id(var);
319
320	/* Identify the SPROM image layout */
321	if ((error = bhnd_nvram_sprom_ident(io, &layout)))
322		return (error);
323
324	/* Initialize SPROM layout interpreter */
325	if ((error = bhnd_sprom_opcode_init(&state, layout))) {
326		BHND_NV_LOG("error initializing opcode state: %d\n", error);
327		return (ENXIO);
328	}
329
330	/* Find SPROM layout entry for the requested variable */
331	while ((error = bhnd_sprom_opcode_next_var(&state)) == 0) {
332		bhnd_sprom_opcode_idx_entry	entry;
333		union bhnd_nvram_sprom_storage	storage;
334		bhnd_nvram_val			val;
335
336		/* Fetch the variable's entry state */
337		if ((error = bhnd_sprom_opcode_init_entry(&state, &entry)))
338			return (error);
339
340		/* Match against expected VID */
341		if (entry.vid != vid)
342			continue;
343
344		/* Decode variable to a new value instance */
345		error = bhnd_nvram_sprom_read_var(&state, &entry, io, &storage,
346		    &val);
347		if (error)
348			return (error);
349
350		/* Perform value coercion */
351		error = bhnd_nvram_val_encode(&val, buf, len, type);
352
353		/* Clean up */
354		bhnd_nvram_val_release(&val);
355		return (error);
356	}
357
358	/* Hit EOF without matching the requested variable? */
359	if (error == ENOENT)
360		return (ENOENT);
361
362	/* Some other parse error occured */
363	return (error);
364}
365
366/**
367 * Return the SPROM layout definition for the given @p sromrev, or NULL if
368 * not found.
369 */
370static const bhnd_sprom_layout *
371bhnd_nvram_sprom_get_layout(uint8_t sromrev)
372{
373	/* Find matching SPROM layout definition */
374	for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
375		if (bhnd_sprom_layouts[i].rev == sromrev)
376			return (&bhnd_sprom_layouts[i]);
377	}
378
379	/* Not found */
380	return (NULL);
381}
382
383/**
384 * Serialize a SPROM variable.
385 *
386 * @param state	The SPROM opcode state describing the layout of @p io.
387 * @param entry	The variable's SPROM opcode index entry.
388 * @param value	The value to encode to @p io as per @p entry.
389 * @param io	I/O context to which @p value should be written, or NULL
390 *		if no output should be produced. This may be used to validate
391 *		values prior to write.
392 *
393 * @retval 0		success
394 * @retval EFTYPE	If value coercion from @p value to the type required by
395 *			@p entry is unsupported.
396 * @retval ERANGE	If value coercion from @p value would overflow
397 *			(or underflow) the type required by @p entry.
398 * @retval non-zero	If serialization otherwise fails, a regular unix error
399 *			code will be returned.
400 */
401static int
402bhnd_nvram_sprom_write_var(bhnd_sprom_opcode_state *state,
403    bhnd_sprom_opcode_idx_entry *entry, bhnd_nvram_val *value,
404    struct bhnd_nvram_io *io)
405{
406	const struct bhnd_nvram_vardefn	*var;
407	uint32_t			 u32[BHND_SPROM_ARRAY_MAXLEN];
408	bhnd_nvram_type			 itype, var_base_type;
409	size_t				 ipos, ilen, nelem;
410	int				 error;
411
412	/* Fetch variable definition and the native element type */
413	var = bhnd_nvram_get_vardefn(entry->vid);
414	BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
415
416	var_base_type = bhnd_nvram_base_type(var->type);
417
418	/* Fetch the element count from the SPROM variable layout definition */
419	if ((error = bhnd_sprom_opcode_eval_var(state, entry)))
420		return (error);
421
422	nelem = state->var.nelem;
423	BHND_NV_ASSERT(nelem <= var->nelem, ("SPROM nelem=%zu exceeds maximum "
424	     "NVRAM nelem=%hhu", nelem, var->nelem));
425
426	/* Promote the data to a common 32-bit representation */
427	if (bhnd_nvram_is_signed_type(var_base_type))
428		itype = BHND_NVRAM_TYPE_INT32_ARRAY;
429	else
430		itype = BHND_NVRAM_TYPE_UINT32_ARRAY;
431
432	/* Calculate total size of the 32-bit promoted representation */
433	if ((ilen = bhnd_nvram_value_size(NULL, 0, itype, nelem)) == 0) {
434		/* Variable-width types are unsupported */
435		BHND_NV_LOG("invalid %s SPROM variable type %d\n",
436			    var->name, var->type);
437		return (EFTYPE);
438	}
439
440	/* The native representation must fit within our scratch array */
441	if (ilen > sizeof(u32)) {
442		BHND_NV_LOG("error encoding '%s', SPROM_ARRAY_MAXLEN "
443			    "incorrect\n", var->name);
444		return (EFTYPE);
445	}
446
447	/* Initialize our common 32-bit value representation */
448	if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) {
449		/* No value provided; can this variable be encoded as missing
450		 * by setting all bits to one? */
451		if (!(var->flags & BHND_NVRAM_VF_IGNALL1)) {
452			BHND_NV_LOG("missing required property: %s\n",
453			    var->name);
454			return (EINVAL);
455		}
456
457		/* Set all bits */
458		memset(u32, 0xFF, ilen);
459	} else {
460		bhnd_nvram_val	 bcm_val;
461		const void	*var_ptr;
462		bhnd_nvram_type	 var_type, raw_type;
463		size_t		 var_len, enc_nelem;
464
465		/* Try to coerce the value to the native variable format. */
466		error = bhnd_nvram_val_convert_init(&bcm_val, var->fmt, value,
467		    BHND_NVRAM_VAL_DYNAMIC|BHND_NVRAM_VAL_BORROW_DATA);
468		if (error) {
469			BHND_NV_LOG("error converting input type %s to %s "
470			    "format\n",
471			    bhnd_nvram_type_name(bhnd_nvram_val_type(value)),
472			    bhnd_nvram_val_fmt_name(var->fmt));
473			return (error);
474		}
475
476		var_ptr = bhnd_nvram_val_bytes(&bcm_val, &var_len, &var_type);
477
478		/*
479		 * Promote to a common 32-bit representation.
480		 *
481		 * We must use the raw type to interpret the input data as its
482		 * underlying integer representation -- otherwise, coercion
483		 * would attempt to parse the input as its complex
484		 * representation.
485		 *
486		 * For example, direct CHAR -> UINT32 coercion would attempt to
487		 * parse the character as a decimal integer, rather than
488		 * promoting the raw UTF8 byte value to a 32-bit value.
489		 */
490		raw_type = bhnd_nvram_raw_type(var_type);
491		error = bhnd_nvram_value_coerce(var_ptr, var_len, raw_type,
492		     u32, &ilen, itype);
493
494		/* Clean up temporary value representation */
495		bhnd_nvram_val_release(&bcm_val);
496
497		/* Report coercion failure */
498		if (error) {
499			BHND_NV_LOG("error promoting %s to %s: %d\n",
500			    bhnd_nvram_type_name(var_type),
501			    bhnd_nvram_type_name(itype), error);
502			return (error);
503		}
504
505		/* Encoded element count must match SPROM's definition */
506		error = bhnd_nvram_value_nelem(u32, ilen, itype, &enc_nelem);
507		if (error)
508			return (error);
509
510		if (enc_nelem != nelem) {
511			const char *type_name;
512
513			type_name = bhnd_nvram_type_name(var_base_type);
514			BHND_NV_LOG("invalid %s property value '%s[%zu]': "
515			    "required %s[%zu]", var->name, type_name,
516			    enc_nelem, type_name, nelem);
517			return (EFTYPE);
518		}
519	}
520
521	/*
522	 * Seek to the start of the variable's SPROM layout definition and
523	 * iterate over all bindings.
524	 */
525	if ((error = bhnd_sprom_opcode_seek(state, entry))) {
526		BHND_NV_LOG("variable seek failed: %d\n", error);
527		return (error);
528	}
529
530	ipos = 0;
531	while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) {
532		bhnd_sprom_opcode_bind	*binding;
533		bhnd_sprom_opcode_var	*binding_var;
534		size_t			 offset;
535		uint32_t		 skip_out_bytes;
536
537		BHND_NV_ASSERT(
538		    state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
539		    ("invalid var state"));
540		BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state"));
541
542		binding_var = &state->var;
543		binding = &state->var.bind;
544
545		/* Calculate output skip bytes for this binding.
546		 *
547		 * Skip directions are defined in terms of decoding, and
548		 * reversed when encoding. */
549		skip_out_bytes = binding->skip_in;
550		error = bhnd_sprom_opcode_apply_scale(state, &skip_out_bytes);
551		if (error)
552			return (error);
553
554		/* Bind */
555		offset = state->offset;
556		for (size_t i = 0; i < binding->count; i++) {
557			if (ipos >= nelem) {
558				BHND_NV_LOG("input skip %u positioned %zu "
559				    "beyond nelem %zu\n", binding->skip_out,
560				    ipos, nelem);
561				return (EINVAL);
562			}
563
564			/* Write next offset */
565			if (io != NULL) {
566				error = bhnd_nvram_sprom_write_offset(var, io,
567				    binding_var->base_type,
568				    offset,
569				    binding_var->mask,
570				    binding_var->shift,
571				    u32[ipos]);
572				if (error)
573					return (error);
574			}
575
576			/* Adjust output position; this was already verified to
577			 * not overflow/underflow during SPROM opcode
578			 * evaluation */
579			if (binding->skip_in_negative) {
580				offset -= skip_out_bytes;
581			} else {
582				offset += skip_out_bytes;
583			}
584
585			/* Skip advancing input if additional bindings are
586			 * required to fully encode intv */
587			if (binding->skip_out == 0)
588				continue;
589
590			/* Advance input position */
591			if (SIZE_MAX - binding->skip_out < ipos) {
592				BHND_NV_LOG("output skip %u would overflow "
593				    "%zu\n", binding->skip_out, ipos);
594				return (EINVAL);
595			}
596
597			ipos += binding->skip_out;
598		}
599	}
600
601	/* Did we iterate all bindings until hitting end of the variable
602	 * definition? */
603	BHND_NV_ASSERT(error != 0, ("loop terminated early"));
604	if (error != ENOENT)
605		return (error);
606
607	return (0);
608}
609
610static int
611bhnd_nvram_sprom_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
612    bhnd_nvram_plist *options, void *outp, size_t *olen)
613{
614	bhnd_sprom_opcode_state		 state;
615	struct bhnd_nvram_io		*io;
616	bhnd_nvram_prop			*prop;
617	bhnd_sprom_opcode_idx_entry	*entry;
618	const bhnd_sprom_layout		*layout;
619	size_t				 limit;
620	uint8_t				 crc;
621	uint8_t				 sromrev;
622	int				 error;
623
624	limit = *olen;
625	layout = NULL;
626	io = NULL;
627
628	/* Fetch sromrev property */
629	if (!bhnd_nvram_plist_contains(props, BHND_NVAR_SROMREV)) {
630		BHND_NV_LOG("missing required property: %s\n",
631		    BHND_NVAR_SROMREV);
632		return (EINVAL);
633	}
634
635	error = bhnd_nvram_plist_get_uint8(props, BHND_NVAR_SROMREV, &sromrev);
636	if (error) {
637		BHND_NV_LOG("error reading sromrev property: %d\n", error);
638		return (EFTYPE);
639	}
640
641	/* Find SPROM layout definition */
642	if ((layout = bhnd_nvram_sprom_get_layout(sromrev)) == NULL) {
643		BHND_NV_LOG("unsupported sromrev: %hhu\n", sromrev);
644		return (EFTYPE);
645	}
646
647	/* Provide required size to caller */
648	*olen = layout->size;
649	if (outp == NULL)
650		return (0);
651	else if (limit < *olen)
652		return (ENOMEM);
653
654	/* Initialize SPROM layout interpreter */
655	if ((error = bhnd_sprom_opcode_init(&state, layout))) {
656		BHND_NV_LOG("error initializing opcode state: %d\n", error);
657		return (ENXIO);
658	}
659
660	/* Check for unsupported properties */
661	prop = NULL;
662	while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
663		const char *name;
664
665		/* Fetch the corresponding SPROM layout index entry */
666		name = bhnd_nvram_prop_name(prop);
667		entry = bhnd_sprom_opcode_index_find(&state, name);
668		if (entry == NULL) {
669			BHND_NV_LOG("property '%s' unsupported by sromrev "
670			    "%hhu\n", name, layout->rev);
671			error = EINVAL;
672			goto finished;
673		}
674	}
675
676	/* Zero-initialize output */
677	memset(outp, 0, *olen);
678
679	/* Allocate wrapping I/O context for output buffer */
680	io = bhnd_nvram_ioptr_new(outp, *olen, *olen, BHND_NVRAM_IOPTR_RDWR);
681	if (io == NULL) {
682		error = ENOMEM;
683		goto finished;
684	}
685
686	/*
687	 * Serialize all SPROM variable data.
688	 */
689	entry = NULL;
690	while ((entry = bhnd_sprom_opcode_index_next(&state, entry)) != NULL) {
691		const struct bhnd_nvram_vardefn	*var;
692		bhnd_nvram_val			*val;
693
694		var = bhnd_nvram_get_vardefn(entry->vid);
695		BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
696
697		/* Fetch prop; will be NULL if unavailable */
698		prop = bhnd_nvram_plist_get_prop(props, var->name);
699		if (prop != NULL) {
700			val = bhnd_nvram_prop_val(prop);
701		} else {
702			val = BHND_NVRAM_VAL_NULL;
703		}
704
705		/* Attempt to serialize the property value to the appropriate
706		 * offset within the output buffer */
707		error = bhnd_nvram_sprom_write_var(&state, entry, val, io);
708		if (error) {
709			BHND_NV_LOG("error serializing %s to required type "
710			    "%s: %d\n", var->name,
711			    bhnd_nvram_type_name(var->type), error);
712
713			/* ENOMEM is reserved for signaling that the output
714			 * buffer capacity is insufficient */
715			if (error == ENOMEM)
716				error = EINVAL;
717
718			goto finished;
719		}
720	}
721
722	/*
723	 * Write magic value, if any.
724	 */
725	if (!(layout->flags & SPROM_LAYOUT_MAGIC_NONE)) {
726		uint16_t magic;
727
728		magic = htole16(layout->magic_value);
729		error = bhnd_nvram_io_write(io, layout->magic_offset, &magic,
730		    sizeof(magic));
731		if (error) {
732			BHND_NV_LOG("error writing magic value: %d\n", error);
733			goto finished;
734		}
735	}
736
737	/* Calculate the CRC over all SPROM data, not including the CRC byte. */
738	crc = ~bhnd_nvram_crc8(outp, layout->crc_offset,
739	    BHND_NVRAM_CRC8_INITIAL);
740
741	/* Write the checksum. */
742	error = bhnd_nvram_io_write(io, layout->crc_offset, &crc, sizeof(crc));
743	if (error) {
744		BHND_NV_LOG("error writing CRC value: %d\n", error);
745		goto finished;
746	}
747
748	/*
749	 * Success!
750	 */
751	error = 0;
752
753finished:
754	bhnd_sprom_opcode_fini(&state);
755
756	if (io != NULL)
757		bhnd_nvram_io_free(io);
758
759	return (error);
760}
761
762static int
763bhnd_nvram_sprom_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
764{
765	struct bhnd_nvram_sprom	*sp;
766	int			 error;
767
768	sp = (struct bhnd_nvram_sprom *)nv;
769
770	/* Identify the SPROM input data */
771	if ((error = bhnd_nvram_sprom_ident(io, &sp->layout)))
772		return (error);
773
774	/* Copy SPROM image to our shadow buffer */
775	sp->data = bhnd_nvram_iobuf_copy_range(io, 0, sp->layout->size);
776	if (sp->data == NULL)
777		goto failed;
778
779	/* Initialize SPROM binding eval state */
780	if ((error = bhnd_sprom_opcode_init(&sp->state, sp->layout)))
781		goto failed;
782
783	return (0);
784
785failed:
786	if (sp->data != NULL)
787		bhnd_nvram_io_free(sp->data);
788
789	return (error);
790}
791
792static void
793bhnd_nvram_sprom_free(struct bhnd_nvram_data *nv)
794{
795	struct bhnd_nvram_sprom *sp = (struct bhnd_nvram_sprom *)nv;
796
797	bhnd_sprom_opcode_fini(&sp->state);
798	bhnd_nvram_io_free(sp->data);
799}
800
801size_t
802bhnd_nvram_sprom_count(struct bhnd_nvram_data *nv)
803{
804	struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv;
805	return (sprom->layout->num_vars);
806}
807
808static bhnd_nvram_plist *
809bhnd_nvram_sprom_options(struct bhnd_nvram_data *nv)
810{
811	return (NULL);
812}
813
814static uint32_t
815bhnd_nvram_sprom_caps(struct bhnd_nvram_data *nv)
816{
817	return (BHND_NVRAM_DATA_CAP_INDEXED);
818}
819
820static const char *
821bhnd_nvram_sprom_next(struct bhnd_nvram_data *nv, void **cookiep)
822{
823	struct bhnd_nvram_sprom		*sp;
824	bhnd_sprom_opcode_idx_entry	*entry;
825	const struct bhnd_nvram_vardefn	*var;
826
827	sp = (struct bhnd_nvram_sprom *)nv;
828
829	/* Find next index entry that is not disabled by virtue of IGNALL1 */
830	entry = *cookiep;
831	while ((entry = bhnd_sprom_opcode_index_next(&sp->state, entry))) {
832		/* Update cookiep and fetch variable definition */
833		*cookiep = entry;
834		var = SPROM_COOKIE_TO_NVRAM_VAR(*cookiep);
835		BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
836
837		/* We might need to parse the variable's value to determine
838		 * whether it should be treated as unset */
839		if (var->flags & BHND_NVRAM_VF_IGNALL1) {
840			int     error;
841			size_t  len;
842
843			error = bhnd_nvram_sprom_getvar(nv, *cookiep, NULL,
844			    &len, var->type);
845			if (error) {
846				BHND_NV_ASSERT(error == ENOENT, ("unexpected "
847				    "error parsing variable: %d", error));
848				continue;
849			}
850		}
851
852		/* Found! */
853		return (var->name);
854	}
855
856	/* Reached end of index entries */
857	return (NULL);
858}
859
860static void *
861bhnd_nvram_sprom_find(struct bhnd_nvram_data *nv, const char *name)
862{
863	struct bhnd_nvram_sprom		*sp;
864	bhnd_sprom_opcode_idx_entry	*entry;
865
866	sp = (struct bhnd_nvram_sprom *)nv;
867
868	entry = bhnd_sprom_opcode_index_find(&sp->state, name);
869	return (entry);
870}
871
872/**
873 * Write @p value of @p type to the SPROM @p data at @p offset, applying
874 * @p mask and @p shift, and OR with the existing data.
875 *
876 * @param var The NVRAM variable definition.
877 * @param data The SPROM data to be modified.
878 * @param type The type to write at @p offset.
879 * @param offset The data offset to be written.
880 * @param mask The mask to be applied to @p value after shifting.
881 * @param shift The shift to be applied to @p value; if positive, a left
882 * shift will be applied, if negative, a right shift (this is the reverse of the
883 * decoding behavior)
884 * @param value The value to be written. The parsed value will be OR'd with the
885 * current contents of @p data at @p offset.
886 */
887static int
888bhnd_nvram_sprom_write_offset(const struct bhnd_nvram_vardefn *var,
889    struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset,
890    uint32_t mask, int8_t shift, uint32_t value)
891{
892	union bhnd_nvram_sprom_storage	scratch;
893	int				error;
894
895#define	NV_WRITE_INT(_widen, _repr, _swap)	do {		\
896	/* Narrow the 32-bit representation */			\
897	scratch._repr[1] = (_widen)value;			\
898								\
899	/* Shift and mask the new value */			\
900	if (shift > 0)						\
901		scratch._repr[1] <<= shift;			\
902	else if (shift < 0)					\
903		scratch._repr[1] >>= -shift;			\
904	scratch._repr[1] &= mask;				\
905								\
906	/* Swap to output byte order */				\
907	scratch._repr[1] = _swap(scratch._repr[1]);		\
908								\
909	/* Fetch the current value */				\
910	error = bhnd_nvram_io_read(data, offset,		\
911	    &scratch._repr[0], sizeof(scratch._repr[0]));	\
912	if (error) {						\
913		BHND_NV_LOG("error reading %s SPROM offset "	\
914		    "%#zx: %d\n", var->name, offset, error);	\
915		return (EFTYPE);				\
916	}							\
917								\
918	/* Mask and set our new value's bits in the current	\
919	 * value */						\
920	if (shift >= 0)						\
921		scratch._repr[0] &= ~_swap(mask << shift);	\
922	else if (shift < 0)					\
923		scratch._repr[0] &= ~_swap(mask >> (-shift));	\
924	scratch._repr[0] |= scratch._repr[1];			\
925								\
926	/* Perform write */					\
927	error = bhnd_nvram_io_write(data, offset,		\
928	    &scratch._repr[0], sizeof(scratch._repr[0]));	\
929	if (error) {						\
930		BHND_NV_LOG("error writing %s SPROM offset "	\
931		    "%#zx: %d\n", var->name, offset, error);	\
932		return (EFTYPE);				\
933	}							\
934} while(0)
935
936	/* Apply mask/shift and widen to a common 32bit representation */
937	switch (type) {
938	case BHND_NVRAM_TYPE_UINT8:
939		NV_WRITE_INT(uint32_t,	u8,	);
940		break;
941	case BHND_NVRAM_TYPE_UINT16:
942		NV_WRITE_INT(uint32_t,	u16,	htole16);
943		break;
944	case BHND_NVRAM_TYPE_UINT32:
945		NV_WRITE_INT(uint32_t,	u32,	htole32);
946		break;
947	case BHND_NVRAM_TYPE_INT8:
948		NV_WRITE_INT(int32_t,	i8,	);
949		break;
950	case BHND_NVRAM_TYPE_INT16:
951		NV_WRITE_INT(int32_t,	i16,	htole16);
952		break;
953	case BHND_NVRAM_TYPE_INT32:
954		NV_WRITE_INT(int32_t,	i32,	htole32);
955		break;
956	case BHND_NVRAM_TYPE_CHAR:
957		NV_WRITE_INT(uint32_t,	u8,	);
958		break;
959	default:
960		BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
961		return (EFTYPE);
962	}
963#undef	NV_WRITE_INT
964
965	return (0);
966}
967
968/**
969 * Read the value of @p type from the SPROM @p data at @p offset, apply @p mask
970 * and @p shift, and OR with the existing @p value.
971 *
972 * @param var The NVRAM variable definition.
973 * @param data The SPROM data to be decoded.
974 * @param type The type to read at @p offset
975 * @param offset The data offset to be read.
976 * @param mask The mask to be applied to the value read at @p offset.
977 * @param shift The shift to be applied after masking; if positive, a right
978 * shift will be applied, if negative, a left shift.
979 * @param value The read destination; the parsed value will be OR'd with the
980 * current contents of @p value.
981 */
982static int
983bhnd_nvram_sprom_read_offset(const struct bhnd_nvram_vardefn *var,
984    struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset,
985    uint32_t mask, int8_t shift, uint32_t *value)
986{
987	union bhnd_nvram_sprom_storage	scratch;
988	int				error;
989
990#define	NV_PARSE_INT(_widen, _repr, _swap)		do {	\
991	/* Perform read */					\
992	error = bhnd_nvram_io_read(data, offset,		\
993	    &scratch._repr[0], sizeof(scratch._repr[0]));	\
994	if (error) {						\
995		BHND_NV_LOG("error reading %s SPROM offset "	\
996		    "%#zx: %d\n", var->name, offset, error);	\
997		return (EFTYPE);				\
998	}							\
999								\
1000	/* Swap to host byte order */				\
1001	scratch._repr[0] = _swap(scratch._repr[0]);		\
1002								\
1003	/* Mask and shift the value */				\
1004	scratch._repr[0] &= mask;				\
1005	if (shift > 0) {					\
1006		scratch. _repr[0] >>= shift;			\
1007	} else if (shift < 0) {					\
1008		scratch. _repr[0] <<= -shift;			\
1009	}							\
1010								\
1011	/* Widen to 32-bit representation and OR with current	\
1012	 * value */						\
1013	(*value) |= (_widen)scratch._repr[0];			\
1014} while(0)
1015
1016	/* Apply mask/shift and widen to a common 32bit representation */
1017	switch (type) {
1018	case BHND_NVRAM_TYPE_UINT8:
1019		NV_PARSE_INT(uint32_t,	u8,	);
1020		break;
1021	case BHND_NVRAM_TYPE_UINT16:
1022		NV_PARSE_INT(uint32_t,	u16,	le16toh);
1023		break;
1024	case BHND_NVRAM_TYPE_UINT32:
1025		NV_PARSE_INT(uint32_t,	u32,	le32toh);
1026		break;
1027	case BHND_NVRAM_TYPE_INT8:
1028		NV_PARSE_INT(int32_t,	i8,	);
1029		break;
1030	case BHND_NVRAM_TYPE_INT16:
1031		NV_PARSE_INT(int32_t,	i16,	le16toh);
1032		break;
1033	case BHND_NVRAM_TYPE_INT32:
1034		NV_PARSE_INT(int32_t,	i32,	le32toh);
1035		break;
1036	case BHND_NVRAM_TYPE_CHAR:
1037		NV_PARSE_INT(uint32_t,	u8,	);
1038		break;
1039	default:
1040		BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
1041		return (EFTYPE);
1042	}
1043#undef	NV_PARSE_INT
1044
1045	return (0);
1046}
1047
1048/**
1049 * Read a SPROM variable value from @p io.
1050 *
1051 * @param	state		The SPROM opcode state describing the layout of @p io.
1052 * @param	entry		The variable's SPROM opcode index entry.
1053 * @param	io		The input I/O context.
1054 * @param	storage		Storage to be used with @p val.
1055 * @param[out]	val		Value instance to be initialized with the
1056 *				parsed variable data.
1057 *
1058 * The returned @p val instance will hold a borrowed reference to @p storage,
1059 * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
1060 * the lifetime of @p storage.
1061 *
1062 * The caller is responsible for releasing any allocated value state
1063 * via bhnd_nvram_val_release().
1064 */
1065static int
1066bhnd_nvram_sprom_read_var(struct bhnd_sprom_opcode_state *state,
1067    struct bhnd_sprom_opcode_idx_entry *entry, struct bhnd_nvram_io *io,
1068    union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
1069{
1070	union bhnd_nvram_sprom_storage	*inp;
1071	const struct bhnd_nvram_vardefn	*var;
1072	bhnd_nvram_type			 var_btype;
1073	uint32_t			 intv;
1074	size_t				 ilen, ipos, iwidth;
1075	size_t				 nelem;
1076	bool				 all_bits_set;
1077	int				 error;
1078
1079	/* Fetch canonical variable definition */
1080	var = bhnd_nvram_get_vardefn(entry->vid);
1081	BHND_NV_ASSERT(var != NULL, ("invalid entry"));
1082
1083	/*
1084	 * Fetch the array length from the SPROM variable definition.
1085	 *
1086	 * This generally be identical to the array length provided by the
1087	 * canonical NVRAM variable definition, but some SPROM layouts may
1088	 * define a smaller element count.
1089	 */
1090	if ((error = bhnd_sprom_opcode_eval_var(state, entry))) {
1091		BHND_NV_LOG("variable evaluation failed: %d\n", error);
1092		return (error);
1093	}
1094
1095	nelem = state->var.nelem;
1096	if (nelem > var->nelem) {
1097		BHND_NV_LOG("SPROM array element count %zu cannot be "
1098		    "represented by '%s' element count of %hhu\n", nelem,
1099		    var->name, var->nelem);
1100		return (EFTYPE);
1101	}
1102
1103	/* Fetch the var's base element type */
1104	var_btype = bhnd_nvram_base_type(var->type);
1105
1106	/* Calculate total byte length of the native encoding */
1107	if ((iwidth = bhnd_nvram_value_size(NULL, 0, var_btype, 1)) == 0) {
1108		/* SPROM does not use (and we do not support) decoding of
1109		 * variable-width data types */
1110		BHND_NV_LOG("invalid SPROM data type: %d", var->type);
1111		return (EFTYPE);
1112	}
1113	ilen = nelem * iwidth;
1114
1115	/* Decode into our caller's local storage */
1116	inp = storage;
1117	if (ilen > sizeof(*storage)) {
1118		BHND_NV_LOG("error decoding '%s', SPROM_ARRAY_MAXLEN "
1119		    "incorrect\n", var->name);
1120		return (EFTYPE);
1121	}
1122
1123	/* Zero-initialize our decode buffer; any output elements skipped
1124	 * during decode should default to zero. */
1125	memset(inp, 0, ilen);
1126
1127	/*
1128	 * Decode the SPROM data, iteratively decoding up to nelem values.
1129	 */
1130	if ((error = bhnd_sprom_opcode_seek(state, entry))) {
1131		BHND_NV_LOG("variable seek failed: %d\n", error);
1132		return (error);
1133	}
1134
1135	ipos = 0;
1136	intv = 0x0;
1137	if (var->flags & BHND_NVRAM_VF_IGNALL1)
1138		all_bits_set = true;
1139	else
1140		all_bits_set = false;
1141	while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) {
1142		bhnd_sprom_opcode_bind	*binding;
1143		bhnd_sprom_opcode_var	*binding_var;
1144		bhnd_nvram_type		 intv_type;
1145		size_t			 offset;
1146		size_t			 nbyte;
1147		uint32_t		 skip_in_bytes;
1148		void			*ptr;
1149
1150		BHND_NV_ASSERT(
1151		    state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
1152		    ("invalid var state"));
1153		BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state"));
1154
1155		binding_var = &state->var;
1156		binding = &state->var.bind;
1157
1158		if (ipos >= nelem) {
1159			BHND_NV_LOG("output skip %u positioned "
1160			    "%zu beyond nelem %zu\n",
1161			    binding->skip_out, ipos, nelem);
1162			return (EINVAL);
1163		}
1164
1165		/* Calculate input skip bytes for this binding */
1166		skip_in_bytes = binding->skip_in;
1167		error = bhnd_sprom_opcode_apply_scale(state, &skip_in_bytes);
1168		if (error)
1169			return (error);
1170
1171		/* Bind */
1172		offset = state->offset;
1173		for (size_t i = 0; i < binding->count; i++) {
1174			/* Read the offset value, OR'ing with the current
1175			 * value of intv */
1176			error = bhnd_nvram_sprom_read_offset(var, io,
1177			    binding_var->base_type,
1178			    offset,
1179			    binding_var->mask,
1180			    binding_var->shift,
1181			    &intv);
1182			if (error)
1183				return (error);
1184
1185			/* If IGNALL1, record whether value does not have
1186			 * all bits set. */
1187			if (var->flags & BHND_NVRAM_VF_IGNALL1 &&
1188			    all_bits_set)
1189			{
1190				uint32_t all1;
1191
1192				all1 = binding_var->mask;
1193				if (binding_var->shift > 0)
1194					all1 >>= binding_var->shift;
1195				else if (binding_var->shift < 0)
1196					all1 <<= -binding_var->shift;
1197
1198				if ((intv & all1) != all1)
1199					all_bits_set = false;
1200			}
1201
1202			/* Adjust input position; this was already verified to
1203			 * not overflow/underflow during SPROM opcode
1204			 * evaluation */
1205			if (binding->skip_in_negative) {
1206				offset -= skip_in_bytes;
1207			} else {
1208				offset += skip_in_bytes;
1209			}
1210
1211			/* Skip writing to inp if additional bindings are
1212			 * required to fully populate intv */
1213			if (binding->skip_out == 0)
1214				continue;
1215
1216			/* We use bhnd_nvram_value_coerce() to perform
1217			 * overflow-checked coercion from the widened
1218			 * uint32/int32 intv value to the requested output
1219			 * type */
1220			if (bhnd_nvram_is_signed_type(var_btype))
1221				intv_type = BHND_NVRAM_TYPE_INT32;
1222			else
1223				intv_type = BHND_NVRAM_TYPE_UINT32;
1224
1225			/* Calculate address of the current element output
1226			 * position */
1227			ptr = (uint8_t *)inp + (iwidth * ipos);
1228
1229			/* Perform coercion of the array element */
1230			nbyte = iwidth;
1231			error = bhnd_nvram_value_coerce(&intv, sizeof(intv),
1232			    intv_type, ptr, &nbyte, var_btype);
1233			if (error)
1234				return (error);
1235
1236			/* Clear temporary state */
1237			intv = 0x0;
1238
1239			/* Advance output position */
1240			if (SIZE_MAX - binding->skip_out < ipos) {
1241				BHND_NV_LOG("output skip %u would overflow "
1242				    "%zu\n", binding->skip_out, ipos);
1243				return (EINVAL);
1244			}
1245
1246			ipos += binding->skip_out;
1247		}
1248	}
1249
1250	/* Did we iterate all bindings until hitting end of the variable
1251	 * definition? */
1252	BHND_NV_ASSERT(error != 0, ("loop terminated early"));
1253	if (error != ENOENT) {
1254		return (error);
1255	}
1256
1257	/* If marked IGNALL1 and all bits are set, treat variable as
1258	 * unavailable */
1259	if ((var->flags & BHND_NVRAM_VF_IGNALL1) && all_bits_set)
1260		return (ENOENT);
1261
1262	/* Provide value wrapper */
1263	return (bhnd_nvram_val_init(val, var->fmt, inp, ilen, var->type,
1264	    BHND_NVRAM_VAL_BORROW_DATA));
1265}
1266
1267
1268/**
1269 * Common variable decoding; fetches and decodes variable to @p val,
1270 * using @p storage for actual data storage.
1271 *
1272 * The returned @p val instance will hold a borrowed reference to @p storage,
1273 * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
1274 * the lifetime of @p storage.
1275 *
1276 * The caller is responsible for releasing any allocated value state
1277 * via bhnd_nvram_val_release().
1278 */
1279static int
1280bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep,
1281    union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
1282{
1283	struct bhnd_nvram_sprom		*sp;
1284	bhnd_sprom_opcode_idx_entry	*entry;
1285	const struct bhnd_nvram_vardefn	*var;
1286
1287	BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
1288
1289	sp = (struct bhnd_nvram_sprom *)nv;
1290	entry = cookiep;
1291
1292	/* Fetch canonical variable definition */
1293	var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
1294	BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
1295
1296	return (bhnd_nvram_sprom_read_var(&sp->state, entry, sp->data, storage,
1297	    val));
1298}
1299
1300static int
1301bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
1302    void *cookiep2)
1303{
1304	struct bhnd_sprom_opcode_idx_entry *e1, *e2;
1305
1306	e1 = cookiep1;
1307	e2 = cookiep2;
1308
1309	/* Use the index entry order; this matches the order of variables
1310	 * returned via bhnd_nvram_sprom_next() */
1311	if (e1 < e2)
1312		return (-1);
1313	else if (e1 > e2)
1314		return (1);
1315
1316	return (0);
1317}
1318
1319static int
1320bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
1321    size_t *len, bhnd_nvram_type otype)
1322{
1323	bhnd_nvram_val			val;
1324	union bhnd_nvram_sprom_storage	storage;
1325	int				error;
1326
1327	/* Decode variable to a new value instance */
1328	error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
1329	if (error)
1330		return (error);
1331
1332	/* Perform value coercion */
1333	error = bhnd_nvram_val_encode(&val, buf, len, otype);
1334
1335	/* Clean up */
1336	bhnd_nvram_val_release(&val);
1337	return (error);
1338}
1339
1340static int
1341bhnd_nvram_sprom_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
1342    bhnd_nvram_val **value)
1343{
1344	bhnd_nvram_val			val;
1345	union bhnd_nvram_sprom_storage	storage;
1346	int				error;
1347
1348	/* Decode variable to a new value instance */
1349	error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
1350	if (error)
1351		return (error);
1352
1353	/* Attempt to copy to heap */
1354	*value = bhnd_nvram_val_copy(&val);
1355	bhnd_nvram_val_release(&val);
1356
1357	if (*value == NULL)
1358		return (ENOMEM);
1359
1360	return (0);
1361}
1362
1363static const void *
1364bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
1365    size_t *len, bhnd_nvram_type *type)
1366{
1367	/* Unsupported */
1368	return (NULL);
1369}
1370
1371static const char *
1372bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
1373{
1374	const struct bhnd_nvram_vardefn	*var;
1375
1376	BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
1377
1378	var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
1379	BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
1380
1381	return (var->name);
1382}
1383
1384static int
1385bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
1386    bhnd_nvram_val *value, bhnd_nvram_val **result)
1387{
1388	struct bhnd_nvram_sprom		*sp;
1389	const struct bhnd_nvram_vardefn	*var;
1390	bhnd_sprom_opcode_idx_entry	*entry;
1391	bhnd_nvram_val			*spval;
1392	int				 error;
1393
1394	sp = (struct bhnd_nvram_sprom *)nv;
1395
1396	/* Is this an externally immutable variable name? */
1397	if (bhnd_sprom_is_external_immutable(name))
1398		return (EINVAL);
1399
1400	/* Variable must be defined in our SPROM layout */
1401	if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
1402		return (ENOENT);
1403
1404	var = bhnd_nvram_get_vardefn(entry->vid);
1405	BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
1406
1407	/* Value must be convertible to the native variable type */
1408	error = bhnd_nvram_val_convert_new(&spval, var->fmt, value,
1409	    BHND_NVRAM_VAL_DYNAMIC);
1410	if (error)
1411		return (error);
1412
1413	/* Value must be encodeable by our SPROM layout */
1414	error = bhnd_nvram_sprom_write_var(&sp->state, entry, spval, NULL);
1415	if (error) {
1416		bhnd_nvram_val_release(spval);
1417		return (error);
1418	}
1419
1420	/* Success. Transfer our ownership of the converted value to the
1421	 * caller */
1422	*result = spval;
1423	return (0);
1424}
1425
1426static int
1427bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
1428{
1429	struct bhnd_nvram_sprom		*sp;
1430	const struct bhnd_nvram_vardefn	*var;
1431	bhnd_sprom_opcode_idx_entry	*entry;
1432
1433	sp = (struct bhnd_nvram_sprom *)nv;
1434
1435	/* Is this an externally immutable variable name? */
1436	if (bhnd_sprom_is_external_immutable(name))
1437		return (EINVAL);
1438
1439	/* Variable must be defined in our SPROM layout */
1440	if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
1441		return (ENOENT);
1442
1443	var = bhnd_nvram_get_vardefn(entry->vid);
1444	BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
1445
1446	/* Variable must be capable of representing a NULL/deleted value.
1447	 *
1448	 * Since SPROM's layout is fixed, this requires IGNALL -- if
1449	 * all bits are set, an IGNALL variable is treated as unset. */
1450	if (!(var->flags & BHND_NVRAM_VF_IGNALL1))
1451		return (EINVAL);
1452
1453	return (0);
1454}
1455
1456/**
1457 * Return true if @p name represents a special immutable variable name
1458 * (e.g. sromrev) that cannot be updated in an SPROM existing image.
1459 *
1460 * @param name The name to check.
1461 */
1462static bool
1463bhnd_sprom_is_external_immutable(const char *name)
1464{
1465	/* The layout revision is immutable and cannot be changed */
1466	if (strcmp(name, BHND_NVAR_SROMREV) == 0)
1467		return (true);
1468
1469	return (false);
1470}
1471