xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
177cb4d3eSLandon J. Fuller /*-
277cb4d3eSLandon J. Fuller  * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
377cb4d3eSLandon J. Fuller  * All rights reserved.
477cb4d3eSLandon J. Fuller  *
577cb4d3eSLandon J. Fuller  * Redistribution and use in source and binary forms, with or without
677cb4d3eSLandon J. Fuller  * modification, are permitted provided that the following conditions
777cb4d3eSLandon J. Fuller  * are met:
877cb4d3eSLandon J. Fuller  * 1. Redistributions of source code must retain the above copyright
977cb4d3eSLandon J. Fuller  *    notice, this list of conditions and the following disclaimer,
1077cb4d3eSLandon J. Fuller  *    without modification.
1177cb4d3eSLandon J. Fuller  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1277cb4d3eSLandon J. Fuller  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
1377cb4d3eSLandon J. Fuller  *    redistribution must be conditioned upon including a substantially
1477cb4d3eSLandon J. Fuller  *    similar Disclaimer requirement for further binary redistribution.
1577cb4d3eSLandon J. Fuller  *
1677cb4d3eSLandon J. Fuller  * NO WARRANTY
1777cb4d3eSLandon J. Fuller  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1877cb4d3eSLandon J. Fuller  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1977cb4d3eSLandon J. Fuller  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
2077cb4d3eSLandon J. Fuller  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
2177cb4d3eSLandon J. Fuller  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
2277cb4d3eSLandon J. Fuller  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2377cb4d3eSLandon J. Fuller  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2477cb4d3eSLandon J. Fuller  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
2577cb4d3eSLandon J. Fuller  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2677cb4d3eSLandon J. Fuller  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
2777cb4d3eSLandon J. Fuller  * THE POSSIBILITY OF SUCH DAMAGES.
2877cb4d3eSLandon J. Fuller  */
2977cb4d3eSLandon J. Fuller 
3077cb4d3eSLandon J. Fuller #include <sys/cdefs.h>
3177cb4d3eSLandon J. Fuller #include <sys/endian.h>
3277cb4d3eSLandon J. Fuller 
3377cb4d3eSLandon J. Fuller #ifdef _KERNEL
3477cb4d3eSLandon J. Fuller #include <sys/param.h>
3577cb4d3eSLandon J. Fuller #include <sys/ctype.h>
3677cb4d3eSLandon J. Fuller #include <sys/malloc.h>
3777cb4d3eSLandon J. Fuller #include <sys/systm.h>
3877cb4d3eSLandon J. Fuller 
3977cb4d3eSLandon J. Fuller #include <machine/_inttypes.h>
4077cb4d3eSLandon J. Fuller #else /* !_KERNEL */
4177cb4d3eSLandon J. Fuller #include <ctype.h>
4277cb4d3eSLandon J. Fuller #include <errno.h>
4377cb4d3eSLandon J. Fuller #include <inttypes.h>
4477cb4d3eSLandon J. Fuller #include <stdint.h>
4577cb4d3eSLandon J. Fuller #include <stdio.h>
4677cb4d3eSLandon J. Fuller #include <stdlib.h>
4777cb4d3eSLandon J. Fuller #include <string.h>
4877cb4d3eSLandon J. Fuller #endif /* _KERNEL */
4977cb4d3eSLandon J. Fuller 
50c283839dSLandon J. Fuller #include "bhnd_nvram_map.h"
5177cb4d3eSLandon J. Fuller 
52c283839dSLandon J. Fuller #include "bhnd_nvram_private.h"
5377cb4d3eSLandon J. Fuller #include "bhnd_nvram_datavar.h"
5477cb4d3eSLandon J. Fuller 
5577cb4d3eSLandon J. Fuller #include "bhnd_nvram_data_spromvar.h"
5677cb4d3eSLandon J. Fuller 
5777cb4d3eSLandon J. Fuller /*
5877cb4d3eSLandon J. Fuller  * BHND SPROM NVRAM data class
5977cb4d3eSLandon J. Fuller  *
6077cb4d3eSLandon J. Fuller  * The SPROM data format is a fixed-layout, non-self-descriptive binary format,
6177cb4d3eSLandon J. Fuller  * used on Broadcom wireless and wired adapters, that provides a subset of the
6277cb4d3eSLandon J. Fuller  * variables defined by Broadcom SoC NVRAM formats.
6377cb4d3eSLandon J. Fuller  */
6477cb4d3eSLandon J. Fuller 
65c283839dSLandon J. Fuller static const bhnd_sprom_layout  *bhnd_nvram_sprom_get_layout(uint8_t sromrev);
6677cb4d3eSLandon J. Fuller 
67c283839dSLandon J. Fuller static int			 bhnd_nvram_sprom_ident(
68c283839dSLandon J. Fuller 				     struct bhnd_nvram_io *io,
69591e79bcSLandon J. Fuller 				     const bhnd_sprom_layout **ident);
7077cb4d3eSLandon J. Fuller 
71c283839dSLandon J. Fuller static int			 bhnd_nvram_sprom_write_var(
72c283839dSLandon J. Fuller 				     bhnd_sprom_opcode_state *state,
73c283839dSLandon J. Fuller 				     bhnd_sprom_opcode_idx_entry *entry,
74c283839dSLandon J. Fuller 				     bhnd_nvram_val *value,
75c283839dSLandon J. Fuller 				     struct bhnd_nvram_io *io);
7677cb4d3eSLandon J. Fuller 
77591e79bcSLandon J. Fuller static int			 bhnd_nvram_sprom_read_var(
78591e79bcSLandon J. Fuller 				     struct bhnd_sprom_opcode_state *state,
79591e79bcSLandon J. Fuller 				     struct bhnd_sprom_opcode_idx_entry *entry,
80591e79bcSLandon J. Fuller 				     struct bhnd_nvram_io *io,
81591e79bcSLandon J. Fuller 				     union bhnd_nvram_sprom_storage *storage,
82591e79bcSLandon J. Fuller 				     bhnd_nvram_val *val);
83591e79bcSLandon J. Fuller 
84c283839dSLandon J. Fuller static int			 bhnd_nvram_sprom_write_offset(
85c283839dSLandon J. Fuller 				     const struct bhnd_nvram_vardefn *var,
86c283839dSLandon J. Fuller 				     struct bhnd_nvram_io *data,
87c283839dSLandon J. Fuller 				     bhnd_nvram_type type, size_t offset,
88c283839dSLandon J. Fuller 				     uint32_t mask, int8_t shift,
89c283839dSLandon J. Fuller 				     uint32_t value);
9077cb4d3eSLandon J. Fuller 
91c283839dSLandon J. Fuller static int			 bhnd_nvram_sprom_read_offset(
92c283839dSLandon J. Fuller 				     const struct bhnd_nvram_vardefn *var,
93c283839dSLandon J. Fuller 				     struct bhnd_nvram_io *data,
94c283839dSLandon J. Fuller 				     bhnd_nvram_type type, size_t offset,
95c283839dSLandon J. Fuller 				     uint32_t mask, int8_t shift,
9677cb4d3eSLandon J. Fuller 				     uint32_t *value);
9777cb4d3eSLandon J. Fuller 
98c283839dSLandon J. Fuller static bool			 bhnd_sprom_is_external_immutable(
99c283839dSLandon J. Fuller 				     const char *name);
10077cb4d3eSLandon J. Fuller 
101c283839dSLandon J. Fuller BHND_NVRAM_DATA_CLASS_DEFN(sprom, "Broadcom SPROM",
102c283839dSLandon J. Fuller     BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_sprom))
10377cb4d3eSLandon J. Fuller 
104c283839dSLandon J. Fuller #define	SPROM_COOKIE_TO_VID(_cookie)	\
105c283839dSLandon J. Fuller 	(((struct bhnd_sprom_opcode_idx_entry *)(_cookie))->vid)
106c283839dSLandon J. Fuller 
107c283839dSLandon J. Fuller #define	SPROM_COOKIE_TO_NVRAM_VAR(_cookie)	\
108c283839dSLandon J. Fuller 	bhnd_nvram_get_vardefn(SPROM_COOKIE_TO_VID(_cookie))
10977cb4d3eSLandon J. Fuller 
11077cb4d3eSLandon J. Fuller /**
11177cb4d3eSLandon J. Fuller  * Read the magic value from @p io, and verify that it matches
11277cb4d3eSLandon J. Fuller  * the @p layout's expected magic value.
11377cb4d3eSLandon J. Fuller  *
11477cb4d3eSLandon J. Fuller  * If @p layout does not defined a magic value, @p magic is set to 0x0
11577cb4d3eSLandon J. Fuller  * and success is returned.
11677cb4d3eSLandon J. Fuller  *
11777cb4d3eSLandon J. Fuller  * @param	io	An I/O context mapping the SPROM data to be identified.
11877cb4d3eSLandon J. Fuller  * @param	layout	The SPROM layout against which @p io should be verified.
11977cb4d3eSLandon J. Fuller  * @param[out]	magic	On success, the SPROM magic value.
12077cb4d3eSLandon J. Fuller  *
12177cb4d3eSLandon J. Fuller  * @retval 0		success
12277cb4d3eSLandon J. Fuller  * @retval non-zero	If checking @p io otherwise fails, a regular unix
12377cb4d3eSLandon J. Fuller  *			error code will be returned.
12477cb4d3eSLandon J. Fuller  */
12577cb4d3eSLandon J. Fuller static int
bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io * io,const bhnd_sprom_layout * layout,uint16_t * magic)12677cb4d3eSLandon J. Fuller bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io *io,
127c283839dSLandon J. Fuller     const bhnd_sprom_layout *layout, uint16_t *magic)
12877cb4d3eSLandon J. Fuller {
12977cb4d3eSLandon J. Fuller 	int error;
13077cb4d3eSLandon J. Fuller 
13177cb4d3eSLandon J. Fuller 	/* Skip if layout does not define a magic value */
13277cb4d3eSLandon J. Fuller 	if (layout->flags & SPROM_LAYOUT_MAGIC_NONE)
13377cb4d3eSLandon J. Fuller 		return (0);
13477cb4d3eSLandon J. Fuller 
13577cb4d3eSLandon J. Fuller 	/* Read the magic value */
13677cb4d3eSLandon J. Fuller 	error = bhnd_nvram_io_read(io, layout->magic_offset, magic,
13777cb4d3eSLandon J. Fuller 	    sizeof(*magic));
13877cb4d3eSLandon J. Fuller 	if (error)
13977cb4d3eSLandon J. Fuller 		return (error);
14077cb4d3eSLandon J. Fuller 
14177cb4d3eSLandon J. Fuller 	*magic = le16toh(*magic);
14277cb4d3eSLandon J. Fuller 
14377cb4d3eSLandon J. Fuller 	/* If the signature does not match, skip to next layout */
14477cb4d3eSLandon J. Fuller 	if (*magic != layout->magic_value)
14577cb4d3eSLandon J. Fuller 		return (ENXIO);
14677cb4d3eSLandon J. Fuller 
14777cb4d3eSLandon J. Fuller 	return (0);
14877cb4d3eSLandon J. Fuller }
14977cb4d3eSLandon J. Fuller 
15077cb4d3eSLandon J. Fuller /**
15177cb4d3eSLandon J. Fuller  * Attempt to identify the format of the SPROM data mapped by @p io.
15277cb4d3eSLandon J. Fuller  *
15377cb4d3eSLandon J. Fuller  * The SPROM data format does not provide any identifying information at a
15477cb4d3eSLandon J. Fuller  * known offset, instead requiring that we iterate over the known SPROM image
15577cb4d3eSLandon J. Fuller  * sizes until we are able to compute a valid checksum (and, for later
15677cb4d3eSLandon J. Fuller  * revisions, validate a signature at a revision-specific offset).
15777cb4d3eSLandon J. Fuller  *
15877cb4d3eSLandon J. Fuller  * @param	io	An I/O context mapping the SPROM data to be identified.
15977cb4d3eSLandon J. Fuller  * @param[out]	ident	On success, the identified SPROM layout.
16077cb4d3eSLandon J. Fuller  *
16177cb4d3eSLandon J. Fuller  * @retval 0		success
16277cb4d3eSLandon J. Fuller  * @retval non-zero	If identifying @p io otherwise fails, a regular unix
16377cb4d3eSLandon J. Fuller  *			error code will be returned.
16477cb4d3eSLandon J. Fuller  */
16577cb4d3eSLandon J. Fuller static int
bhnd_nvram_sprom_ident(struct bhnd_nvram_io * io,const bhnd_sprom_layout ** ident)16677cb4d3eSLandon J. Fuller bhnd_nvram_sprom_ident(struct bhnd_nvram_io *io,
167591e79bcSLandon J. Fuller     const bhnd_sprom_layout **ident)
16877cb4d3eSLandon J. Fuller {
16977cb4d3eSLandon J. Fuller 	uint8_t	crc;
17077cb4d3eSLandon J. Fuller 	size_t	crc_errors;
171591e79bcSLandon J. Fuller 	size_t	nbytes;
17277cb4d3eSLandon J. Fuller 	int	error;
17377cb4d3eSLandon J. Fuller 
17477cb4d3eSLandon J. Fuller 	crc = BHND_NVRAM_CRC8_INITIAL;
17577cb4d3eSLandon J. Fuller 	crc_errors = 0;
176591e79bcSLandon J. Fuller 	nbytes = 0;
17777cb4d3eSLandon J. Fuller 
17877cb4d3eSLandon J. Fuller 	/* We iterate the SPROM layouts smallest to largest, allowing us to
17977cb4d3eSLandon J. Fuller 	 * perform incremental checksum calculation */
18077cb4d3eSLandon J. Fuller 	for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
181c283839dSLandon J. Fuller 		const bhnd_sprom_layout	*layout;
182591e79bcSLandon J. Fuller 		u_char			 buf[512];
183591e79bcSLandon J. Fuller 		size_t			 nread;
18477cb4d3eSLandon J. Fuller 		uint16_t		 magic;
18526e4f220SLandon J. Fuller 		uint8_t			 srevcrc[2];
18677cb4d3eSLandon J. Fuller 		uint8_t			 srev;
18777cb4d3eSLandon J. Fuller 		bool			 crc_valid;
18877cb4d3eSLandon J. Fuller 		bool			 have_magic;
18977cb4d3eSLandon J. Fuller 
19077cb4d3eSLandon J. Fuller 		layout = &bhnd_sprom_layouts[i];
1916467a17bSLandon J. Fuller 		crc_valid = true;
19277cb4d3eSLandon J. Fuller 
19377cb4d3eSLandon J. Fuller 		have_magic = true;
194591e79bcSLandon J. Fuller 		if ((layout->flags & SPROM_LAYOUT_MAGIC_NONE))
195591e79bcSLandon J. Fuller 			have_magic = false;
19677cb4d3eSLandon J. Fuller 
197591e79bcSLandon J. Fuller 		/*
198591e79bcSLandon J. Fuller 		 * Read image data and update CRC (errors are reported
199591e79bcSLandon J. Fuller 		 * after the signature check)
200591e79bcSLandon J. Fuller 		 *
201591e79bcSLandon J. Fuller 		 * Layout instances must be ordered from smallest to largest by
202591e79bcSLandon J. Fuller 		 * the nvram_map compiler, allowing us to incrementally update
203591e79bcSLandon J. Fuller 		 * our CRC.
204591e79bcSLandon J. Fuller 		 */
20577cb4d3eSLandon J. Fuller 		if (nbytes > layout->size)
206591e79bcSLandon J. Fuller 			BHND_NV_PANIC("SPROM layout defined out-of-order");
20777cb4d3eSLandon J. Fuller 
208591e79bcSLandon J. Fuller 		nread = layout->size - nbytes;
20977cb4d3eSLandon J. Fuller 
210591e79bcSLandon J. Fuller 		while (nread > 0) {
211591e79bcSLandon J. Fuller 			size_t nr;
21277cb4d3eSLandon J. Fuller 
213591e79bcSLandon J. Fuller 			nr = bhnd_nv_ummin(nread, sizeof(buf));
21477cb4d3eSLandon J. Fuller 
215591e79bcSLandon J. Fuller 			if ((error = bhnd_nvram_io_read(io, nbytes, buf, nr)))
216591e79bcSLandon J. Fuller 				return (error);
21777cb4d3eSLandon J. Fuller 
218591e79bcSLandon J. Fuller 			crc = bhnd_nvram_crc8(buf, nr, crc);
21977cb4d3eSLandon J. Fuller 			crc_valid = (crc == BHND_NVRAM_CRC8_VALID);
22077cb4d3eSLandon J. Fuller 			if (!crc_valid)
22177cb4d3eSLandon J. Fuller 				crc_errors++;
22277cb4d3eSLandon J. Fuller 
223591e79bcSLandon J. Fuller 			nread -= nr;
224591e79bcSLandon J. Fuller 			nbytes += nr;
225591e79bcSLandon J. Fuller 		}
226591e79bcSLandon J. Fuller 
22726e4f220SLandon J. Fuller 		/* Read 8-bit SPROM revision, maintaining 16-bit size alignment
22826e4f220SLandon J. Fuller 		 * required by some OTP/SPROM chipsets. */
22926e4f220SLandon J. Fuller 		error = bhnd_nvram_io_read(io, layout->srev_offset, &srevcrc,
23026e4f220SLandon J. Fuller 		    sizeof(srevcrc));
23177cb4d3eSLandon J. Fuller 		if (error)
232591e79bcSLandon J. Fuller 			return (error);
23377cb4d3eSLandon J. Fuller 
23426e4f220SLandon J. Fuller 		srev = srevcrc[0];
23526e4f220SLandon J. Fuller 
23677cb4d3eSLandon J. Fuller 		/* Early sromrev 1 devices (specifically some BCM440x enet
23777cb4d3eSLandon J. Fuller 		 * cards) are reported to have been incorrectly programmed
23877cb4d3eSLandon J. Fuller 		 * with a revision of 0x10. */
23977cb4d3eSLandon J. Fuller 		if (layout->rev == 1 && srev == 0x10)
24077cb4d3eSLandon J. Fuller 			srev = 0x1;
24177cb4d3eSLandon J. Fuller 
24277cb4d3eSLandon J. Fuller 		/* Check revision against the layout definition */
24377cb4d3eSLandon J. Fuller 		if (srev != layout->rev)
24477cb4d3eSLandon J. Fuller 			continue;
24577cb4d3eSLandon J. Fuller 
24677cb4d3eSLandon J. Fuller 		/* Check the magic value, skipping to the next layout on
24777cb4d3eSLandon J. Fuller 		 * failure. */
248591e79bcSLandon J. Fuller 		error = bhnd_nvram_sprom_check_magic(io, layout, &magic);
24977cb4d3eSLandon J. Fuller 		if (error) {
25077cb4d3eSLandon J. Fuller 			/* If the CRC is was valid, log the mismatch */
25177cb4d3eSLandon J. Fuller 			if (crc_valid || BHND_NV_VERBOSE) {
25277cb4d3eSLandon J. Fuller 				BHND_NV_LOG("invalid sprom %hhu signature: "
25377cb4d3eSLandon J. Fuller 					    "0x%hx (expected 0x%hx)\n", srev,
25477cb4d3eSLandon J. Fuller 					    magic, layout->magic_value);
25577cb4d3eSLandon J. Fuller 
256591e79bcSLandon J. Fuller 					return (ENXIO);
25777cb4d3eSLandon J. Fuller 			}
25877cb4d3eSLandon J. Fuller 
25977cb4d3eSLandon J. Fuller 			continue;
26077cb4d3eSLandon J. Fuller 		}
26177cb4d3eSLandon J. Fuller 
26277cb4d3eSLandon J. Fuller 		/* Check for an earlier CRC error */
26377cb4d3eSLandon J. Fuller 		if (!crc_valid) {
26477cb4d3eSLandon J. Fuller 			/* If the magic check succeeded, then we may just have
26577cb4d3eSLandon J. Fuller 			 * data corruption -- log the CRC error */
26677cb4d3eSLandon J. Fuller 			if (have_magic || BHND_NV_VERBOSE) {
26777cb4d3eSLandon J. Fuller 				BHND_NV_LOG("sprom %hhu CRC error (crc=%#hhx, "
26877cb4d3eSLandon J. Fuller 					    "expected=%#x)\n", srev, crc,
26977cb4d3eSLandon J. Fuller 					    BHND_NVRAM_CRC8_VALID);
27077cb4d3eSLandon J. Fuller 			}
27177cb4d3eSLandon J. Fuller 
27277cb4d3eSLandon J. Fuller 			continue;
27377cb4d3eSLandon J. Fuller 		}
27477cb4d3eSLandon J. Fuller 
27577cb4d3eSLandon J. Fuller 		/* Identified */
27677cb4d3eSLandon J. Fuller 		*ident = layout;
27777cb4d3eSLandon J. Fuller 		return (0);
27877cb4d3eSLandon J. Fuller 	}
27977cb4d3eSLandon J. Fuller 
280591e79bcSLandon J. Fuller 	/* No match */
28177cb4d3eSLandon J. Fuller 	if (crc_errors > 0 && BHND_NV_VERBOSE) {
28277cb4d3eSLandon J. Fuller 		BHND_NV_LOG("sprom parsing failed with %zu CRC errors\n",
28377cb4d3eSLandon J. Fuller 		    crc_errors);
28477cb4d3eSLandon J. Fuller 	}
28577cb4d3eSLandon J. Fuller 
286591e79bcSLandon J. Fuller 	return (ENXIO);
28777cb4d3eSLandon J. Fuller }
28877cb4d3eSLandon J. Fuller 
28977cb4d3eSLandon J. Fuller static int
bhnd_nvram_sprom_probe(struct bhnd_nvram_io * io)29077cb4d3eSLandon J. Fuller bhnd_nvram_sprom_probe(struct bhnd_nvram_io *io)
29177cb4d3eSLandon J. Fuller {
292c283839dSLandon J. Fuller 	const bhnd_sprom_layout	*layout;
29377cb4d3eSLandon J. Fuller 	int			 error;
29477cb4d3eSLandon J. Fuller 
29577cb4d3eSLandon J. Fuller 	/* Try to parse the input */
296591e79bcSLandon J. Fuller 	if ((error = bhnd_nvram_sprom_ident(io, &layout)))
29777cb4d3eSLandon J. Fuller 		return (error);
29877cb4d3eSLandon J. Fuller 
29977cb4d3eSLandon J. Fuller 	return (BHND_NVRAM_DATA_PROBE_DEFAULT);
30077cb4d3eSLandon J. Fuller }
30177cb4d3eSLandon J. Fuller 
302591e79bcSLandon J. Fuller static int
bhnd_nvram_sprom_getvar_direct(struct bhnd_nvram_io * io,const char * name,void * buf,size_t * len,bhnd_nvram_type type)303591e79bcSLandon J. Fuller bhnd_nvram_sprom_getvar_direct(struct bhnd_nvram_io *io, const char *name,
304591e79bcSLandon J. Fuller     void *buf, size_t *len, bhnd_nvram_type type)
305591e79bcSLandon J. Fuller {
306591e79bcSLandon J. Fuller 	const bhnd_sprom_layout		*layout;
307591e79bcSLandon J. Fuller 	bhnd_sprom_opcode_state		 state;
308591e79bcSLandon J. Fuller 	const struct bhnd_nvram_vardefn	*var;
309591e79bcSLandon J. Fuller 	size_t				 vid;
310591e79bcSLandon J. Fuller 	int				 error;
311591e79bcSLandon J. Fuller 
312591e79bcSLandon J. Fuller 	/* Look up the variable definition and ID */
313591e79bcSLandon J. Fuller 	if ((var = bhnd_nvram_find_vardefn(name)) == NULL)
314591e79bcSLandon J. Fuller 		return (ENOENT);
315591e79bcSLandon J. Fuller 
316591e79bcSLandon J. Fuller 	vid = bhnd_nvram_get_vardefn_id(var);
317591e79bcSLandon J. Fuller 
318591e79bcSLandon J. Fuller 	/* Identify the SPROM image layout */
319591e79bcSLandon J. Fuller 	if ((error = bhnd_nvram_sprom_ident(io, &layout)))
320591e79bcSLandon J. Fuller 		return (error);
321591e79bcSLandon J. Fuller 
322591e79bcSLandon J. Fuller 	/* Initialize SPROM layout interpreter */
323591e79bcSLandon J. Fuller 	if ((error = bhnd_sprom_opcode_init(&state, layout))) {
324591e79bcSLandon J. Fuller 		BHND_NV_LOG("error initializing opcode state: %d\n", error);
325591e79bcSLandon J. Fuller 		return (ENXIO);
326591e79bcSLandon J. Fuller 	}
327591e79bcSLandon J. Fuller 
328591e79bcSLandon J. Fuller 	/* Find SPROM layout entry for the requested variable */
329591e79bcSLandon J. Fuller 	while ((error = bhnd_sprom_opcode_next_var(&state)) == 0) {
330591e79bcSLandon J. Fuller 		bhnd_sprom_opcode_idx_entry	entry;
331591e79bcSLandon J. Fuller 		union bhnd_nvram_sprom_storage	storage;
332591e79bcSLandon J. Fuller 		bhnd_nvram_val			val;
333591e79bcSLandon J. Fuller 
334591e79bcSLandon J. Fuller 		/* Fetch the variable's entry state */
335591e79bcSLandon J. Fuller 		if ((error = bhnd_sprom_opcode_init_entry(&state, &entry)))
336591e79bcSLandon J. Fuller 			return (error);
337591e79bcSLandon J. Fuller 
338591e79bcSLandon J. Fuller 		/* Match against expected VID */
339591e79bcSLandon J. Fuller 		if (entry.vid != vid)
340591e79bcSLandon J. Fuller 			continue;
341591e79bcSLandon J. Fuller 
342591e79bcSLandon J. Fuller 		/* Decode variable to a new value instance */
343591e79bcSLandon J. Fuller 		error = bhnd_nvram_sprom_read_var(&state, &entry, io, &storage,
344591e79bcSLandon J. Fuller 		    &val);
345591e79bcSLandon J. Fuller 		if (error)
346591e79bcSLandon J. Fuller 			return (error);
347591e79bcSLandon J. Fuller 
348591e79bcSLandon J. Fuller 		/* Perform value coercion */
349591e79bcSLandon J. Fuller 		error = bhnd_nvram_val_encode(&val, buf, len, type);
350591e79bcSLandon J. Fuller 
351591e79bcSLandon J. Fuller 		/* Clean up */
352591e79bcSLandon J. Fuller 		bhnd_nvram_val_release(&val);
353591e79bcSLandon J. Fuller 		return (error);
354591e79bcSLandon J. Fuller 	}
355591e79bcSLandon J. Fuller 
356591e79bcSLandon J. Fuller 	/* Hit EOF without matching the requested variable? */
357591e79bcSLandon J. Fuller 	if (error == ENOENT)
358591e79bcSLandon J. Fuller 		return (ENOENT);
359591e79bcSLandon J. Fuller 
360*88cdf609SGordon Bergling 	/* Some other parse error occurred */
361591e79bcSLandon J. Fuller 	return (error);
362591e79bcSLandon J. Fuller }
363c283839dSLandon J. Fuller 
364c283839dSLandon J. Fuller /**
365c283839dSLandon J. Fuller  * Return the SPROM layout definition for the given @p sromrev, or NULL if
366c283839dSLandon J. Fuller  * not found.
367c283839dSLandon J. Fuller  */
368c283839dSLandon J. Fuller static const bhnd_sprom_layout *
bhnd_nvram_sprom_get_layout(uint8_t sromrev)369c283839dSLandon J. Fuller bhnd_nvram_sprom_get_layout(uint8_t sromrev)
370c283839dSLandon J. Fuller {
371c283839dSLandon J. Fuller 	/* Find matching SPROM layout definition */
372c283839dSLandon J. Fuller 	for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
373c283839dSLandon J. Fuller 		if (bhnd_sprom_layouts[i].rev == sromrev)
374c283839dSLandon J. Fuller 			return (&bhnd_sprom_layouts[i]);
375c283839dSLandon J. Fuller 	}
376c283839dSLandon J. Fuller 
377c283839dSLandon J. Fuller 	/* Not found */
378c283839dSLandon J. Fuller 	return (NULL);
379c283839dSLandon J. Fuller }
380c283839dSLandon J. Fuller 
381c283839dSLandon J. Fuller /**
382c283839dSLandon J. Fuller  * Serialize a SPROM variable.
383c283839dSLandon J. Fuller  *
384c283839dSLandon J. Fuller  * @param state	The SPROM opcode state describing the layout of @p io.
385c283839dSLandon J. Fuller  * @param entry	The variable's SPROM opcode index entry.
386c283839dSLandon J. Fuller  * @param value	The value to encode to @p io as per @p entry.
387c283839dSLandon J. Fuller  * @param io	I/O context to which @p value should be written, or NULL
388c283839dSLandon J. Fuller  *		if no output should be produced. This may be used to validate
389c283839dSLandon J. Fuller  *		values prior to write.
390c283839dSLandon J. Fuller  *
391c283839dSLandon J. Fuller  * @retval 0		success
392c283839dSLandon J. Fuller  * @retval EFTYPE	If value coercion from @p value to the type required by
393c283839dSLandon J. Fuller  *			@p entry is unsupported.
394c283839dSLandon J. Fuller  * @retval ERANGE	If value coercion from @p value would overflow
395c283839dSLandon J. Fuller  *			(or underflow) the type required by @p entry.
396c283839dSLandon J. Fuller  * @retval non-zero	If serialization otherwise fails, a regular unix error
397c283839dSLandon J. Fuller  *			code will be returned.
398c283839dSLandon J. Fuller  */
399c283839dSLandon J. Fuller static int
bhnd_nvram_sprom_write_var(bhnd_sprom_opcode_state * state,bhnd_sprom_opcode_idx_entry * entry,bhnd_nvram_val * value,struct bhnd_nvram_io * io)400c283839dSLandon J. Fuller bhnd_nvram_sprom_write_var(bhnd_sprom_opcode_state *state,
401c283839dSLandon J. Fuller     bhnd_sprom_opcode_idx_entry *entry, bhnd_nvram_val *value,
402c283839dSLandon J. Fuller     struct bhnd_nvram_io *io)
403c283839dSLandon J. Fuller {
404c283839dSLandon J. Fuller 	const struct bhnd_nvram_vardefn	*var;
405c283839dSLandon J. Fuller 	uint32_t			 u32[BHND_SPROM_ARRAY_MAXLEN];
406c283839dSLandon J. Fuller 	bhnd_nvram_type			 itype, var_base_type;
407c283839dSLandon J. Fuller 	size_t				 ipos, ilen, nelem;
408c283839dSLandon J. Fuller 	int				 error;
409c283839dSLandon J. Fuller 
410c283839dSLandon J. Fuller 	/* Fetch variable definition and the native element type */
411c283839dSLandon J. Fuller 	var = bhnd_nvram_get_vardefn(entry->vid);
412c283839dSLandon J. Fuller 	BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
413c283839dSLandon J. Fuller 
414c283839dSLandon J. Fuller 	var_base_type = bhnd_nvram_base_type(var->type);
415c283839dSLandon J. Fuller 
416c283839dSLandon J. Fuller 	/* Fetch the element count from the SPROM variable layout definition */
417591e79bcSLandon J. Fuller 	if ((error = bhnd_sprom_opcode_eval_var(state, entry)))
418c283839dSLandon J. Fuller 		return (error);
419c283839dSLandon J. Fuller 
420c283839dSLandon J. Fuller 	nelem = state->var.nelem;
421c283839dSLandon J. Fuller 	BHND_NV_ASSERT(nelem <= var->nelem, ("SPROM nelem=%zu exceeds maximum "
422c283839dSLandon J. Fuller 	     "NVRAM nelem=%hhu", nelem, var->nelem));
423c283839dSLandon J. Fuller 
424c283839dSLandon J. Fuller 	/* Promote the data to a common 32-bit representation */
425c283839dSLandon J. Fuller 	if (bhnd_nvram_is_signed_type(var_base_type))
426c283839dSLandon J. Fuller 		itype = BHND_NVRAM_TYPE_INT32_ARRAY;
427c283839dSLandon J. Fuller 	else
428c283839dSLandon J. Fuller 		itype = BHND_NVRAM_TYPE_UINT32_ARRAY;
429c283839dSLandon J. Fuller 
430c283839dSLandon J. Fuller 	/* Calculate total size of the 32-bit promoted representation */
431c283839dSLandon J. Fuller 	if ((ilen = bhnd_nvram_value_size(NULL, 0, itype, nelem)) == 0) {
432c283839dSLandon J. Fuller 		/* Variable-width types are unsupported */
433c283839dSLandon J. Fuller 		BHND_NV_LOG("invalid %s SPROM variable type %d\n",
434c283839dSLandon J. Fuller 			    var->name, var->type);
435c283839dSLandon J. Fuller 		return (EFTYPE);
436c283839dSLandon J. Fuller 	}
437c283839dSLandon J. Fuller 
438c283839dSLandon J. Fuller 	/* The native representation must fit within our scratch array */
439c283839dSLandon J. Fuller 	if (ilen > sizeof(u32)) {
440c283839dSLandon J. Fuller 		BHND_NV_LOG("error encoding '%s', SPROM_ARRAY_MAXLEN "
441c283839dSLandon J. Fuller 			    "incorrect\n", var->name);
442c283839dSLandon J. Fuller 		return (EFTYPE);
443c283839dSLandon J. Fuller 	}
444c283839dSLandon J. Fuller 
445c283839dSLandon J. Fuller 	/* Initialize our common 32-bit value representation */
446c283839dSLandon J. Fuller 	if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) {
447c283839dSLandon J. Fuller 		/* No value provided; can this variable be encoded as missing
448c283839dSLandon J. Fuller 		 * by setting all bits to one? */
449c283839dSLandon J. Fuller 		if (!(var->flags & BHND_NVRAM_VF_IGNALL1)) {
450c283839dSLandon J. Fuller 			BHND_NV_LOG("missing required property: %s\n",
451c283839dSLandon J. Fuller 			    var->name);
452c283839dSLandon J. Fuller 			return (EINVAL);
453c283839dSLandon J. Fuller 		}
454c283839dSLandon J. Fuller 
455c283839dSLandon J. Fuller 		/* Set all bits */
456c283839dSLandon J. Fuller 		memset(u32, 0xFF, ilen);
457c283839dSLandon J. Fuller 	} else {
458c283839dSLandon J. Fuller 		bhnd_nvram_val	 bcm_val;
459c283839dSLandon J. Fuller 		const void	*var_ptr;
460c283839dSLandon J. Fuller 		bhnd_nvram_type	 var_type, raw_type;
461c283839dSLandon J. Fuller 		size_t		 var_len, enc_nelem;
462c283839dSLandon J. Fuller 
463c283839dSLandon J. Fuller 		/* Try to coerce the value to the native variable format. */
464c283839dSLandon J. Fuller 		error = bhnd_nvram_val_convert_init(&bcm_val, var->fmt, value,
465c283839dSLandon J. Fuller 		    BHND_NVRAM_VAL_DYNAMIC|BHND_NVRAM_VAL_BORROW_DATA);
466c283839dSLandon J. Fuller 		if (error) {
467c283839dSLandon J. Fuller 			BHND_NV_LOG("error converting input type %s to %s "
468c283839dSLandon J. Fuller 			    "format\n",
469c283839dSLandon J. Fuller 			    bhnd_nvram_type_name(bhnd_nvram_val_type(value)),
470c283839dSLandon J. Fuller 			    bhnd_nvram_val_fmt_name(var->fmt));
471c283839dSLandon J. Fuller 			return (error);
472c283839dSLandon J. Fuller 		}
473c283839dSLandon J. Fuller 
474c283839dSLandon J. Fuller 		var_ptr = bhnd_nvram_val_bytes(&bcm_val, &var_len, &var_type);
475c283839dSLandon J. Fuller 
476c283839dSLandon J. Fuller 		/*
477c283839dSLandon J. Fuller 		 * Promote to a common 32-bit representation.
478c283839dSLandon J. Fuller 		 *
479c283839dSLandon J. Fuller 		 * We must use the raw type to interpret the input data as its
480c283839dSLandon J. Fuller 		 * underlying integer representation -- otherwise, coercion
481c283839dSLandon J. Fuller 		 * would attempt to parse the input as its complex
482c283839dSLandon J. Fuller 		 * representation.
483c283839dSLandon J. Fuller 		 *
484c283839dSLandon J. Fuller 		 * For example, direct CHAR -> UINT32 coercion would attempt to
485c283839dSLandon J. Fuller 		 * parse the character as a decimal integer, rather than
486c283839dSLandon J. Fuller 		 * promoting the raw UTF8 byte value to a 32-bit value.
487c283839dSLandon J. Fuller 		 */
488c283839dSLandon J. Fuller 		raw_type = bhnd_nvram_raw_type(var_type);
489c283839dSLandon J. Fuller 		error = bhnd_nvram_value_coerce(var_ptr, var_len, raw_type,
490c283839dSLandon J. Fuller 		     u32, &ilen, itype);
491c283839dSLandon J. Fuller 
492c283839dSLandon J. Fuller 		/* Clean up temporary value representation */
493c283839dSLandon J. Fuller 		bhnd_nvram_val_release(&bcm_val);
494c283839dSLandon J. Fuller 
495c283839dSLandon J. Fuller 		/* Report coercion failure */
496c283839dSLandon J. Fuller 		if (error) {
497c283839dSLandon J. Fuller 			BHND_NV_LOG("error promoting %s to %s: %d\n",
498c283839dSLandon J. Fuller 			    bhnd_nvram_type_name(var_type),
499c283839dSLandon J. Fuller 			    bhnd_nvram_type_name(itype), error);
500c283839dSLandon J. Fuller 			return (error);
501c283839dSLandon J. Fuller 		}
502c283839dSLandon J. Fuller 
503c283839dSLandon J. Fuller 		/* Encoded element count must match SPROM's definition */
504c283839dSLandon J. Fuller 		error = bhnd_nvram_value_nelem(u32, ilen, itype, &enc_nelem);
505c283839dSLandon J. Fuller 		if (error)
506c283839dSLandon J. Fuller 			return (error);
507c283839dSLandon J. Fuller 
508c283839dSLandon J. Fuller 		if (enc_nelem != nelem) {
509c283839dSLandon J. Fuller 			const char *type_name;
510c283839dSLandon J. Fuller 
511c283839dSLandon J. Fuller 			type_name = bhnd_nvram_type_name(var_base_type);
512c283839dSLandon J. Fuller 			BHND_NV_LOG("invalid %s property value '%s[%zu]': "
513c283839dSLandon J. Fuller 			    "required %s[%zu]", var->name, type_name,
514c283839dSLandon J. Fuller 			    enc_nelem, type_name, nelem);
515c283839dSLandon J. Fuller 			return (EFTYPE);
516c283839dSLandon J. Fuller 		}
517c283839dSLandon J. Fuller 	}
518c283839dSLandon J. Fuller 
519c283839dSLandon J. Fuller 	/*
520c283839dSLandon J. Fuller 	 * Seek to the start of the variable's SPROM layout definition and
521c283839dSLandon J. Fuller 	 * iterate over all bindings.
522c283839dSLandon J. Fuller 	 */
523c283839dSLandon J. Fuller 	if ((error = bhnd_sprom_opcode_seek(state, entry))) {
524c283839dSLandon J. Fuller 		BHND_NV_LOG("variable seek failed: %d\n", error);
525c283839dSLandon J. Fuller 		return (error);
526c283839dSLandon J. Fuller 	}
527c283839dSLandon J. Fuller 
528c283839dSLandon J. Fuller 	ipos = 0;
529c283839dSLandon J. Fuller 	while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) {
530c283839dSLandon J. Fuller 		bhnd_sprom_opcode_bind	*binding;
531c283839dSLandon J. Fuller 		bhnd_sprom_opcode_var	*binding_var;
532c283839dSLandon J. Fuller 		size_t			 offset;
533c283839dSLandon J. Fuller 		uint32_t		 skip_out_bytes;
534c283839dSLandon J. Fuller 
535c283839dSLandon J. Fuller 		BHND_NV_ASSERT(
536c283839dSLandon J. Fuller 		    state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
537c283839dSLandon J. Fuller 		    ("invalid var state"));
538c283839dSLandon J. Fuller 		BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state"));
539c283839dSLandon J. Fuller 
540c283839dSLandon J. Fuller 		binding_var = &state->var;
541c283839dSLandon J. Fuller 		binding = &state->var.bind;
542c283839dSLandon J. Fuller 
543c283839dSLandon J. Fuller 		/* Calculate output skip bytes for this binding.
544c283839dSLandon J. Fuller 		 *
545c283839dSLandon J. Fuller 		 * Skip directions are defined in terms of decoding, and
546c283839dSLandon J. Fuller 		 * reversed when encoding. */
547c283839dSLandon J. Fuller 		skip_out_bytes = binding->skip_in;
548c283839dSLandon J. Fuller 		error = bhnd_sprom_opcode_apply_scale(state, &skip_out_bytes);
549c283839dSLandon J. Fuller 		if (error)
550c283839dSLandon J. Fuller 			return (error);
551c283839dSLandon J. Fuller 
552c283839dSLandon J. Fuller 		/* Bind */
553c283839dSLandon J. Fuller 		offset = state->offset;
554c283839dSLandon J. Fuller 		for (size_t i = 0; i < binding->count; i++) {
555c283839dSLandon J. Fuller 			if (ipos >= nelem) {
556c283839dSLandon J. Fuller 				BHND_NV_LOG("input skip %u positioned %zu "
557c283839dSLandon J. Fuller 				    "beyond nelem %zu\n", binding->skip_out,
558c283839dSLandon J. Fuller 				    ipos, nelem);
559c283839dSLandon J. Fuller 				return (EINVAL);
560c283839dSLandon J. Fuller 			}
561c283839dSLandon J. Fuller 
562c283839dSLandon J. Fuller 			/* Write next offset */
563c283839dSLandon J. Fuller 			if (io != NULL) {
564c283839dSLandon J. Fuller 				error = bhnd_nvram_sprom_write_offset(var, io,
565c283839dSLandon J. Fuller 				    binding_var->base_type,
566c283839dSLandon J. Fuller 				    offset,
567c283839dSLandon J. Fuller 				    binding_var->mask,
568c283839dSLandon J. Fuller 				    binding_var->shift,
569c283839dSLandon J. Fuller 				    u32[ipos]);
570c283839dSLandon J. Fuller 				if (error)
571c283839dSLandon J. Fuller 					return (error);
572c283839dSLandon J. Fuller 			}
573c283839dSLandon J. Fuller 
574c283839dSLandon J. Fuller 			/* Adjust output position; this was already verified to
575c283839dSLandon J. Fuller 			 * not overflow/underflow during SPROM opcode
576c283839dSLandon J. Fuller 			 * evaluation */
577c283839dSLandon J. Fuller 			if (binding->skip_in_negative) {
578c283839dSLandon J. Fuller 				offset -= skip_out_bytes;
579c283839dSLandon J. Fuller 			} else {
580c283839dSLandon J. Fuller 				offset += skip_out_bytes;
581c283839dSLandon J. Fuller 			}
582c283839dSLandon J. Fuller 
583c283839dSLandon J. Fuller 			/* Skip advancing input if additional bindings are
584c283839dSLandon J. Fuller 			 * required to fully encode intv */
585c283839dSLandon J. Fuller 			if (binding->skip_out == 0)
586c283839dSLandon J. Fuller 				continue;
587c283839dSLandon J. Fuller 
588c283839dSLandon J. Fuller 			/* Advance input position */
589c283839dSLandon J. Fuller 			if (SIZE_MAX - binding->skip_out < ipos) {
590c283839dSLandon J. Fuller 				BHND_NV_LOG("output skip %u would overflow "
591c283839dSLandon J. Fuller 				    "%zu\n", binding->skip_out, ipos);
592c283839dSLandon J. Fuller 				return (EINVAL);
593c283839dSLandon J. Fuller 			}
594c283839dSLandon J. Fuller 
595c283839dSLandon J. Fuller 			ipos += binding->skip_out;
596c283839dSLandon J. Fuller 		}
597c283839dSLandon J. Fuller 	}
598c283839dSLandon J. Fuller 
599c283839dSLandon J. Fuller 	/* Did we iterate all bindings until hitting end of the variable
600c283839dSLandon J. Fuller 	 * definition? */
601c283839dSLandon J. Fuller 	BHND_NV_ASSERT(error != 0, ("loop terminated early"));
602c283839dSLandon J. Fuller 	if (error != ENOENT)
603c283839dSLandon J. Fuller 		return (error);
604c283839dSLandon J. Fuller 
605c283839dSLandon J. Fuller 	return (0);
606c283839dSLandon J. Fuller }
607c283839dSLandon J. Fuller 
608c283839dSLandon J. Fuller static int
bhnd_nvram_sprom_serialize(bhnd_nvram_data_class * cls,bhnd_nvram_plist * props,bhnd_nvram_plist * options,void * outp,size_t * olen)609c283839dSLandon J. Fuller bhnd_nvram_sprom_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
610c283839dSLandon J. Fuller     bhnd_nvram_plist *options, void *outp, size_t *olen)
611c283839dSLandon J. Fuller {
612c283839dSLandon J. Fuller 	bhnd_sprom_opcode_state		 state;
613c283839dSLandon J. Fuller 	struct bhnd_nvram_io		*io;
614c283839dSLandon J. Fuller 	bhnd_nvram_prop			*prop;
615c283839dSLandon J. Fuller 	bhnd_sprom_opcode_idx_entry	*entry;
616c283839dSLandon J. Fuller 	const bhnd_sprom_layout		*layout;
617c283839dSLandon J. Fuller 	size_t				 limit;
618c283839dSLandon J. Fuller 	uint8_t				 crc;
619c283839dSLandon J. Fuller 	uint8_t				 sromrev;
620c283839dSLandon J. Fuller 	int				 error;
621c283839dSLandon J. Fuller 
622c283839dSLandon J. Fuller 	limit = *olen;
623c283839dSLandon J. Fuller 	layout = NULL;
624c283839dSLandon J. Fuller 	io = NULL;
625c283839dSLandon J. Fuller 
626c283839dSLandon J. Fuller 	/* Fetch sromrev property */
627c283839dSLandon J. Fuller 	if (!bhnd_nvram_plist_contains(props, BHND_NVAR_SROMREV)) {
628c283839dSLandon J. Fuller 		BHND_NV_LOG("missing required property: %s\n",
629c283839dSLandon J. Fuller 		    BHND_NVAR_SROMREV);
630c283839dSLandon J. Fuller 		return (EINVAL);
631c283839dSLandon J. Fuller 	}
632c283839dSLandon J. Fuller 
633c283839dSLandon J. Fuller 	error = bhnd_nvram_plist_get_uint8(props, BHND_NVAR_SROMREV, &sromrev);
634c283839dSLandon J. Fuller 	if (error) {
635c283839dSLandon J. Fuller 		BHND_NV_LOG("error reading sromrev property: %d\n", error);
636c283839dSLandon J. Fuller 		return (EFTYPE);
637c283839dSLandon J. Fuller 	}
638c283839dSLandon J. Fuller 
639c283839dSLandon J. Fuller 	/* Find SPROM layout definition */
640c283839dSLandon J. Fuller 	if ((layout = bhnd_nvram_sprom_get_layout(sromrev)) == NULL) {
641c283839dSLandon J. Fuller 		BHND_NV_LOG("unsupported sromrev: %hhu\n", sromrev);
642c283839dSLandon J. Fuller 		return (EFTYPE);
643c283839dSLandon J. Fuller 	}
644c283839dSLandon J. Fuller 
645c283839dSLandon J. Fuller 	/* Provide required size to caller */
646c283839dSLandon J. Fuller 	*olen = layout->size;
647c283839dSLandon J. Fuller 	if (outp == NULL)
648c283839dSLandon J. Fuller 		return (0);
649c283839dSLandon J. Fuller 	else if (limit < *olen)
650c283839dSLandon J. Fuller 		return (ENOMEM);
651c283839dSLandon J. Fuller 
652c283839dSLandon J. Fuller 	/* Initialize SPROM layout interpreter */
653c283839dSLandon J. Fuller 	if ((error = bhnd_sprom_opcode_init(&state, layout))) {
654c283839dSLandon J. Fuller 		BHND_NV_LOG("error initializing opcode state: %d\n", error);
655c283839dSLandon J. Fuller 		return (ENXIO);
656c283839dSLandon J. Fuller 	}
657c283839dSLandon J. Fuller 
658c283839dSLandon J. Fuller 	/* Check for unsupported properties */
659c283839dSLandon J. Fuller 	prop = NULL;
660c283839dSLandon J. Fuller 	while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
661c283839dSLandon J. Fuller 		const char *name;
662c283839dSLandon J. Fuller 
663c283839dSLandon J. Fuller 		/* Fetch the corresponding SPROM layout index entry */
664c283839dSLandon J. Fuller 		name = bhnd_nvram_prop_name(prop);
665c283839dSLandon J. Fuller 		entry = bhnd_sprom_opcode_index_find(&state, name);
666c283839dSLandon J. Fuller 		if (entry == NULL) {
667c283839dSLandon J. Fuller 			BHND_NV_LOG("property '%s' unsupported by sromrev "
668c283839dSLandon J. Fuller 			    "%hhu\n", name, layout->rev);
669c283839dSLandon J. Fuller 			error = EINVAL;
670c283839dSLandon J. Fuller 			goto finished;
671c283839dSLandon J. Fuller 		}
672c283839dSLandon J. Fuller 	}
673c283839dSLandon J. Fuller 
674c283839dSLandon J. Fuller 	/* Zero-initialize output */
675c283839dSLandon J. Fuller 	memset(outp, 0, *olen);
676c283839dSLandon J. Fuller 
677c283839dSLandon J. Fuller 	/* Allocate wrapping I/O context for output buffer */
678c283839dSLandon J. Fuller 	io = bhnd_nvram_ioptr_new(outp, *olen, *olen, BHND_NVRAM_IOPTR_RDWR);
679c283839dSLandon J. Fuller 	if (io == NULL) {
680c283839dSLandon J. Fuller 		error = ENOMEM;
681c283839dSLandon J. Fuller 		goto finished;
682c283839dSLandon J. Fuller 	}
683c283839dSLandon J. Fuller 
684c283839dSLandon J. Fuller 	/*
685c283839dSLandon J. Fuller 	 * Serialize all SPROM variable data.
686c283839dSLandon J. Fuller 	 */
687c283839dSLandon J. Fuller 	entry = NULL;
688c283839dSLandon J. Fuller 	while ((entry = bhnd_sprom_opcode_index_next(&state, entry)) != NULL) {
689c283839dSLandon J. Fuller 		const struct bhnd_nvram_vardefn	*var;
690c283839dSLandon J. Fuller 		bhnd_nvram_val			*val;
691c283839dSLandon J. Fuller 
692c283839dSLandon J. Fuller 		var = bhnd_nvram_get_vardefn(entry->vid);
693c283839dSLandon J. Fuller 		BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
694c283839dSLandon J. Fuller 
695c283839dSLandon J. Fuller 		/* Fetch prop; will be NULL if unavailable */
696c283839dSLandon J. Fuller 		prop = bhnd_nvram_plist_get_prop(props, var->name);
697c283839dSLandon J. Fuller 		if (prop != NULL) {
698c283839dSLandon J. Fuller 			val = bhnd_nvram_prop_val(prop);
699c283839dSLandon J. Fuller 		} else {
700c283839dSLandon J. Fuller 			val = BHND_NVRAM_VAL_NULL;
701c283839dSLandon J. Fuller 		}
702c283839dSLandon J. Fuller 
703c283839dSLandon J. Fuller 		/* Attempt to serialize the property value to the appropriate
704c283839dSLandon J. Fuller 		 * offset within the output buffer */
705c283839dSLandon J. Fuller 		error = bhnd_nvram_sprom_write_var(&state, entry, val, io);
706c283839dSLandon J. Fuller 		if (error) {
707c283839dSLandon J. Fuller 			BHND_NV_LOG("error serializing %s to required type "
708c283839dSLandon J. Fuller 			    "%s: %d\n", var->name,
709c283839dSLandon J. Fuller 			    bhnd_nvram_type_name(var->type), error);
710c283839dSLandon J. Fuller 
711c283839dSLandon J. Fuller 			/* ENOMEM is reserved for signaling that the output
712c283839dSLandon J. Fuller 			 * buffer capacity is insufficient */
713c283839dSLandon J. Fuller 			if (error == ENOMEM)
714c283839dSLandon J. Fuller 				error = EINVAL;
715c283839dSLandon J. Fuller 
716c283839dSLandon J. Fuller 			goto finished;
717c283839dSLandon J. Fuller 		}
718c283839dSLandon J. Fuller 	}
719c283839dSLandon J. Fuller 
720c283839dSLandon J. Fuller 	/*
721c283839dSLandon J. Fuller 	 * Write magic value, if any.
722c283839dSLandon J. Fuller 	 */
723c283839dSLandon J. Fuller 	if (!(layout->flags & SPROM_LAYOUT_MAGIC_NONE)) {
724c283839dSLandon J. Fuller 		uint16_t magic;
725c283839dSLandon J. Fuller 
726c283839dSLandon J. Fuller 		magic = htole16(layout->magic_value);
727c283839dSLandon J. Fuller 		error = bhnd_nvram_io_write(io, layout->magic_offset, &magic,
728c283839dSLandon J. Fuller 		    sizeof(magic));
729c283839dSLandon J. Fuller 		if (error) {
730c283839dSLandon J. Fuller 			BHND_NV_LOG("error writing magic value: %d\n", error);
731c283839dSLandon J. Fuller 			goto finished;
732c283839dSLandon J. Fuller 		}
733c283839dSLandon J. Fuller 	}
734c283839dSLandon J. Fuller 
735c283839dSLandon J. Fuller 	/* Calculate the CRC over all SPROM data, not including the CRC byte. */
736c283839dSLandon J. Fuller 	crc = ~bhnd_nvram_crc8(outp, layout->crc_offset,
737c283839dSLandon J. Fuller 	    BHND_NVRAM_CRC8_INITIAL);
738c283839dSLandon J. Fuller 
739c283839dSLandon J. Fuller 	/* Write the checksum. */
740c283839dSLandon J. Fuller 	error = bhnd_nvram_io_write(io, layout->crc_offset, &crc, sizeof(crc));
741c283839dSLandon J. Fuller 	if (error) {
742c283839dSLandon J. Fuller 		BHND_NV_LOG("error writing CRC value: %d\n", error);
743c283839dSLandon J. Fuller 		goto finished;
744c283839dSLandon J. Fuller 	}
745c283839dSLandon J. Fuller 
746c283839dSLandon J. Fuller 	/*
747c283839dSLandon J. Fuller 	 * Success!
748c283839dSLandon J. Fuller 	 */
749c283839dSLandon J. Fuller 	error = 0;
750c283839dSLandon J. Fuller 
751c283839dSLandon J. Fuller finished:
752c283839dSLandon J. Fuller 	bhnd_sprom_opcode_fini(&state);
753c283839dSLandon J. Fuller 
754c283839dSLandon J. Fuller 	if (io != NULL)
755c283839dSLandon J. Fuller 		bhnd_nvram_io_free(io);
756c283839dSLandon J. Fuller 
757c283839dSLandon J. Fuller 	return (error);
758c283839dSLandon J. Fuller }
759c283839dSLandon J. Fuller 
76077cb4d3eSLandon J. Fuller static int
bhnd_nvram_sprom_new(struct bhnd_nvram_data * nv,struct bhnd_nvram_io * io)76177cb4d3eSLandon J. Fuller bhnd_nvram_sprom_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
76277cb4d3eSLandon J. Fuller {
76377cb4d3eSLandon J. Fuller 	struct bhnd_nvram_sprom	*sp;
76477cb4d3eSLandon J. Fuller 	int			 error;
76577cb4d3eSLandon J. Fuller 
76677cb4d3eSLandon J. Fuller 	sp = (struct bhnd_nvram_sprom *)nv;
76777cb4d3eSLandon J. Fuller 
76877cb4d3eSLandon J. Fuller 	/* Identify the SPROM input data */
769591e79bcSLandon J. Fuller 	if ((error = bhnd_nvram_sprom_ident(io, &sp->layout)))
770591e79bcSLandon J. Fuller 		return (error);
771591e79bcSLandon J. Fuller 
772591e79bcSLandon J. Fuller 	/* Copy SPROM image to our shadow buffer */
773591e79bcSLandon J. Fuller 	sp->data = bhnd_nvram_iobuf_copy_range(io, 0, sp->layout->size);
774591e79bcSLandon J. Fuller 	if (sp->data == NULL)
77577cb4d3eSLandon J. Fuller 		goto failed;
77677cb4d3eSLandon J. Fuller 
77777cb4d3eSLandon J. Fuller 	/* Initialize SPROM binding eval state */
778c283839dSLandon J. Fuller 	if ((error = bhnd_sprom_opcode_init(&sp->state, sp->layout)))
77977cb4d3eSLandon J. Fuller 		goto failed;
78077cb4d3eSLandon J. Fuller 
78177cb4d3eSLandon J. Fuller 	return (0);
78277cb4d3eSLandon J. Fuller 
78377cb4d3eSLandon J. Fuller failed:
78477cb4d3eSLandon J. Fuller 	if (sp->data != NULL)
78577cb4d3eSLandon J. Fuller 		bhnd_nvram_io_free(sp->data);
78677cb4d3eSLandon J. Fuller 
78777cb4d3eSLandon J. Fuller 	return (error);
78877cb4d3eSLandon J. Fuller }
78977cb4d3eSLandon J. Fuller 
79077cb4d3eSLandon J. Fuller static void
bhnd_nvram_sprom_free(struct bhnd_nvram_data * nv)79177cb4d3eSLandon J. Fuller bhnd_nvram_sprom_free(struct bhnd_nvram_data *nv)
79277cb4d3eSLandon J. Fuller {
79377cb4d3eSLandon J. Fuller 	struct bhnd_nvram_sprom *sp = (struct bhnd_nvram_sprom *)nv;
79477cb4d3eSLandon J. Fuller 
795c283839dSLandon J. Fuller 	bhnd_sprom_opcode_fini(&sp->state);
79677cb4d3eSLandon J. Fuller 	bhnd_nvram_io_free(sp->data);
797a7c43ebdSLandon J. Fuller }
798a7c43ebdSLandon J. Fuller 
79977cb4d3eSLandon J. Fuller size_t
bhnd_nvram_sprom_count(struct bhnd_nvram_data * nv)80077cb4d3eSLandon J. Fuller bhnd_nvram_sprom_count(struct bhnd_nvram_data *nv)
80177cb4d3eSLandon J. Fuller {
80277cb4d3eSLandon J. Fuller 	struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv;
80377cb4d3eSLandon J. Fuller 	return (sprom->layout->num_vars);
80477cb4d3eSLandon J. Fuller }
80577cb4d3eSLandon J. Fuller 
806c283839dSLandon J. Fuller static bhnd_nvram_plist *
bhnd_nvram_sprom_options(struct bhnd_nvram_data * nv)807c283839dSLandon J. Fuller bhnd_nvram_sprom_options(struct bhnd_nvram_data *nv)
80877cb4d3eSLandon J. Fuller {
809c283839dSLandon J. Fuller 	return (NULL);
81077cb4d3eSLandon J. Fuller }
81177cb4d3eSLandon J. Fuller 
81277cb4d3eSLandon J. Fuller static uint32_t
bhnd_nvram_sprom_caps(struct bhnd_nvram_data * nv)81377cb4d3eSLandon J. Fuller bhnd_nvram_sprom_caps(struct bhnd_nvram_data *nv)
81477cb4d3eSLandon J. Fuller {
81577cb4d3eSLandon J. Fuller 	return (BHND_NVRAM_DATA_CAP_INDEXED);
81677cb4d3eSLandon J. Fuller }
81777cb4d3eSLandon J. Fuller 
81877cb4d3eSLandon J. Fuller static const char *
bhnd_nvram_sprom_next(struct bhnd_nvram_data * nv,void ** cookiep)81977cb4d3eSLandon J. Fuller bhnd_nvram_sprom_next(struct bhnd_nvram_data *nv, void **cookiep)
82077cb4d3eSLandon J. Fuller {
82177cb4d3eSLandon J. Fuller 	struct bhnd_nvram_sprom		*sp;
822c283839dSLandon J. Fuller 	bhnd_sprom_opcode_idx_entry	*entry;
82377cb4d3eSLandon J. Fuller 	const struct bhnd_nvram_vardefn	*var;
82477cb4d3eSLandon J. Fuller 
82577cb4d3eSLandon J. Fuller 	sp = (struct bhnd_nvram_sprom *)nv;
82677cb4d3eSLandon J. Fuller 
827c283839dSLandon J. Fuller 	/* Find next index entry that is not disabled by virtue of IGNALL1 */
828c283839dSLandon J. Fuller 	entry = *cookiep;
829c283839dSLandon J. Fuller 	while ((entry = bhnd_sprom_opcode_index_next(&sp->state, entry))) {
830c283839dSLandon J. Fuller 		/* Update cookiep and fetch variable definition */
831c283839dSLandon J. Fuller 		*cookiep = entry;
832c283839dSLandon J. Fuller 		var = SPROM_COOKIE_TO_NVRAM_VAR(*cookiep);
8336467a17bSLandon J. Fuller 		BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
83477cb4d3eSLandon J. Fuller 
83577cb4d3eSLandon J. Fuller 		/* We might need to parse the variable's value to determine
83677cb4d3eSLandon J. Fuller 		 * whether it should be treated as unset */
83777cb4d3eSLandon J. Fuller 		if (var->flags & BHND_NVRAM_VF_IGNALL1) {
83877cb4d3eSLandon J. Fuller 			int     error;
83977cb4d3eSLandon J. Fuller 			size_t  len;
84077cb4d3eSLandon J. Fuller 
84177cb4d3eSLandon J. Fuller 			error = bhnd_nvram_sprom_getvar(nv, *cookiep, NULL,
84277cb4d3eSLandon J. Fuller 			    &len, var->type);
84377cb4d3eSLandon J. Fuller 			if (error) {
84477cb4d3eSLandon J. Fuller 				BHND_NV_ASSERT(error == ENOENT, ("unexpected "
84577cb4d3eSLandon J. Fuller 				    "error parsing variable: %d", error));
84677cb4d3eSLandon J. Fuller 				continue;
84777cb4d3eSLandon J. Fuller 			}
84877cb4d3eSLandon J. Fuller 		}
84977cb4d3eSLandon J. Fuller 
85077cb4d3eSLandon J. Fuller 		/* Found! */
85177cb4d3eSLandon J. Fuller 		return (var->name);
85277cb4d3eSLandon J. Fuller 	}
85377cb4d3eSLandon J. Fuller 
85477cb4d3eSLandon J. Fuller 	/* Reached end of index entries */
85577cb4d3eSLandon J. Fuller 	return (NULL);
85677cb4d3eSLandon J. Fuller }
85777cb4d3eSLandon J. Fuller 
85877cb4d3eSLandon J. Fuller static void *
bhnd_nvram_sprom_find(struct bhnd_nvram_data * nv,const char * name)85977cb4d3eSLandon J. Fuller bhnd_nvram_sprom_find(struct bhnd_nvram_data *nv, const char *name)
86077cb4d3eSLandon J. Fuller {
86177cb4d3eSLandon J. Fuller 	struct bhnd_nvram_sprom		*sp;
862c283839dSLandon J. Fuller 	bhnd_sprom_opcode_idx_entry	*entry;
86377cb4d3eSLandon J. Fuller 
86477cb4d3eSLandon J. Fuller 	sp = (struct bhnd_nvram_sprom *)nv;
86577cb4d3eSLandon J. Fuller 
866c283839dSLandon J. Fuller 	entry = bhnd_sprom_opcode_index_find(&sp->state, name);
867c283839dSLandon J. Fuller 	return (entry);
86877cb4d3eSLandon J. Fuller }
86977cb4d3eSLandon J. Fuller 
87077cb4d3eSLandon J. Fuller /**
871c283839dSLandon J. Fuller  * Write @p value of @p type to the SPROM @p data at @p offset, applying
872c283839dSLandon J. Fuller  * @p mask and @p shift, and OR with the existing data.
873c283839dSLandon J. Fuller  *
874c283839dSLandon J. Fuller  * @param var The NVRAM variable definition.
875c283839dSLandon J. Fuller  * @param data The SPROM data to be modified.
876c283839dSLandon J. Fuller  * @param type The type to write at @p offset.
877c283839dSLandon J. Fuller  * @param offset The data offset to be written.
878c283839dSLandon J. Fuller  * @param mask The mask to be applied to @p value after shifting.
879c283839dSLandon J. Fuller  * @param shift The shift to be applied to @p value; if positive, a left
880c283839dSLandon J. Fuller  * shift will be applied, if negative, a right shift (this is the reverse of the
881c283839dSLandon J. Fuller  * decoding behavior)
882c283839dSLandon J. Fuller  * @param value The value to be written. The parsed value will be OR'd with the
883c283839dSLandon J. Fuller  * current contents of @p data at @p offset.
884c283839dSLandon J. Fuller  */
885c283839dSLandon J. Fuller static int
bhnd_nvram_sprom_write_offset(const struct bhnd_nvram_vardefn * var,struct bhnd_nvram_io * data,bhnd_nvram_type type,size_t offset,uint32_t mask,int8_t shift,uint32_t value)886c283839dSLandon J. Fuller bhnd_nvram_sprom_write_offset(const struct bhnd_nvram_vardefn *var,
887c283839dSLandon J. Fuller     struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset,
888c283839dSLandon J. Fuller     uint32_t mask, int8_t shift, uint32_t value)
889c283839dSLandon J. Fuller {
890c283839dSLandon J. Fuller 	union bhnd_nvram_sprom_storage	scratch;
891c283839dSLandon J. Fuller 	int				error;
892c283839dSLandon J. Fuller 
893c283839dSLandon J. Fuller #define	NV_WRITE_INT(_widen, _repr, _swap)	do {		\
894c283839dSLandon J. Fuller 	/* Narrow the 32-bit representation */			\
895c283839dSLandon J. Fuller 	scratch._repr[1] = (_widen)value;			\
896c283839dSLandon J. Fuller 								\
897c283839dSLandon J. Fuller 	/* Shift and mask the new value */			\
898c283839dSLandon J. Fuller 	if (shift > 0)						\
899c283839dSLandon J. Fuller 		scratch._repr[1] <<= shift;			\
900c283839dSLandon J. Fuller 	else if (shift < 0)					\
901c283839dSLandon J. Fuller 		scratch._repr[1] >>= -shift;			\
902c283839dSLandon J. Fuller 	scratch._repr[1] &= mask;				\
903c283839dSLandon J. Fuller 								\
904c283839dSLandon J. Fuller 	/* Swap to output byte order */				\
905c283839dSLandon J. Fuller 	scratch._repr[1] = _swap(scratch._repr[1]);		\
906c283839dSLandon J. Fuller 								\
907c283839dSLandon J. Fuller 	/* Fetch the current value */				\
908c283839dSLandon J. Fuller 	error = bhnd_nvram_io_read(data, offset,		\
909c283839dSLandon J. Fuller 	    &scratch._repr[0], sizeof(scratch._repr[0]));	\
910c283839dSLandon J. Fuller 	if (error) {						\
911c283839dSLandon J. Fuller 		BHND_NV_LOG("error reading %s SPROM offset "	\
912c283839dSLandon J. Fuller 		    "%#zx: %d\n", var->name, offset, error);	\
913c283839dSLandon J. Fuller 		return (EFTYPE);				\
914c283839dSLandon J. Fuller 	}							\
915c283839dSLandon J. Fuller 								\
916c283839dSLandon J. Fuller 	/* Mask and set our new value's bits in the current	\
917c283839dSLandon J. Fuller 	 * value */						\
918c283839dSLandon J. Fuller 	if (shift >= 0)						\
919c283839dSLandon J. Fuller 		scratch._repr[0] &= ~_swap(mask << shift);	\
920c283839dSLandon J. Fuller 	else if (shift < 0)					\
921c283839dSLandon J. Fuller 		scratch._repr[0] &= ~_swap(mask >> (-shift));	\
922c283839dSLandon J. Fuller 	scratch._repr[0] |= scratch._repr[1];			\
923c283839dSLandon J. Fuller 								\
924c283839dSLandon J. Fuller 	/* Perform write */					\
925c283839dSLandon J. Fuller 	error = bhnd_nvram_io_write(data, offset,		\
926c283839dSLandon J. Fuller 	    &scratch._repr[0], sizeof(scratch._repr[0]));	\
927c283839dSLandon J. Fuller 	if (error) {						\
928c283839dSLandon J. Fuller 		BHND_NV_LOG("error writing %s SPROM offset "	\
929c283839dSLandon J. Fuller 		    "%#zx: %d\n", var->name, offset, error);	\
930c283839dSLandon J. Fuller 		return (EFTYPE);				\
931c283839dSLandon J. Fuller 	}							\
932c283839dSLandon J. Fuller } while(0)
933c283839dSLandon J. Fuller 
934c283839dSLandon J. Fuller 	/* Apply mask/shift and widen to a common 32bit representation */
935c283839dSLandon J. Fuller 	switch (type) {
936c283839dSLandon J. Fuller 	case BHND_NVRAM_TYPE_UINT8:
937c283839dSLandon J. Fuller 		NV_WRITE_INT(uint32_t,	u8,	);
938c283839dSLandon J. Fuller 		break;
939c283839dSLandon J. Fuller 	case BHND_NVRAM_TYPE_UINT16:
940c283839dSLandon J. Fuller 		NV_WRITE_INT(uint32_t,	u16,	htole16);
941c283839dSLandon J. Fuller 		break;
942c283839dSLandon J. Fuller 	case BHND_NVRAM_TYPE_UINT32:
943c283839dSLandon J. Fuller 		NV_WRITE_INT(uint32_t,	u32,	htole32);
944c283839dSLandon J. Fuller 		break;
945c283839dSLandon J. Fuller 	case BHND_NVRAM_TYPE_INT8:
946c283839dSLandon J. Fuller 		NV_WRITE_INT(int32_t,	i8,	);
947c283839dSLandon J. Fuller 		break;
948c283839dSLandon J. Fuller 	case BHND_NVRAM_TYPE_INT16:
949c283839dSLandon J. Fuller 		NV_WRITE_INT(int32_t,	i16,	htole16);
950c283839dSLandon J. Fuller 		break;
951c283839dSLandon J. Fuller 	case BHND_NVRAM_TYPE_INT32:
952c283839dSLandon J. Fuller 		NV_WRITE_INT(int32_t,	i32,	htole32);
953c283839dSLandon J. Fuller 		break;
954c283839dSLandon J. Fuller 	case BHND_NVRAM_TYPE_CHAR:
955c283839dSLandon J. Fuller 		NV_WRITE_INT(uint32_t,	u8,	);
956c283839dSLandon J. Fuller 		break;
957c283839dSLandon J. Fuller 	default:
958c283839dSLandon J. Fuller 		BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
959c283839dSLandon J. Fuller 		return (EFTYPE);
960c283839dSLandon J. Fuller 	}
961c283839dSLandon J. Fuller #undef	NV_WRITE_INT
962c283839dSLandon J. Fuller 
963c283839dSLandon J. Fuller 	return (0);
964c283839dSLandon J. Fuller }
965c283839dSLandon J. Fuller 
966c283839dSLandon J. Fuller /**
967c283839dSLandon J. Fuller  * Read the value of @p type from the SPROM @p data at @p offset, apply @p mask
96877cb4d3eSLandon J. Fuller  * and @p shift, and OR with the existing @p value.
96977cb4d3eSLandon J. Fuller  *
970c283839dSLandon J. Fuller  * @param var The NVRAM variable definition.
971c283839dSLandon J. Fuller  * @param data The SPROM data to be decoded.
97277cb4d3eSLandon J. Fuller  * @param type The type to read at @p offset
97377cb4d3eSLandon J. Fuller  * @param offset The data offset to be read.
97477cb4d3eSLandon J. Fuller  * @param mask The mask to be applied to the value read at @p offset.
97577cb4d3eSLandon J. Fuller  * @param shift The shift to be applied after masking; if positive, a right
97677cb4d3eSLandon J. Fuller  * shift will be applied, if negative, a left shift.
97777cb4d3eSLandon J. Fuller  * @param value The read destination; the parsed value will be OR'd with the
97877cb4d3eSLandon J. Fuller  * current contents of @p value.
97977cb4d3eSLandon J. Fuller  */
98077cb4d3eSLandon J. Fuller static int
bhnd_nvram_sprom_read_offset(const struct bhnd_nvram_vardefn * var,struct bhnd_nvram_io * data,bhnd_nvram_type type,size_t offset,uint32_t mask,int8_t shift,uint32_t * value)981c283839dSLandon J. Fuller bhnd_nvram_sprom_read_offset(const struct bhnd_nvram_vardefn *var,
982c283839dSLandon J. Fuller     struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset,
983c283839dSLandon J. Fuller     uint32_t mask, int8_t shift, uint32_t *value)
98477cb4d3eSLandon J. Fuller {
985c283839dSLandon J. Fuller 	union bhnd_nvram_sprom_storage	scratch;
98677cb4d3eSLandon J. Fuller 	int				error;
98777cb4d3eSLandon J. Fuller 
988c283839dSLandon J. Fuller #define	NV_PARSE_INT(_widen, _repr, _swap)		do {	\
989c283839dSLandon J. Fuller 	/* Perform read */					\
990c283839dSLandon J. Fuller 	error = bhnd_nvram_io_read(data, offset,		\
991c283839dSLandon J. Fuller 	    &scratch._repr[0], sizeof(scratch._repr[0]));	\
992c283839dSLandon J. Fuller 	if (error) {						\
993c283839dSLandon J. Fuller 		BHND_NV_LOG("error reading %s SPROM offset "	\
994c283839dSLandon J. Fuller 		    "%#zx: %d\n", var->name, offset, error);	\
995c283839dSLandon J. Fuller 		return (EFTYPE);				\
99677cb4d3eSLandon J. Fuller 	}							\
99777cb4d3eSLandon J. Fuller 								\
998c283839dSLandon J. Fuller 	/* Swap to host byte order */				\
999c283839dSLandon J. Fuller 	scratch._repr[0] = _swap(scratch._repr[0]);		\
1000c283839dSLandon J. Fuller 								\
1001c283839dSLandon J. Fuller 	/* Mask and shift the value */				\
1002c283839dSLandon J. Fuller 	scratch._repr[0] &= mask;				\
1003c283839dSLandon J. Fuller 	if (shift > 0) {					\
1004c283839dSLandon J. Fuller 		scratch. _repr[0] >>= shift;			\
1005c283839dSLandon J. Fuller 	} else if (shift < 0) {					\
1006c283839dSLandon J. Fuller 		scratch. _repr[0] <<= -shift;			\
1007c283839dSLandon J. Fuller 	}							\
1008c283839dSLandon J. Fuller 								\
1009c283839dSLandon J. Fuller 	/* Widen to 32-bit representation and OR with current	\
1010c283839dSLandon J. Fuller 	 * value */						\
1011c283839dSLandon J. Fuller 	(*value) |= (_widen)scratch._repr[0];			\
101277cb4d3eSLandon J. Fuller } while(0)
101377cb4d3eSLandon J. Fuller 
101477cb4d3eSLandon J. Fuller 	/* Apply mask/shift and widen to a common 32bit representation */
101577cb4d3eSLandon J. Fuller 	switch (type) {
101677cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_UINT8:
1017c283839dSLandon J. Fuller 		NV_PARSE_INT(uint32_t,	u8,	);
101877cb4d3eSLandon J. Fuller 		break;
101977cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_UINT16:
1020c283839dSLandon J. Fuller 		NV_PARSE_INT(uint32_t,	u16,	le16toh);
102177cb4d3eSLandon J. Fuller 		break;
102277cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_UINT32:
1023c283839dSLandon J. Fuller 		NV_PARSE_INT(uint32_t,	u32,	le32toh);
102477cb4d3eSLandon J. Fuller 		break;
102577cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_INT8:
1026c283839dSLandon J. Fuller 		NV_PARSE_INT(int32_t,	i8,	);
102777cb4d3eSLandon J. Fuller 		break;
102877cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_INT16:
1029c283839dSLandon J. Fuller 		NV_PARSE_INT(int32_t,	i16,	le16toh);
103077cb4d3eSLandon J. Fuller 		break;
103177cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_INT32:
1032c283839dSLandon J. Fuller 		NV_PARSE_INT(int32_t,	i32,	le32toh);
103377cb4d3eSLandon J. Fuller 		break;
103477cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_CHAR:
1035c283839dSLandon J. Fuller 		NV_PARSE_INT(uint32_t,	u8,	);
103677cb4d3eSLandon J. Fuller 		break;
103777cb4d3eSLandon J. Fuller 	default:
103877cb4d3eSLandon J. Fuller 		BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
103977cb4d3eSLandon J. Fuller 		return (EFTYPE);
104077cb4d3eSLandon J. Fuller 	}
1041c283839dSLandon J. Fuller #undef	NV_PARSE_INT
104277cb4d3eSLandon J. Fuller 
104377cb4d3eSLandon J. Fuller 	return (0);
104477cb4d3eSLandon J. Fuller }
104577cb4d3eSLandon J. Fuller 
104619be09f3SLandon J. Fuller /**
1047591e79bcSLandon J. Fuller  * Read a SPROM variable value from @p io.
1048591e79bcSLandon J. Fuller  *
1049591e79bcSLandon J. Fuller  * @param	state		The SPROM opcode state describing the layout of @p io.
1050591e79bcSLandon J. Fuller  * @param	entry		The variable's SPROM opcode index entry.
1051591e79bcSLandon J. Fuller  * @param	io		The input I/O context.
1052591e79bcSLandon J. Fuller  * @param	storage		Storage to be used with @p val.
1053591e79bcSLandon J. Fuller  * @param[out]	val		Value instance to be initialized with the
1054591e79bcSLandon J. Fuller  *				parsed variable data.
105519be09f3SLandon J. Fuller  *
105619be09f3SLandon J. Fuller  * The returned @p val instance will hold a borrowed reference to @p storage,
105719be09f3SLandon J. Fuller  * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
105819be09f3SLandon J. Fuller  * the lifetime of @p storage.
105919be09f3SLandon J. Fuller  *
106019be09f3SLandon J. Fuller  * The caller is responsible for releasing any allocated value state
106119be09f3SLandon J. Fuller  * via bhnd_nvram_val_release().
106219be09f3SLandon J. Fuller  */
106377cb4d3eSLandon J. Fuller static int
bhnd_nvram_sprom_read_var(struct bhnd_sprom_opcode_state * state,struct bhnd_sprom_opcode_idx_entry * entry,struct bhnd_nvram_io * io,union bhnd_nvram_sprom_storage * storage,bhnd_nvram_val * val)1064591e79bcSLandon J. Fuller bhnd_nvram_sprom_read_var(struct bhnd_sprom_opcode_state *state,
1065591e79bcSLandon J. Fuller     struct bhnd_sprom_opcode_idx_entry *entry, struct bhnd_nvram_io *io,
106619be09f3SLandon J. Fuller     union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
106777cb4d3eSLandon J. Fuller {
106877cb4d3eSLandon J. Fuller 	union bhnd_nvram_sprom_storage	*inp;
1069591e79bcSLandon J. Fuller 	const struct bhnd_nvram_vardefn	*var;
107077cb4d3eSLandon J. Fuller 	bhnd_nvram_type			 var_btype;
1071c283839dSLandon J. Fuller 	uint32_t			 intv;
107277cb4d3eSLandon J. Fuller 	size_t				 ilen, ipos, iwidth;
107377cb4d3eSLandon J. Fuller 	size_t				 nelem;
107477cb4d3eSLandon J. Fuller 	bool				 all_bits_set;
107577cb4d3eSLandon J. Fuller 	int				 error;
107677cb4d3eSLandon J. Fuller 
107777cb4d3eSLandon J. Fuller 	/* Fetch canonical variable definition */
1078591e79bcSLandon J. Fuller 	var = bhnd_nvram_get_vardefn(entry->vid);
1079591e79bcSLandon J. Fuller 	BHND_NV_ASSERT(var != NULL, ("invalid entry"));
108077cb4d3eSLandon J. Fuller 
108177cb4d3eSLandon J. Fuller 	/*
108277cb4d3eSLandon J. Fuller 	 * Fetch the array length from the SPROM variable definition.
108377cb4d3eSLandon J. Fuller 	 *
108477cb4d3eSLandon J. Fuller 	 * This generally be identical to the array length provided by the
108577cb4d3eSLandon J. Fuller 	 * canonical NVRAM variable definition, but some SPROM layouts may
108677cb4d3eSLandon J. Fuller 	 * define a smaller element count.
108777cb4d3eSLandon J. Fuller 	 */
1088591e79bcSLandon J. Fuller 	if ((error = bhnd_sprom_opcode_eval_var(state, entry))) {
108977cb4d3eSLandon J. Fuller 		BHND_NV_LOG("variable evaluation failed: %d\n", error);
109077cb4d3eSLandon J. Fuller 		return (error);
109177cb4d3eSLandon J. Fuller 	}
109277cb4d3eSLandon J. Fuller 
1093591e79bcSLandon J. Fuller 	nelem = state->var.nelem;
109477cb4d3eSLandon J. Fuller 	if (nelem > var->nelem) {
109577cb4d3eSLandon J. Fuller 		BHND_NV_LOG("SPROM array element count %zu cannot be "
109677cb4d3eSLandon J. Fuller 		    "represented by '%s' element count of %hhu\n", nelem,
109777cb4d3eSLandon J. Fuller 		    var->name, var->nelem);
109877cb4d3eSLandon J. Fuller 		return (EFTYPE);
109977cb4d3eSLandon J. Fuller 	}
110077cb4d3eSLandon J. Fuller 
110177cb4d3eSLandon J. Fuller 	/* Fetch the var's base element type */
110277cb4d3eSLandon J. Fuller 	var_btype = bhnd_nvram_base_type(var->type);
110377cb4d3eSLandon J. Fuller 
110477cb4d3eSLandon J. Fuller 	/* Calculate total byte length of the native encoding */
11059be0790dSLandon J. Fuller 	if ((iwidth = bhnd_nvram_value_size(NULL, 0, var_btype, 1)) == 0) {
110677cb4d3eSLandon J. Fuller 		/* SPROM does not use (and we do not support) decoding of
110777cb4d3eSLandon J. Fuller 		 * variable-width data types */
110877cb4d3eSLandon J. Fuller 		BHND_NV_LOG("invalid SPROM data type: %d", var->type);
110977cb4d3eSLandon J. Fuller 		return (EFTYPE);
111077cb4d3eSLandon J. Fuller 	}
111177cb4d3eSLandon J. Fuller 	ilen = nelem * iwidth;
111277cb4d3eSLandon J. Fuller 
111319be09f3SLandon J. Fuller 	/* Decode into our caller's local storage */
111419be09f3SLandon J. Fuller 	inp = storage;
111519be09f3SLandon J. Fuller 	if (ilen > sizeof(*storage)) {
111677cb4d3eSLandon J. Fuller 		BHND_NV_LOG("error decoding '%s', SPROM_ARRAY_MAXLEN "
111777cb4d3eSLandon J. Fuller 		    "incorrect\n", var->name);
111877cb4d3eSLandon J. Fuller 		return (EFTYPE);
111977cb4d3eSLandon J. Fuller 	}
112077cb4d3eSLandon J. Fuller 
112177cb4d3eSLandon J. Fuller 	/* Zero-initialize our decode buffer; any output elements skipped
112277cb4d3eSLandon J. Fuller 	 * during decode should default to zero. */
112377cb4d3eSLandon J. Fuller 	memset(inp, 0, ilen);
112477cb4d3eSLandon J. Fuller 
112577cb4d3eSLandon J. Fuller 	/*
112677cb4d3eSLandon J. Fuller 	 * Decode the SPROM data, iteratively decoding up to nelem values.
112777cb4d3eSLandon J. Fuller 	 */
1128591e79bcSLandon J. Fuller 	if ((error = bhnd_sprom_opcode_seek(state, entry))) {
112977cb4d3eSLandon J. Fuller 		BHND_NV_LOG("variable seek failed: %d\n", error);
113077cb4d3eSLandon J. Fuller 		return (error);
113177cb4d3eSLandon J. Fuller 	}
113277cb4d3eSLandon J. Fuller 
113377cb4d3eSLandon J. Fuller 	ipos = 0;
1134c283839dSLandon J. Fuller 	intv = 0x0;
113577cb4d3eSLandon J. Fuller 	if (var->flags & BHND_NVRAM_VF_IGNALL1)
113677cb4d3eSLandon J. Fuller 		all_bits_set = true;
113777cb4d3eSLandon J. Fuller 	else
113877cb4d3eSLandon J. Fuller 		all_bits_set = false;
1139591e79bcSLandon J. Fuller 	while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) {
1140c283839dSLandon J. Fuller 		bhnd_sprom_opcode_bind	*binding;
1141c283839dSLandon J. Fuller 		bhnd_sprom_opcode_var	*binding_var;
114277cb4d3eSLandon J. Fuller 		bhnd_nvram_type		 intv_type;
114377cb4d3eSLandon J. Fuller 		size_t			 offset;
114477cb4d3eSLandon J. Fuller 		size_t			 nbyte;
114577cb4d3eSLandon J. Fuller 		uint32_t		 skip_in_bytes;
114677cb4d3eSLandon J. Fuller 		void			*ptr;
114777cb4d3eSLandon J. Fuller 
114877cb4d3eSLandon J. Fuller 		BHND_NV_ASSERT(
1149591e79bcSLandon J. Fuller 		    state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
115077cb4d3eSLandon J. Fuller 		    ("invalid var state"));
1151591e79bcSLandon J. Fuller 		BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state"));
115277cb4d3eSLandon J. Fuller 
1153591e79bcSLandon J. Fuller 		binding_var = &state->var;
1154591e79bcSLandon J. Fuller 		binding = &state->var.bind;
115577cb4d3eSLandon J. Fuller 
115677cb4d3eSLandon J. Fuller 		if (ipos >= nelem) {
115777cb4d3eSLandon J. Fuller 			BHND_NV_LOG("output skip %u positioned "
115877cb4d3eSLandon J. Fuller 			    "%zu beyond nelem %zu\n",
115977cb4d3eSLandon J. Fuller 			    binding->skip_out, ipos, nelem);
116077cb4d3eSLandon J. Fuller 			return (EINVAL);
116177cb4d3eSLandon J. Fuller 		}
116277cb4d3eSLandon J. Fuller 
116377cb4d3eSLandon J. Fuller 		/* Calculate input skip bytes for this binding */
116477cb4d3eSLandon J. Fuller 		skip_in_bytes = binding->skip_in;
1165591e79bcSLandon J. Fuller 		error = bhnd_sprom_opcode_apply_scale(state, &skip_in_bytes);
116677cb4d3eSLandon J. Fuller 		if (error)
116777cb4d3eSLandon J. Fuller 			return (error);
116877cb4d3eSLandon J. Fuller 
116977cb4d3eSLandon J. Fuller 		/* Bind */
1170591e79bcSLandon J. Fuller 		offset = state->offset;
117177cb4d3eSLandon J. Fuller 		for (size_t i = 0; i < binding->count; i++) {
117277cb4d3eSLandon J. Fuller 			/* Read the offset value, OR'ing with the current
117377cb4d3eSLandon J. Fuller 			 * value of intv */
1174591e79bcSLandon J. Fuller 			error = bhnd_nvram_sprom_read_offset(var, io,
117577cb4d3eSLandon J. Fuller 			    binding_var->base_type,
117677cb4d3eSLandon J. Fuller 			    offset,
117777cb4d3eSLandon J. Fuller 			    binding_var->mask,
117877cb4d3eSLandon J. Fuller 			    binding_var->shift,
117977cb4d3eSLandon J. Fuller 			    &intv);
118077cb4d3eSLandon J. Fuller 			if (error)
118177cb4d3eSLandon J. Fuller 				return (error);
118277cb4d3eSLandon J. Fuller 
118377cb4d3eSLandon J. Fuller 			/* If IGNALL1, record whether value does not have
118477cb4d3eSLandon J. Fuller 			 * all bits set. */
118577cb4d3eSLandon J. Fuller 			if (var->flags & BHND_NVRAM_VF_IGNALL1 &&
118677cb4d3eSLandon J. Fuller 			    all_bits_set)
118777cb4d3eSLandon J. Fuller 			{
118877cb4d3eSLandon J. Fuller 				uint32_t all1;
118977cb4d3eSLandon J. Fuller 
119077cb4d3eSLandon J. Fuller 				all1 = binding_var->mask;
119177cb4d3eSLandon J. Fuller 				if (binding_var->shift > 0)
119277cb4d3eSLandon J. Fuller 					all1 >>= binding_var->shift;
119377cb4d3eSLandon J. Fuller 				else if (binding_var->shift < 0)
119477cb4d3eSLandon J. Fuller 					all1 <<= -binding_var->shift;
119577cb4d3eSLandon J. Fuller 
1196c283839dSLandon J. Fuller 				if ((intv & all1) != all1)
119777cb4d3eSLandon J. Fuller 					all_bits_set = false;
119877cb4d3eSLandon J. Fuller 			}
119977cb4d3eSLandon J. Fuller 
120077cb4d3eSLandon J. Fuller 			/* Adjust input position; this was already verified to
120177cb4d3eSLandon J. Fuller 			 * not overflow/underflow during SPROM opcode
120277cb4d3eSLandon J. Fuller 			 * evaluation */
120377cb4d3eSLandon J. Fuller 			if (binding->skip_in_negative) {
120477cb4d3eSLandon J. Fuller 				offset -= skip_in_bytes;
120577cb4d3eSLandon J. Fuller 			} else {
120677cb4d3eSLandon J. Fuller 				offset += skip_in_bytes;
120777cb4d3eSLandon J. Fuller 			}
120877cb4d3eSLandon J. Fuller 
120977cb4d3eSLandon J. Fuller 			/* Skip writing to inp if additional bindings are
121077cb4d3eSLandon J. Fuller 			 * required to fully populate intv */
121177cb4d3eSLandon J. Fuller 			if (binding->skip_out == 0)
121277cb4d3eSLandon J. Fuller 				continue;
121377cb4d3eSLandon J. Fuller 
121477cb4d3eSLandon J. Fuller 			/* We use bhnd_nvram_value_coerce() to perform
121577cb4d3eSLandon J. Fuller 			 * overflow-checked coercion from the widened
121677cb4d3eSLandon J. Fuller 			 * uint32/int32 intv value to the requested output
121777cb4d3eSLandon J. Fuller 			 * type */
121877cb4d3eSLandon J. Fuller 			if (bhnd_nvram_is_signed_type(var_btype))
121977cb4d3eSLandon J. Fuller 				intv_type = BHND_NVRAM_TYPE_INT32;
122077cb4d3eSLandon J. Fuller 			else
122177cb4d3eSLandon J. Fuller 				intv_type = BHND_NVRAM_TYPE_UINT32;
122277cb4d3eSLandon J. Fuller 
122377cb4d3eSLandon J. Fuller 			/* Calculate address of the current element output
122477cb4d3eSLandon J. Fuller 			 * position */
122577cb4d3eSLandon J. Fuller 			ptr = (uint8_t *)inp + (iwidth * ipos);
122677cb4d3eSLandon J. Fuller 
122777cb4d3eSLandon J. Fuller 			/* Perform coercion of the array element */
122877cb4d3eSLandon J. Fuller 			nbyte = iwidth;
1229c283839dSLandon J. Fuller 			error = bhnd_nvram_value_coerce(&intv, sizeof(intv),
1230c283839dSLandon J. Fuller 			    intv_type, ptr, &nbyte, var_btype);
123177cb4d3eSLandon J. Fuller 			if (error)
123277cb4d3eSLandon J. Fuller 				return (error);
123377cb4d3eSLandon J. Fuller 
123477cb4d3eSLandon J. Fuller 			/* Clear temporary state */
1235c283839dSLandon J. Fuller 			intv = 0x0;
123677cb4d3eSLandon J. Fuller 
123777cb4d3eSLandon J. Fuller 			/* Advance output position */
123877cb4d3eSLandon J. Fuller 			if (SIZE_MAX - binding->skip_out < ipos) {
123977cb4d3eSLandon J. Fuller 				BHND_NV_LOG("output skip %u would overflow "
124077cb4d3eSLandon J. Fuller 				    "%zu\n", binding->skip_out, ipos);
124177cb4d3eSLandon J. Fuller 				return (EINVAL);
124277cb4d3eSLandon J. Fuller 			}
124377cb4d3eSLandon J. Fuller 
124477cb4d3eSLandon J. Fuller 			ipos += binding->skip_out;
124577cb4d3eSLandon J. Fuller 		}
124677cb4d3eSLandon J. Fuller 	}
124777cb4d3eSLandon J. Fuller 
124877cb4d3eSLandon J. Fuller 	/* Did we iterate all bindings until hitting end of the variable
124977cb4d3eSLandon J. Fuller 	 * definition? */
125077cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT(error != 0, ("loop terminated early"));
125177cb4d3eSLandon J. Fuller 	if (error != ENOENT) {
125277cb4d3eSLandon J. Fuller 		return (error);
125377cb4d3eSLandon J. Fuller 	}
125477cb4d3eSLandon J. Fuller 
125577cb4d3eSLandon J. Fuller 	/* If marked IGNALL1 and all bits are set, treat variable as
125677cb4d3eSLandon J. Fuller 	 * unavailable */
125777cb4d3eSLandon J. Fuller 	if ((var->flags & BHND_NVRAM_VF_IGNALL1) && all_bits_set)
125877cb4d3eSLandon J. Fuller 		return (ENOENT);
125977cb4d3eSLandon J. Fuller 
126019be09f3SLandon J. Fuller 	/* Provide value wrapper */
126119be09f3SLandon J. Fuller 	return (bhnd_nvram_val_init(val, var->fmt, inp, ilen, var->type,
126219be09f3SLandon J. Fuller 	    BHND_NVRAM_VAL_BORROW_DATA));
126319be09f3SLandon J. Fuller }
1264591e79bcSLandon J. Fuller 
1265591e79bcSLandon J. Fuller /**
1266591e79bcSLandon J. Fuller  * Common variable decoding; fetches and decodes variable to @p val,
1267591e79bcSLandon J. Fuller  * using @p storage for actual data storage.
1268591e79bcSLandon J. Fuller  *
1269591e79bcSLandon J. Fuller  * The returned @p val instance will hold a borrowed reference to @p storage,
1270591e79bcSLandon J. Fuller  * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
1271591e79bcSLandon J. Fuller  * the lifetime of @p storage.
1272591e79bcSLandon J. Fuller  *
1273591e79bcSLandon J. Fuller  * The caller is responsible for releasing any allocated value state
1274591e79bcSLandon J. Fuller  * via bhnd_nvram_val_release().
1275591e79bcSLandon J. Fuller  */
1276591e79bcSLandon J. Fuller static int
bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data * nv,void * cookiep,union bhnd_nvram_sprom_storage * storage,bhnd_nvram_val * val)1277591e79bcSLandon J. Fuller bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep,
1278591e79bcSLandon J. Fuller     union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
1279591e79bcSLandon J. Fuller {
1280591e79bcSLandon J. Fuller 	struct bhnd_nvram_sprom		*sp;
1281591e79bcSLandon J. Fuller 	bhnd_sprom_opcode_idx_entry	*entry;
1282acb7423dSJohn Baldwin 	const struct bhnd_nvram_vardefn	*var __diagused;
1283591e79bcSLandon J. Fuller 
1284591e79bcSLandon J. Fuller 	BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
1285591e79bcSLandon J. Fuller 
1286591e79bcSLandon J. Fuller 	sp = (struct bhnd_nvram_sprom *)nv;
1287591e79bcSLandon J. Fuller 	entry = cookiep;
1288591e79bcSLandon J. Fuller 
1289591e79bcSLandon J. Fuller 	/* Fetch canonical variable definition */
1290591e79bcSLandon J. Fuller 	var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
1291591e79bcSLandon J. Fuller 	BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
1292591e79bcSLandon J. Fuller 
1293591e79bcSLandon J. Fuller 	return (bhnd_nvram_sprom_read_var(&sp->state, entry, sp->data, storage,
1294591e79bcSLandon J. Fuller 	    val));
1295591e79bcSLandon J. Fuller }
1296591e79bcSLandon J. Fuller 
129719be09f3SLandon J. Fuller static int
bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data * nv,void * cookiep1,void * cookiep2)129819be09f3SLandon J. Fuller bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
129919be09f3SLandon J. Fuller     void *cookiep2)
130019be09f3SLandon J. Fuller {
1301c283839dSLandon J. Fuller 	struct bhnd_sprom_opcode_idx_entry *e1, *e2;
130219be09f3SLandon J. Fuller 
130319be09f3SLandon J. Fuller 	e1 = cookiep1;
130419be09f3SLandon J. Fuller 	e2 = cookiep2;
130519be09f3SLandon J. Fuller 
130619be09f3SLandon J. Fuller 	/* Use the index entry order; this matches the order of variables
130719be09f3SLandon J. Fuller 	 * returned via bhnd_nvram_sprom_next() */
130819be09f3SLandon J. Fuller 	if (e1 < e2)
130919be09f3SLandon J. Fuller 		return (-1);
131019be09f3SLandon J. Fuller 	else if (e1 > e2)
131119be09f3SLandon J. Fuller 		return (1);
131219be09f3SLandon J. Fuller 
131319be09f3SLandon J. Fuller 	return (0);
131419be09f3SLandon J. Fuller }
131519be09f3SLandon J. Fuller 
131619be09f3SLandon J. Fuller static int
bhnd_nvram_sprom_getvar(struct bhnd_nvram_data * nv,void * cookiep,void * buf,size_t * len,bhnd_nvram_type otype)131719be09f3SLandon J. Fuller bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
131819be09f3SLandon J. Fuller     size_t *len, bhnd_nvram_type otype)
131919be09f3SLandon J. Fuller {
132019be09f3SLandon J. Fuller 	bhnd_nvram_val			val;
132119be09f3SLandon J. Fuller 	union bhnd_nvram_sprom_storage	storage;
132219be09f3SLandon J. Fuller 	int				error;
132319be09f3SLandon J. Fuller 
132419be09f3SLandon J. Fuller 	/* Decode variable to a new value instance */
132519be09f3SLandon J. Fuller 	error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
132677cb4d3eSLandon J. Fuller 	if (error)
132777cb4d3eSLandon J. Fuller 		return (error);
132877cb4d3eSLandon J. Fuller 
132919be09f3SLandon J. Fuller 	/* Perform value coercion */
133077cb4d3eSLandon J. Fuller 	error = bhnd_nvram_val_encode(&val, buf, len, otype);
133177cb4d3eSLandon J. Fuller 
133277cb4d3eSLandon J. Fuller 	/* Clean up */
133377cb4d3eSLandon J. Fuller 	bhnd_nvram_val_release(&val);
133477cb4d3eSLandon J. Fuller 	return (error);
133577cb4d3eSLandon J. Fuller }
133677cb4d3eSLandon J. Fuller 
133719be09f3SLandon J. Fuller static int
bhnd_nvram_sprom_copy_val(struct bhnd_nvram_data * nv,void * cookiep,bhnd_nvram_val ** value)133819be09f3SLandon J. Fuller bhnd_nvram_sprom_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
133919be09f3SLandon J. Fuller     bhnd_nvram_val **value)
134019be09f3SLandon J. Fuller {
134119be09f3SLandon J. Fuller 	bhnd_nvram_val			val;
134219be09f3SLandon J. Fuller 	union bhnd_nvram_sprom_storage	storage;
134319be09f3SLandon J. Fuller 	int				error;
134419be09f3SLandon J. Fuller 
134519be09f3SLandon J. Fuller 	/* Decode variable to a new value instance */
134619be09f3SLandon J. Fuller 	error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
134719be09f3SLandon J. Fuller 	if (error)
134819be09f3SLandon J. Fuller 		return (error);
134919be09f3SLandon J. Fuller 
135019be09f3SLandon J. Fuller 	/* Attempt to copy to heap */
135119be09f3SLandon J. Fuller 	*value = bhnd_nvram_val_copy(&val);
135219be09f3SLandon J. Fuller 	bhnd_nvram_val_release(&val);
135319be09f3SLandon J. Fuller 
135419be09f3SLandon J. Fuller 	if (*value == NULL)
135519be09f3SLandon J. Fuller 		return (ENOMEM);
135619be09f3SLandon J. Fuller 
135719be09f3SLandon J. Fuller 	return (0);
135819be09f3SLandon J. Fuller }
135919be09f3SLandon J. Fuller 
136077cb4d3eSLandon J. Fuller static const void *
bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data * nv,void * cookiep,size_t * len,bhnd_nvram_type * type)136177cb4d3eSLandon J. Fuller bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
136277cb4d3eSLandon J. Fuller     size_t *len, bhnd_nvram_type *type)
136377cb4d3eSLandon J. Fuller {
136477cb4d3eSLandon J. Fuller 	/* Unsupported */
136577cb4d3eSLandon J. Fuller 	return (NULL);
136677cb4d3eSLandon J. Fuller }
136777cb4d3eSLandon J. Fuller 
136877cb4d3eSLandon J. Fuller static const char *
bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data * nv,void * cookiep)136977cb4d3eSLandon J. Fuller bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
137077cb4d3eSLandon J. Fuller {
137177cb4d3eSLandon J. Fuller 	const struct bhnd_nvram_vardefn	*var;
137277cb4d3eSLandon J. Fuller 
137377cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
137477cb4d3eSLandon J. Fuller 
1375c283839dSLandon J. Fuller 	var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
137677cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
137777cb4d3eSLandon J. Fuller 
137877cb4d3eSLandon J. Fuller 	return (var->name);
137977cb4d3eSLandon J. Fuller }
138077cb4d3eSLandon J. Fuller 
138119be09f3SLandon J. Fuller static int
bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data * nv,const char * name,bhnd_nvram_val * value,bhnd_nvram_val ** result)138219be09f3SLandon J. Fuller bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
138319be09f3SLandon J. Fuller     bhnd_nvram_val *value, bhnd_nvram_val **result)
138419be09f3SLandon J. Fuller {
1385c283839dSLandon J. Fuller 	struct bhnd_nvram_sprom		*sp;
1386c283839dSLandon J. Fuller 	const struct bhnd_nvram_vardefn	*var;
1387c283839dSLandon J. Fuller 	bhnd_sprom_opcode_idx_entry	*entry;
1388c283839dSLandon J. Fuller 	bhnd_nvram_val			*spval;
1389c283839dSLandon J. Fuller 	int				 error;
1390c283839dSLandon J. Fuller 
1391c283839dSLandon J. Fuller 	sp = (struct bhnd_nvram_sprom *)nv;
1392c283839dSLandon J. Fuller 
1393c283839dSLandon J. Fuller 	/* Is this an externally immutable variable name? */
1394c283839dSLandon J. Fuller 	if (bhnd_sprom_is_external_immutable(name))
1395c283839dSLandon J. Fuller 		return (EINVAL);
1396c283839dSLandon J. Fuller 
1397c283839dSLandon J. Fuller 	/* Variable must be defined in our SPROM layout */
1398c283839dSLandon J. Fuller 	if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
1399c283839dSLandon J. Fuller 		return (ENOENT);
1400c283839dSLandon J. Fuller 
1401c283839dSLandon J. Fuller 	var = bhnd_nvram_get_vardefn(entry->vid);
1402c283839dSLandon J. Fuller 	BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
1403c283839dSLandon J. Fuller 
1404c283839dSLandon J. Fuller 	/* Value must be convertible to the native variable type */
1405c283839dSLandon J. Fuller 	error = bhnd_nvram_val_convert_new(&spval, var->fmt, value,
1406c283839dSLandon J. Fuller 	    BHND_NVRAM_VAL_DYNAMIC);
1407c283839dSLandon J. Fuller 	if (error)
1408c283839dSLandon J. Fuller 		return (error);
1409c283839dSLandon J. Fuller 
1410c283839dSLandon J. Fuller 	/* Value must be encodeable by our SPROM layout */
1411c283839dSLandon J. Fuller 	error = bhnd_nvram_sprom_write_var(&sp->state, entry, spval, NULL);
1412c283839dSLandon J. Fuller 	if (error) {
1413c283839dSLandon J. Fuller 		bhnd_nvram_val_release(spval);
1414c283839dSLandon J. Fuller 		return (error);
1415c283839dSLandon J. Fuller 	}
1416c283839dSLandon J. Fuller 
1417c283839dSLandon J. Fuller 	/* Success. Transfer our ownership of the converted value to the
1418c283839dSLandon J. Fuller 	 * caller */
1419c283839dSLandon J. Fuller 	*result = spval;
1420c283839dSLandon J. Fuller 	return (0);
142119be09f3SLandon J. Fuller }
142219be09f3SLandon J. Fuller 
142319be09f3SLandon J. Fuller static int
bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data * nv,const char * name)142419be09f3SLandon J. Fuller bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
142519be09f3SLandon J. Fuller {
1426c283839dSLandon J. Fuller 	struct bhnd_nvram_sprom		*sp;
142777cb4d3eSLandon J. Fuller 	const struct bhnd_nvram_vardefn	*var;
1428c283839dSLandon J. Fuller 	bhnd_sprom_opcode_idx_entry	*entry;
142977cb4d3eSLandon J. Fuller 
1430c283839dSLandon J. Fuller 	sp = (struct bhnd_nvram_sprom *)nv;
1431c283839dSLandon J. Fuller 
1432c283839dSLandon J. Fuller 	/* Is this an externally immutable variable name? */
1433c283839dSLandon J. Fuller 	if (bhnd_sprom_is_external_immutable(name))
143477cb4d3eSLandon J. Fuller 		return (EINVAL);
143577cb4d3eSLandon J. Fuller 
1436c283839dSLandon J. Fuller 	/* Variable must be defined in our SPROM layout */
1437c283839dSLandon J. Fuller 	if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
143877cb4d3eSLandon J. Fuller 		return (ENOENT);
143977cb4d3eSLandon J. Fuller 
1440c283839dSLandon J. Fuller 	var = bhnd_nvram_get_vardefn(entry->vid);
14416467a17bSLandon J. Fuller 	BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
1442c283839dSLandon J. Fuller 
1443c283839dSLandon J. Fuller 	/* Variable must be capable of representing a NULL/deleted value.
144477cb4d3eSLandon J. Fuller 	 *
1445c283839dSLandon J. Fuller 	 * Since SPROM's layout is fixed, this requires IGNALL -- if
1446c283839dSLandon J. Fuller 	 * all bits are set, an IGNALL variable is treated as unset. */
1447c283839dSLandon J. Fuller 	if (!(var->flags & BHND_NVRAM_VF_IGNALL1))
144877cb4d3eSLandon J. Fuller 		return (EINVAL);
144977cb4d3eSLandon J. Fuller 
145077cb4d3eSLandon J. Fuller 	return (0);
145177cb4d3eSLandon J. Fuller }
145277cb4d3eSLandon J. Fuller 
1453c283839dSLandon J. Fuller /**
1454c283839dSLandon J. Fuller  * Return true if @p name represents a special immutable variable name
1455c283839dSLandon J. Fuller  * (e.g. sromrev) that cannot be updated in an SPROM existing image.
1456c283839dSLandon J. Fuller  *
1457c283839dSLandon J. Fuller  * @param name The name to check.
1458c283839dSLandon J. Fuller  */
1459c283839dSLandon J. Fuller static bool
bhnd_sprom_is_external_immutable(const char * name)1460c283839dSLandon J. Fuller bhnd_sprom_is_external_immutable(const char *name)
1461c283839dSLandon J. Fuller {
1462c283839dSLandon J. Fuller 	/* The layout revision is immutable and cannot be changed */
1463c283839dSLandon J. Fuller 	if (strcmp(name, BHND_NVAR_SROMREV) == 0)
1464c283839dSLandon J. Fuller 		return (true);
1465c283839dSLandon J. Fuller 
1466c283839dSLandon J. Fuller 	return (false);
146777cb4d3eSLandon J. Fuller }
1468