xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
177cb4d3eSLandon J. Fuller /*-
277cb4d3eSLandon J. Fuller  * Copyright (c) 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/param.h>
3177cb4d3eSLandon J. Fuller #include <sys/endian.h>
3277cb4d3eSLandon J. Fuller 
3377cb4d3eSLandon J. Fuller #ifdef _KERNEL
3477cb4d3eSLandon J. Fuller 
3577cb4d3eSLandon J. Fuller #include <sys/bus.h>
3677cb4d3eSLandon J. Fuller #include <sys/ctype.h>
3777cb4d3eSLandon J. Fuller #include <sys/malloc.h>
3877cb4d3eSLandon J. Fuller #include <sys/systm.h>
3977cb4d3eSLandon J. Fuller 
4077cb4d3eSLandon J. Fuller #else /* !_KERNEL */
4177cb4d3eSLandon J. Fuller 
4277cb4d3eSLandon J. Fuller #include <ctype.h>
4377cb4d3eSLandon J. Fuller #include <stdint.h>
4477cb4d3eSLandon J. Fuller #include <stdio.h>
4577cb4d3eSLandon J. Fuller #include <stdlib.h>
4677cb4d3eSLandon J. Fuller #include <string.h>
4777cb4d3eSLandon J. Fuller 
4877cb4d3eSLandon J. Fuller #endif /* _KERNEL */
4977cb4d3eSLandon J. Fuller 
5077cb4d3eSLandon J. Fuller #include "bhnd_nvram_private.h"
5177cb4d3eSLandon J. Fuller 
5277cb4d3eSLandon J. Fuller #include "bhnd_nvram_datavar.h"
5377cb4d3eSLandon J. Fuller 
5477cb4d3eSLandon J. Fuller #include "bhnd_nvram_data_bcmreg.h"
5577cb4d3eSLandon J. Fuller #include "bhnd_nvram_data_bcmvar.h"
5677cb4d3eSLandon J. Fuller 
5777cb4d3eSLandon J. Fuller /*
5877cb4d3eSLandon J. Fuller  * Broadcom NVRAM data class.
5977cb4d3eSLandon J. Fuller  *
6077cb4d3eSLandon J. Fuller  * The Broadcom NVRAM NUL-delimited ASCII format is used by most
6177cb4d3eSLandon J. Fuller  * Broadcom SoCs.
6277cb4d3eSLandon J. Fuller  *
6377cb4d3eSLandon J. Fuller  * The NVRAM data is encoded as a standard header, followed by series of
6477cb4d3eSLandon J. Fuller  * NUL-terminated 'key=value' strings; the end of the stream is denoted
6577cb4d3eSLandon J. Fuller  * by a single extra NUL character.
6677cb4d3eSLandon J. Fuller  */
6777cb4d3eSLandon J. Fuller 
6877cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm;
6977cb4d3eSLandon J. Fuller 
7077cb4d3eSLandon J. Fuller static struct bhnd_nvram_bcm_hvar	*bhnd_nvram_bcm_gethdrvar(
7177cb4d3eSLandon J. Fuller 					     struct bhnd_nvram_bcm *bcm,
7277cb4d3eSLandon J. Fuller 					     const char *name);
7377cb4d3eSLandon J. Fuller static struct bhnd_nvram_bcm_hvar	*bhnd_nvram_bcm_to_hdrvar(
7477cb4d3eSLandon J. Fuller 					     struct bhnd_nvram_bcm *bcm,
7577cb4d3eSLandon J. Fuller 					     void *cookiep);
7677cb4d3eSLandon J. Fuller static size_t				 bhnd_nvram_bcm_hdrvar_index(
7777cb4d3eSLandon J. Fuller 					     struct bhnd_nvram_bcm *bcm,
7877cb4d3eSLandon J. Fuller 					     struct bhnd_nvram_bcm_hvar *hvar);
7977cb4d3eSLandon J. Fuller /*
8077cb4d3eSLandon J. Fuller  * Set of BCM NVRAM header values that are required to be mirrored in the
8177cb4d3eSLandon J. Fuller  * NVRAM data itself.
8277cb4d3eSLandon J. Fuller  *
8377cb4d3eSLandon J. Fuller  * If they're not included in the parsed NVRAM data, we need to vend the
8477cb4d3eSLandon J. Fuller  * header-parsed values with their appropriate keys, and add them in any
8577cb4d3eSLandon J. Fuller  * updates to the NVRAM data.
8677cb4d3eSLandon J. Fuller  *
8777cb4d3eSLandon J. Fuller  * If they're modified in NVRAM, we need to sync the changes with the
8877cb4d3eSLandon J. Fuller  * the NVRAM header values.
8977cb4d3eSLandon J. Fuller  */
9077cb4d3eSLandon J. Fuller static const struct bhnd_nvram_bcm_hvar bhnd_nvram_bcm_hvars[] = {
9177cb4d3eSLandon J. Fuller 	{
9277cb4d3eSLandon J. Fuller 		.name	= BCM_NVRAM_CFG0_SDRAM_INIT_VAR,
9377cb4d3eSLandon J. Fuller 		.type	= BHND_NVRAM_TYPE_UINT16,
9477cb4d3eSLandon J. Fuller 		.len	= sizeof(uint16_t),
9577cb4d3eSLandon J. Fuller 		.nelem	= 1,
9677cb4d3eSLandon J. Fuller 	},
9777cb4d3eSLandon J. Fuller 	{
9877cb4d3eSLandon J. Fuller 		.name	= BCM_NVRAM_CFG1_SDRAM_CFG_VAR,
9977cb4d3eSLandon J. Fuller 		.type	= BHND_NVRAM_TYPE_UINT16,
10077cb4d3eSLandon J. Fuller 		.len	= sizeof(uint16_t),
10177cb4d3eSLandon J. Fuller 		.nelem	= 1,
10277cb4d3eSLandon J. Fuller 	},
10377cb4d3eSLandon J. Fuller 	{
10477cb4d3eSLandon J. Fuller 		.name	= BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR,
10577cb4d3eSLandon J. Fuller 		.type	= BHND_NVRAM_TYPE_UINT16,
10677cb4d3eSLandon J. Fuller 		.len	= sizeof(uint16_t),
10777cb4d3eSLandon J. Fuller 		.nelem	= 1,
10877cb4d3eSLandon J. Fuller 	},
10977cb4d3eSLandon J. Fuller 	{
11077cb4d3eSLandon J. Fuller 		.name	= BCM_NVRAM_SDRAM_NCDL_VAR,
11177cb4d3eSLandon J. Fuller 		.type	= BHND_NVRAM_TYPE_UINT32,
11277cb4d3eSLandon J. Fuller 		.len	= sizeof(uint32_t),
11377cb4d3eSLandon J. Fuller 		.nelem	= 1,
11477cb4d3eSLandon J. Fuller 	},
11577cb4d3eSLandon J. Fuller };
11677cb4d3eSLandon J. Fuller 
11777cb4d3eSLandon J. Fuller /** BCM NVRAM data class instance */
11877cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm {
11977cb4d3eSLandon J. Fuller 	struct bhnd_nvram_data		 nv;	/**< common instance state */
12077cb4d3eSLandon J. Fuller 	struct bhnd_nvram_io		*data;	/**< backing buffer */
121a7c43ebdSLandon J. Fuller 	bhnd_nvram_plist		*opts;	/**< serialization options */
12277cb4d3eSLandon J. Fuller 
12377cb4d3eSLandon J. Fuller 	/** BCM header values */
12477cb4d3eSLandon J. Fuller 	struct bhnd_nvram_bcm_hvar	 hvars[nitems(bhnd_nvram_bcm_hvars)];
12577cb4d3eSLandon J. Fuller 
12677cb4d3eSLandon J. Fuller 	size_t				 count;	/**< total variable count */
12777cb4d3eSLandon J. Fuller };
12877cb4d3eSLandon J. Fuller 
129c283839dSLandon J. Fuller BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", BHND_NVRAM_DATA_CAP_DEVPATHS,
130c283839dSLandon J. Fuller     sizeof(struct bhnd_nvram_bcm))
13177cb4d3eSLandon J. Fuller 
13277cb4d3eSLandon J. Fuller static int
bhnd_nvram_bcm_probe(struct bhnd_nvram_io * io)13377cb4d3eSLandon J. Fuller bhnd_nvram_bcm_probe(struct bhnd_nvram_io *io)
13477cb4d3eSLandon J. Fuller {
13577cb4d3eSLandon J. Fuller 	struct bhnd_nvram_bcmhdr	hdr;
13677cb4d3eSLandon J. Fuller 	int				error;
13777cb4d3eSLandon J. Fuller 
13877cb4d3eSLandon J. Fuller 	if ((error = bhnd_nvram_io_read(io, 0x0, &hdr, sizeof(hdr))))
13977cb4d3eSLandon J. Fuller 		return (error);
14077cb4d3eSLandon J. Fuller 
14177cb4d3eSLandon J. Fuller 	if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
14277cb4d3eSLandon J. Fuller 		return (ENXIO);
14377cb4d3eSLandon J. Fuller 
144*591e79bcSLandon J. Fuller 	if (le32toh(hdr.size) > bhnd_nvram_io_getsize(io))
145*591e79bcSLandon J. Fuller 		return (ENXIO);
146*591e79bcSLandon J. Fuller 
14777cb4d3eSLandon J. Fuller 	return (BHND_NVRAM_DATA_PROBE_DEFAULT);
14877cb4d3eSLandon J. Fuller }
14977cb4d3eSLandon J. Fuller 
150*591e79bcSLandon J. Fuller /**
151*591e79bcSLandon J. Fuller  * Parser states for bhnd_nvram_bcm_getvar_direct_common().
152*591e79bcSLandon J. Fuller  */
153*591e79bcSLandon J. Fuller typedef enum {
154*591e79bcSLandon J. Fuller 	BCM_PARSE_KEY_START,
155*591e79bcSLandon J. Fuller 	BCM_PARSE_KEY_CONT,
156*591e79bcSLandon J. Fuller 	BCM_PARSE_KEY,
157*591e79bcSLandon J. Fuller 	BCM_PARSE_NEXT_KEY,
158*591e79bcSLandon J. Fuller 	BCM_PARSE_VALUE_START,
159*591e79bcSLandon J. Fuller 	BCM_PARSE_VALUE
160*591e79bcSLandon J. Fuller } bcm_parse_state;
161*591e79bcSLandon J. Fuller 
162*591e79bcSLandon J. Fuller static int
bhnd_nvram_bcm_getvar_direct(struct bhnd_nvram_io * io,const char * name,void * outp,size_t * olen,bhnd_nvram_type otype)163*591e79bcSLandon J. Fuller bhnd_nvram_bcm_getvar_direct(struct bhnd_nvram_io *io, const char *name,
164*591e79bcSLandon J. Fuller     void *outp, size_t *olen, bhnd_nvram_type otype)
165*591e79bcSLandon J. Fuller {
166*591e79bcSLandon J. Fuller 	return (bhnd_nvram_bcm_getvar_direct_common(io, name, outp, olen, otype,
167*591e79bcSLandon J. Fuller 	    true));
168*591e79bcSLandon J. Fuller }
169*591e79bcSLandon J. Fuller 
170*591e79bcSLandon J. Fuller /**
171*591e79bcSLandon J. Fuller  * Common BCM/BCMRAW implementation of bhnd_nvram_getvar_direct().
172*591e79bcSLandon J. Fuller  */
173*591e79bcSLandon J. Fuller int
bhnd_nvram_bcm_getvar_direct_common(struct bhnd_nvram_io * io,const char * name,void * outp,size_t * olen,bhnd_nvram_type otype,bool have_header)174*591e79bcSLandon J. Fuller bhnd_nvram_bcm_getvar_direct_common(struct bhnd_nvram_io *io, const char *name,
175*591e79bcSLandon J. Fuller     void *outp, size_t *olen, bhnd_nvram_type otype, bool have_header)
176*591e79bcSLandon J. Fuller {
177*591e79bcSLandon J. Fuller 	struct bhnd_nvram_bcmhdr	 hdr;
178*591e79bcSLandon J. Fuller 	char				 buf[512];
179*591e79bcSLandon J. Fuller 	bcm_parse_state			 pstate;
180*591e79bcSLandon J. Fuller 	size_t				 limit, offset;
181*591e79bcSLandon J. Fuller 	size_t				 buflen, bufpos;
182*591e79bcSLandon J. Fuller 	size_t				 namelen, namepos;
183*591e79bcSLandon J. Fuller 	size_t				 vlen;
184*591e79bcSLandon J. Fuller 	int				 error;
185*591e79bcSLandon J. Fuller 
186*591e79bcSLandon J. Fuller 	limit = bhnd_nvram_io_getsize(io);
187*591e79bcSLandon J. Fuller 	offset = 0;
188*591e79bcSLandon J. Fuller 
189*591e79bcSLandon J. Fuller 	/* Fetch and validate the header */
190*591e79bcSLandon J. Fuller 	if (have_header) {
191*591e79bcSLandon J. Fuller 		if ((error = bhnd_nvram_io_read(io, offset, &hdr, sizeof(hdr))))
192*591e79bcSLandon J. Fuller 			return (error);
193*591e79bcSLandon J. Fuller 
194*591e79bcSLandon J. Fuller 		if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
195*591e79bcSLandon J. Fuller 			return (ENXIO);
196*591e79bcSLandon J. Fuller 
197*591e79bcSLandon J. Fuller 		offset += sizeof(hdr);
198*591e79bcSLandon J. Fuller 		limit = bhnd_nv_ummin(le32toh(hdr.size), limit);
199*591e79bcSLandon J. Fuller 	}
200*591e79bcSLandon J. Fuller 
201*591e79bcSLandon J. Fuller 	/* Loop our parser until we find the requested variable, or hit EOF */
202*591e79bcSLandon J. Fuller 	pstate = BCM_PARSE_KEY_START;
203*591e79bcSLandon J. Fuller 	buflen = 0;
204*591e79bcSLandon J. Fuller 	bufpos = 0;
205*591e79bcSLandon J. Fuller 	namelen = strlen(name);
206*591e79bcSLandon J. Fuller 	namepos = 0;
207*591e79bcSLandon J. Fuller 	vlen = 0;
208*591e79bcSLandon J. Fuller 
209*591e79bcSLandon J. Fuller 	while ((offset - bufpos) < limit) {
210*591e79bcSLandon J. Fuller 		BHND_NV_ASSERT(bufpos <= buflen,
211*591e79bcSLandon J. Fuller 		    ("buf position invalid (%zu > %zu)", bufpos, buflen));
212*591e79bcSLandon J. Fuller 		BHND_NV_ASSERT(buflen <= sizeof(buf),
213*591e79bcSLandon J. Fuller 		    ("buf length invalid (%zu > %zu", buflen, sizeof(buf)));
214*591e79bcSLandon J. Fuller 
215*591e79bcSLandon J. Fuller 		/* Repopulate our parse buffer? */
216*591e79bcSLandon J. Fuller 		if (buflen - bufpos == 0) {
217*591e79bcSLandon J. Fuller 			BHND_NV_ASSERT(offset < limit, ("offset overrun"));
218*591e79bcSLandon J. Fuller 
219*591e79bcSLandon J. Fuller 			buflen = bhnd_nv_ummin(sizeof(buf), limit - offset);
220*591e79bcSLandon J. Fuller 			bufpos = 0;
221*591e79bcSLandon J. Fuller 
222*591e79bcSLandon J. Fuller 			error = bhnd_nvram_io_read(io, offset, buf, buflen);
223*591e79bcSLandon J. Fuller 			if (error)
224*591e79bcSLandon J. Fuller 				return (error);
225*591e79bcSLandon J. Fuller 
226*591e79bcSLandon J. Fuller 			offset += buflen;
227*591e79bcSLandon J. Fuller 		}
228*591e79bcSLandon J. Fuller 
229*591e79bcSLandon J. Fuller 		switch (pstate) {
230*591e79bcSLandon J. Fuller 		case BCM_PARSE_KEY_START:
231*591e79bcSLandon J. Fuller 			BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!"));
232*591e79bcSLandon J. Fuller 
233*591e79bcSLandon J. Fuller 			/* An extra '\0' denotes NVRAM EOF */
234*591e79bcSLandon J. Fuller 			if (buf[bufpos] == '\0')
235*591e79bcSLandon J. Fuller 				return (ENOENT);
236*591e79bcSLandon J. Fuller 
237*591e79bcSLandon J. Fuller 			/* Reset name matching position */
238*591e79bcSLandon J. Fuller 			namepos = 0;
239*591e79bcSLandon J. Fuller 
240*591e79bcSLandon J. Fuller 			/* Start name matching */
241*591e79bcSLandon J. Fuller 			pstate = BCM_PARSE_KEY_CONT;
242*591e79bcSLandon J. Fuller 			break;
243*591e79bcSLandon J. Fuller 
244*591e79bcSLandon J. Fuller 		case BCM_PARSE_KEY_CONT: {
245*591e79bcSLandon J. Fuller 			size_t navail, nleft;
246*591e79bcSLandon J. Fuller 
247*591e79bcSLandon J. Fuller 			nleft = namelen - namepos;
248*591e79bcSLandon J. Fuller 			navail = bhnd_nv_ummin(buflen - bufpos, nleft);
249*591e79bcSLandon J. Fuller 
250*591e79bcSLandon J. Fuller 			if (strncmp(name+namepos, buf+bufpos, navail) == 0) {
251*591e79bcSLandon J. Fuller 				/* Matched */
252*591e79bcSLandon J. Fuller 				namepos += navail;
253*591e79bcSLandon J. Fuller 				bufpos += navail;
254*591e79bcSLandon J. Fuller 
255*591e79bcSLandon J. Fuller 				/* If we've matched the full variable name,
256*591e79bcSLandon J. Fuller 				 * look for its trailing delimiter */
257*591e79bcSLandon J. Fuller 				if (namepos == namelen)
258*591e79bcSLandon J. Fuller 					pstate = BCM_PARSE_KEY;
259*591e79bcSLandon J. Fuller 			} else {
260*591e79bcSLandon J. Fuller 				/* No match; advance to next entry and restart
261*591e79bcSLandon J. Fuller 				 * name matching */
262*591e79bcSLandon J. Fuller 				pstate = BCM_PARSE_NEXT_KEY;
263*591e79bcSLandon J. Fuller 			}
264*591e79bcSLandon J. Fuller 
265*591e79bcSLandon J. Fuller 			break;
266*591e79bcSLandon J. Fuller 		}
267*591e79bcSLandon J. Fuller 
268*591e79bcSLandon J. Fuller 		case BCM_PARSE_KEY:
269*591e79bcSLandon J. Fuller 			BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!"));
270*591e79bcSLandon J. Fuller 
271*591e79bcSLandon J. Fuller 			if (buf[bufpos] == '=') {
272*591e79bcSLandon J. Fuller 				/* Key fully matched; advance past '=' and
273*591e79bcSLandon J. Fuller 				 * parse the value */
274*591e79bcSLandon J. Fuller 				bufpos++;
275*591e79bcSLandon J. Fuller 				pstate = BCM_PARSE_VALUE_START;
276*591e79bcSLandon J. Fuller 			} else {
277*591e79bcSLandon J. Fuller 				/* No match; advance to next entry and restart
278*591e79bcSLandon J. Fuller 				 * name matching */
279*591e79bcSLandon J. Fuller 				pstate = BCM_PARSE_NEXT_KEY;
280*591e79bcSLandon J. Fuller 			}
281*591e79bcSLandon J. Fuller 
282*591e79bcSLandon J. Fuller 			break;
283*591e79bcSLandon J. Fuller 
284*591e79bcSLandon J. Fuller 		case BCM_PARSE_NEXT_KEY: {
285*591e79bcSLandon J. Fuller 			const char *p;
286*591e79bcSLandon J. Fuller 
287*591e79bcSLandon J. Fuller 			/* Scan for a '\0' terminator */
288*591e79bcSLandon J. Fuller 			p = memchr(buf+bufpos, '\0', buflen - bufpos);
289*591e79bcSLandon J. Fuller 
290*591e79bcSLandon J. Fuller 			if (p != NULL) {
291*591e79bcSLandon J. Fuller 				/* Found entry terminator; restart name
292*591e79bcSLandon J. Fuller 				 * matching at next entry */
293*591e79bcSLandon J. Fuller 				pstate = BCM_PARSE_KEY_START;
294*591e79bcSLandon J. Fuller 				bufpos = (p - buf) + 1 /* skip '\0' */;
295*591e79bcSLandon J. Fuller 			} else {
296*591e79bcSLandon J. Fuller 				/* Consumed full buffer looking for '\0';
297*591e79bcSLandon J. Fuller 				 * force repopulation of the buffer and
298*591e79bcSLandon J. Fuller 				 * retry */
299*591e79bcSLandon J. Fuller 				bufpos = buflen;
300*591e79bcSLandon J. Fuller 			}
301*591e79bcSLandon J. Fuller 
302*591e79bcSLandon J. Fuller 			break;
303*591e79bcSLandon J. Fuller 		}
304*591e79bcSLandon J. Fuller 
305*591e79bcSLandon J. Fuller 		case BCM_PARSE_VALUE_START: {
306*591e79bcSLandon J. Fuller 			const char *p;
307*591e79bcSLandon J. Fuller 
308*591e79bcSLandon J. Fuller 			/* Scan for a '\0' terminator */
309*591e79bcSLandon J. Fuller 			p = memchr(buf+bufpos, '\0', buflen - bufpos);
310*591e79bcSLandon J. Fuller 
311*591e79bcSLandon J. Fuller 			if (p != NULL) {
312*591e79bcSLandon J. Fuller 				/* Found entry terminator; parse the value */
313*591e79bcSLandon J. Fuller 				vlen = p - &buf[bufpos];
314*591e79bcSLandon J. Fuller 				pstate = BCM_PARSE_VALUE;
315*591e79bcSLandon J. Fuller 
316*591e79bcSLandon J. Fuller 			} else if (p == NULL && offset == limit) {
317*591e79bcSLandon J. Fuller 				/* Hit EOF without a terminating '\0';
318*591e79bcSLandon J. Fuller 				 * treat the entry as implicitly terminated */
319*591e79bcSLandon J. Fuller 				vlen = buflen - bufpos;
320*591e79bcSLandon J. Fuller 				pstate = BCM_PARSE_VALUE;
321*591e79bcSLandon J. Fuller 
322*591e79bcSLandon J. Fuller 			} else if (p == NULL && bufpos > 0) {
323*591e79bcSLandon J. Fuller 				size_t	nread;
324*591e79bcSLandon J. Fuller 
325*591e79bcSLandon J. Fuller 				/* Move existing value data to start of
326*591e79bcSLandon J. Fuller 				 * buffer */
327*591e79bcSLandon J. Fuller 				memmove(buf, buf+bufpos, buflen - bufpos);
328*591e79bcSLandon J. Fuller 				buflen = bufpos;
329*591e79bcSLandon J. Fuller 				bufpos = 0;
330*591e79bcSLandon J. Fuller 
331*591e79bcSLandon J. Fuller 				/* Populate full buffer to allow retry of
332*591e79bcSLandon J. Fuller 				 * value parsing */
333*591e79bcSLandon J. Fuller 				nread = bhnd_nv_ummin(sizeof(buf) - buflen,
334*591e79bcSLandon J. Fuller 				    limit - offset);
335*591e79bcSLandon J. Fuller 
336*591e79bcSLandon J. Fuller 				error = bhnd_nvram_io_read(io, offset,
337*591e79bcSLandon J. Fuller 				    buf+buflen, nread);
338*591e79bcSLandon J. Fuller 				if (error)
339*591e79bcSLandon J. Fuller 					return (error);
340*591e79bcSLandon J. Fuller 
341*591e79bcSLandon J. Fuller 				offset += nread;
342*591e79bcSLandon J. Fuller 				buflen += nread;
343*591e79bcSLandon J. Fuller 			} else {
344*591e79bcSLandon J. Fuller 				/* Value exceeds our buffer capacity */
345*591e79bcSLandon J. Fuller 				BHND_NV_LOG("cannot parse value for '%s' "
346*591e79bcSLandon J. Fuller 				    "(exceeds %zu byte limit)\n", name,
347*591e79bcSLandon J. Fuller 				    sizeof(buf));
348*591e79bcSLandon J. Fuller 
349*591e79bcSLandon J. Fuller 				return (ENXIO);
350*591e79bcSLandon J. Fuller 			}
351*591e79bcSLandon J. Fuller 
352*591e79bcSLandon J. Fuller 			break;
353*591e79bcSLandon J. Fuller 		}
354*591e79bcSLandon J. Fuller 
355*591e79bcSLandon J. Fuller 		case BCM_PARSE_VALUE:
356*591e79bcSLandon J. Fuller 			BHND_NV_ASSERT(vlen <= buflen, ("value buf overrun"));
357*591e79bcSLandon J. Fuller 
358*591e79bcSLandon J. Fuller 			return (bhnd_nvram_value_coerce(buf+bufpos, vlen,
359*591e79bcSLandon J. Fuller 			    BHND_NVRAM_TYPE_STRING, outp, olen, otype));
360*591e79bcSLandon J. Fuller 		}
361*591e79bcSLandon J. Fuller 	}
362*591e79bcSLandon J. Fuller 
363*591e79bcSLandon J. Fuller 	/* Variable not found */
364*591e79bcSLandon J. Fuller 	return (ENOENT);
365*591e79bcSLandon J. Fuller }
366*591e79bcSLandon J. Fuller 
367c283839dSLandon J. Fuller static int
bhnd_nvram_bcm_serialize(bhnd_nvram_data_class * cls,bhnd_nvram_plist * props,bhnd_nvram_plist * options,void * outp,size_t * olen)368c283839dSLandon J. Fuller bhnd_nvram_bcm_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
369c283839dSLandon J. Fuller     bhnd_nvram_plist *options, void *outp, size_t *olen)
370c283839dSLandon J. Fuller {
371c283839dSLandon J. Fuller 	struct bhnd_nvram_bcmhdr	 hdr;
372c283839dSLandon J. Fuller 	bhnd_nvram_prop			*prop;
373c283839dSLandon J. Fuller 	size_t				 limit, nbytes;
374c283839dSLandon J. Fuller 	uint32_t			 sdram_ncdl;
375c283839dSLandon J. Fuller 	uint16_t			 sdram_init, sdram_cfg, sdram_refresh;
376c283839dSLandon J. Fuller 	uint8_t				 bcm_ver, crc8;
377c283839dSLandon J. Fuller 	int				 error;
378c283839dSLandon J. Fuller 
379c283839dSLandon J. Fuller 	/* Determine output byte limit */
380c283839dSLandon J. Fuller 	if (outp != NULL)
381c283839dSLandon J. Fuller 		limit = *olen;
382c283839dSLandon J. Fuller 	else
383c283839dSLandon J. Fuller 		limit = 0;
384c283839dSLandon J. Fuller 
385c283839dSLandon J. Fuller 	/* Fetch required header variables */
386c283839dSLandon J. Fuller #define	PROPS_GET_HDRVAR(_name, _dest, _type)	do {			\
387c283839dSLandon J. Fuller 		const char *name = BCM_NVRAM_ ## _name ## _VAR;	\
388c283839dSLandon J. Fuller 		if (!bhnd_nvram_plist_contains(props, name)) {		\
389c283839dSLandon J. Fuller 			BHND_NV_LOG("missing required property: %s\n",	\
390c283839dSLandon J. Fuller 			    name);					\
391c283839dSLandon J. Fuller 			return (EFTYPE);				\
392c283839dSLandon J. Fuller 		}							\
393c283839dSLandon J. Fuller 									\
394c283839dSLandon J. Fuller 		error = bhnd_nvram_plist_get_encoded(props, name,	\
395c283839dSLandon J. Fuller 		    (_dest), sizeof(*(_dest)),				\
396c283839dSLandon J. Fuller 		    BHND_NVRAM_TYPE_ ##_type);				\
397c283839dSLandon J. Fuller 		if (error) {						\
398c283839dSLandon J. Fuller 			BHND_NV_LOG("error reading required header "	\
399c283839dSLandon J. Fuller 			    "%s property: %d\n", name, error);		\
400c283839dSLandon J. Fuller 			return (EFTYPE);				\
401c283839dSLandon J. Fuller 		}							\
402c283839dSLandon J. Fuller } while (0)
403c283839dSLandon J. Fuller 
404c283839dSLandon J. Fuller 	PROPS_GET_HDRVAR(SDRAM_NCDL,		&sdram_ncdl,	UINT32);
405c283839dSLandon J. Fuller 	PROPS_GET_HDRVAR(CFG0_SDRAM_INIT,	&sdram_init,	UINT16);
406c283839dSLandon J. Fuller 	PROPS_GET_HDRVAR(CFG1_SDRAM_CFG,	&sdram_cfg,	UINT16);
407c283839dSLandon J. Fuller 	PROPS_GET_HDRVAR(CFG1_SDRAM_REFRESH,	&sdram_refresh,	UINT16);
408c283839dSLandon J. Fuller 
409c283839dSLandon J. Fuller #undef	PROPS_GET_HDRVAR
410c283839dSLandon J. Fuller 
411c283839dSLandon J. Fuller 	/* Fetch BCM nvram version from options */
412c283839dSLandon J. Fuller 	if (options != NULL &&
413c283839dSLandon J. Fuller 	    bhnd_nvram_plist_contains(options, BCM_NVRAM_ENCODE_OPT_VERSION))
414c283839dSLandon J. Fuller 	{
415c283839dSLandon J. Fuller 		error = bhnd_nvram_plist_get_uint8(options,
416c283839dSLandon J. Fuller 		    BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver);
417c283839dSLandon J. Fuller 		if (error) {
418c283839dSLandon J. Fuller 			BHND_NV_LOG("error reading %s uint8 option value: %d\n",
419c283839dSLandon J. Fuller 			    BCM_NVRAM_ENCODE_OPT_VERSION, error);
420c283839dSLandon J. Fuller 			return (EINVAL);
421c283839dSLandon J. Fuller 		}
422c283839dSLandon J. Fuller 	} else {
423c283839dSLandon J. Fuller 		bcm_ver = BCM_NVRAM_CFG0_VER_DEFAULT;
424c283839dSLandon J. Fuller 	}
425c283839dSLandon J. Fuller 
426c283839dSLandon J. Fuller 	/* Construct our header */
427c283839dSLandon J. Fuller 	hdr = (struct bhnd_nvram_bcmhdr) {
428c283839dSLandon J. Fuller 		.magic = htole32(BCM_NVRAM_MAGIC),
429c283839dSLandon J. Fuller 		.size = 0,
430c283839dSLandon J. Fuller 		.cfg0 = 0,
431c283839dSLandon J. Fuller 		.cfg1 = 0,
432c283839dSLandon J. Fuller 		.sdram_ncdl = htole32(sdram_ncdl)
433c283839dSLandon J. Fuller 	};
434c283839dSLandon J. Fuller 
435c283839dSLandon J. Fuller 	hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, 0x0);
436c283839dSLandon J. Fuller 	hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER, bcm_ver);
437c283839dSLandon J. Fuller 	hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_SDRAM_INIT,
438c283839dSLandon J. Fuller 	    htole16(sdram_init));
439c283839dSLandon J. Fuller 
440c283839dSLandon J. Fuller 	hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_CFG,
441c283839dSLandon J. Fuller 	    htole16(sdram_cfg));
442c283839dSLandon J. Fuller 	hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_REFRESH,
443c283839dSLandon J. Fuller 	    htole16(sdram_refresh));
444c283839dSLandon J. Fuller 
445c283839dSLandon J. Fuller 	/* Write the header */
446c283839dSLandon J. Fuller 	nbytes = sizeof(hdr);
447c283839dSLandon J. Fuller 	if (limit >= nbytes)
448c283839dSLandon J. Fuller 		memcpy(outp, &hdr, sizeof(hdr));
449c283839dSLandon J. Fuller 
450c283839dSLandon J. Fuller 	/* Write all properties */
451c283839dSLandon J. Fuller 	prop = NULL;
452c283839dSLandon J. Fuller 	while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
453c283839dSLandon J. Fuller 		const char	*name;
454c283839dSLandon J. Fuller 		char		*p;
455c283839dSLandon J. Fuller 		size_t		 prop_limit;
456c283839dSLandon J. Fuller 		size_t		 name_len, value_len;
457c283839dSLandon J. Fuller 
458c283839dSLandon J. Fuller 		if (outp == NULL || limit < nbytes) {
459c283839dSLandon J. Fuller 			p = NULL;
460c283839dSLandon J. Fuller 			prop_limit = 0;
461c283839dSLandon J. Fuller 		} else {
462c283839dSLandon J. Fuller 			p = ((char *)outp) + nbytes;
463c283839dSLandon J. Fuller 			prop_limit = limit - nbytes;
464c283839dSLandon J. Fuller 		}
465c283839dSLandon J. Fuller 
466c283839dSLandon J. Fuller 		/* Fetch and write name + '=' to output */
467c283839dSLandon J. Fuller 		name = bhnd_nvram_prop_name(prop);
468c283839dSLandon J. Fuller 		name_len = strlen(name) + 1;
469c283839dSLandon J. Fuller 
470c283839dSLandon J. Fuller 		if (prop_limit > name_len) {
471c283839dSLandon J. Fuller 			memcpy(p, name, name_len - 1);
472c283839dSLandon J. Fuller 			p[name_len - 1] = '=';
473c283839dSLandon J. Fuller 
474c283839dSLandon J. Fuller 			prop_limit -= name_len;
475c283839dSLandon J. Fuller 			p += name_len;
476c283839dSLandon J. Fuller 		} else {
477c283839dSLandon J. Fuller 			prop_limit = 0;
478c283839dSLandon J. Fuller 			p = NULL;
479c283839dSLandon J. Fuller 		}
480c283839dSLandon J. Fuller 
481c283839dSLandon J. Fuller 		/* Advance byte count */
482c283839dSLandon J. Fuller 		if (SIZE_MAX - nbytes < name_len)
483c283839dSLandon J. Fuller 			return (EFTYPE); /* would overflow size_t */
484c283839dSLandon J. Fuller 
485c283839dSLandon J. Fuller 		nbytes += name_len;
486c283839dSLandon J. Fuller 
487c283839dSLandon J. Fuller 		/* Attempt to write NUL-terminated value to output */
488c283839dSLandon J. Fuller 		value_len = prop_limit;
489c283839dSLandon J. Fuller 		error = bhnd_nvram_prop_encode(prop, p, &value_len,
490c283839dSLandon J. Fuller 		    BHND_NVRAM_TYPE_STRING);
491c283839dSLandon J. Fuller 
492c283839dSLandon J. Fuller 		/* If encoding failed for any reason other than ENOMEM (which
493c283839dSLandon J. Fuller 		 * we'll detect and report after encoding all properties),
494c283839dSLandon J. Fuller 		 * return immediately */
495c283839dSLandon J. Fuller 		if (error && error != ENOMEM) {
496c283839dSLandon J. Fuller 			BHND_NV_LOG("error serializing %s to required type "
497c283839dSLandon J. Fuller 			    "%s: %d\n", name,
498c283839dSLandon J. Fuller 			    bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
499c283839dSLandon J. Fuller 			    error);
500c283839dSLandon J. Fuller 			return (error);
501c283839dSLandon J. Fuller 		}
502c283839dSLandon J. Fuller 
503c283839dSLandon J. Fuller 		/* Advance byte count */
504c283839dSLandon J. Fuller 		if (SIZE_MAX - nbytes < value_len)
505c283839dSLandon J. Fuller 			return (EFTYPE); /* would overflow size_t */
506c283839dSLandon J. Fuller 
507c283839dSLandon J. Fuller 		nbytes += value_len;
508c283839dSLandon J. Fuller 	}
509c283839dSLandon J. Fuller 
510c283839dSLandon J. Fuller 	/* Write terminating '\0' */
511c283839dSLandon J. Fuller 	if (limit > nbytes)
512c283839dSLandon J. Fuller 		*((char *)outp + nbytes) = '\0';
513c283839dSLandon J. Fuller 
514c283839dSLandon J. Fuller 	if (nbytes == SIZE_MAX)
515c283839dSLandon J. Fuller 		return (EFTYPE); /* would overflow size_t */
516c283839dSLandon J. Fuller 	else
517c283839dSLandon J. Fuller 		nbytes++;
518c283839dSLandon J. Fuller 
519c283839dSLandon J. Fuller 	/* Update header length; this must fit within the header's 32-bit size
520c283839dSLandon J. Fuller 	 * field */
521c283839dSLandon J. Fuller 	if (nbytes <= UINT32_MAX) {
522c283839dSLandon J. Fuller 		hdr.size = (uint32_t)nbytes;
523c283839dSLandon J. Fuller 	} else {
524c283839dSLandon J. Fuller 		BHND_NV_LOG("size %zu exceeds maximum supported size of %u "
525c283839dSLandon J. Fuller 		    "bytes\n", nbytes, UINT32_MAX);
526c283839dSLandon J. Fuller 		return (EFTYPE);
527c283839dSLandon J. Fuller 	}
528c283839dSLandon J. Fuller 
529c283839dSLandon J. Fuller 	/* Provide required length */
530c283839dSLandon J. Fuller 	*olen = nbytes;
531c283839dSLandon J. Fuller 	if (limit < *olen) {
532c283839dSLandon J. Fuller 		if (outp == NULL)
533c283839dSLandon J. Fuller 			return (0);
534c283839dSLandon J. Fuller 
535c283839dSLandon J. Fuller 		return (ENOMEM);
536c283839dSLandon J. Fuller 	}
537c283839dSLandon J. Fuller 
538c283839dSLandon J. Fuller 	/* Calculate the CRC value */
539c283839dSLandon J. Fuller 	BHND_NV_ASSERT(nbytes >= BCM_NVRAM_CRC_SKIP, ("invalid output size"));
540c283839dSLandon J. Fuller 	crc8 = bhnd_nvram_crc8((uint8_t *)outp + BCM_NVRAM_CRC_SKIP,
541c283839dSLandon J. Fuller 	    nbytes - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
542c283839dSLandon J. Fuller 
543c283839dSLandon J. Fuller 	/* Update CRC and write the finalized header */
544c283839dSLandon J. Fuller 	BHND_NV_ASSERT(nbytes >= sizeof(hdr), ("invalid output size"));
545c283839dSLandon J. Fuller 	hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, crc8);
546c283839dSLandon J. Fuller 	memcpy(outp, &hdr, sizeof(hdr));
547c283839dSLandon J. Fuller 
548c283839dSLandon J. Fuller 	return (0);
549c283839dSLandon J. Fuller }
550c283839dSLandon J. Fuller 
55177cb4d3eSLandon J. Fuller /**
55277cb4d3eSLandon J. Fuller  * Initialize @p bcm with the provided NVRAM data mapped by @p src.
55377cb4d3eSLandon J. Fuller  *
55477cb4d3eSLandon J. Fuller  * @param bcm A newly allocated data instance.
55577cb4d3eSLandon J. Fuller  */
55677cb4d3eSLandon J. Fuller static int
bhnd_nvram_bcm_init(struct bhnd_nvram_bcm * bcm,struct bhnd_nvram_io * src)55777cb4d3eSLandon J. Fuller bhnd_nvram_bcm_init(struct bhnd_nvram_bcm *bcm, struct bhnd_nvram_io *src)
55877cb4d3eSLandon J. Fuller {
55977cb4d3eSLandon J. Fuller 	struct bhnd_nvram_bcmhdr	 hdr;
56077cb4d3eSLandon J. Fuller 	uint8_t				*p;
56177cb4d3eSLandon J. Fuller 	void				*ptr;
56277cb4d3eSLandon J. Fuller 	size_t				 io_offset, io_size;
563a7c43ebdSLandon J. Fuller 	uint8_t				 crc, valid, bcm_ver;
56477cb4d3eSLandon J. Fuller 	int				 error;
56577cb4d3eSLandon J. Fuller 
56677cb4d3eSLandon J. Fuller 	if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr))))
56777cb4d3eSLandon J. Fuller 		return (error);
56877cb4d3eSLandon J. Fuller 
56977cb4d3eSLandon J. Fuller 	if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
57077cb4d3eSLandon J. Fuller 		return (ENXIO);
57177cb4d3eSLandon J. Fuller 
57277cb4d3eSLandon J. Fuller 	/* Fetch the actual NVRAM image size */
57377cb4d3eSLandon J. Fuller 	io_size = le32toh(hdr.size);
57477cb4d3eSLandon J. Fuller 	if (io_size < sizeof(hdr)) {
57577cb4d3eSLandon J. Fuller 		/* The header size must include the header itself */
57677cb4d3eSLandon J. Fuller 		BHND_NV_LOG("corrupt header size: %zu\n", io_size);
57777cb4d3eSLandon J. Fuller 		return (EINVAL);
57877cb4d3eSLandon J. Fuller 	}
57977cb4d3eSLandon J. Fuller 
58077cb4d3eSLandon J. Fuller 	if (io_size > bhnd_nvram_io_getsize(src)) {
58177cb4d3eSLandon J. Fuller 		BHND_NV_LOG("header size %zu exceeds input size %zu\n",
58277cb4d3eSLandon J. Fuller 		    io_size, bhnd_nvram_io_getsize(src));
58377cb4d3eSLandon J. Fuller 		return (EINVAL);
58477cb4d3eSLandon J. Fuller 	}
58577cb4d3eSLandon J. Fuller 
58677cb4d3eSLandon J. Fuller 	/* Allocate a buffer large enough to hold the NVRAM image, and
58777cb4d3eSLandon J. Fuller 	 * an extra EOF-signaling NUL (on the chance it's missing from the
58877cb4d3eSLandon J. Fuller 	 * source data) */
58977cb4d3eSLandon J. Fuller 	if (io_size == SIZE_MAX)
59077cb4d3eSLandon J. Fuller 		return (ENOMEM);
59177cb4d3eSLandon J. Fuller 
59277cb4d3eSLandon J. Fuller 	bcm->data = bhnd_nvram_iobuf_empty(io_size, io_size + 1);
59377cb4d3eSLandon J. Fuller 	if (bcm->data == NULL)
59477cb4d3eSLandon J. Fuller 		return (ENOMEM);
59577cb4d3eSLandon J. Fuller 
59677cb4d3eSLandon J. Fuller 	/* Fetch a pointer into our backing buffer and copy in the
59777cb4d3eSLandon J. Fuller 	 * NVRAM image. */
59877cb4d3eSLandon J. Fuller 	error = bhnd_nvram_io_write_ptr(bcm->data, 0x0, &ptr, io_size, NULL);
59977cb4d3eSLandon J. Fuller 	if (error)
60077cb4d3eSLandon J. Fuller 		return (error);
60177cb4d3eSLandon J. Fuller 
60277cb4d3eSLandon J. Fuller 	p = ptr;
60377cb4d3eSLandon J. Fuller 	if ((error = bhnd_nvram_io_read(src, 0x0, p, io_size)))
60477cb4d3eSLandon J. Fuller 		return (error);
60577cb4d3eSLandon J. Fuller 
60677cb4d3eSLandon J. Fuller 	/* Verify the CRC */
60777cb4d3eSLandon J. Fuller 	valid = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC);
60877cb4d3eSLandon J. Fuller 	crc = bhnd_nvram_crc8(p + BCM_NVRAM_CRC_SKIP,
60977cb4d3eSLandon J. Fuller 	    io_size - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
61077cb4d3eSLandon J. Fuller 
61177cb4d3eSLandon J. Fuller 	if (crc != valid) {
61277cb4d3eSLandon J. Fuller 		BHND_NV_LOG("warning: NVRAM CRC error (crc=%#hhx, "
61377cb4d3eSLandon J. Fuller 		    "expected=%hhx)\n", crc, valid);
61477cb4d3eSLandon J. Fuller 	}
61577cb4d3eSLandon J. Fuller 
61677cb4d3eSLandon J. Fuller 	/* Populate header variable definitions */
61777cb4d3eSLandon J. Fuller #define	BCM_READ_HDR_VAR(_name, _dest, _swap) do {		\
61877cb4d3eSLandon J. Fuller 	struct bhnd_nvram_bcm_hvar *data;				\
61977cb4d3eSLandon J. Fuller 	data = bhnd_nvram_bcm_gethdrvar(bcm, _name ##_VAR);		\
62077cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT(data != NULL,						\
62177cb4d3eSLandon J. Fuller 	    ("no such header variable: " __STRING(_name)));		\
62277cb4d3eSLandon J. Fuller 									\
62377cb4d3eSLandon J. Fuller 									\
62477cb4d3eSLandon J. Fuller 	data->value. _dest = _swap(BCM_NVRAM_GET_BITS(			\
62577cb4d3eSLandon J. Fuller 	    hdr. _name ## _FIELD, _name));				\
62677cb4d3eSLandon J. Fuller } while(0)
62777cb4d3eSLandon J. Fuller 
62877cb4d3eSLandon J. Fuller 	BCM_READ_HDR_VAR(BCM_NVRAM_CFG0_SDRAM_INIT,	u16, le16toh);
62977cb4d3eSLandon J. Fuller 	BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_CFG,	u16, le16toh);
63077cb4d3eSLandon J. Fuller 	BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_REFRESH,	u16, le16toh);
63177cb4d3eSLandon J. Fuller 	BCM_READ_HDR_VAR(BCM_NVRAM_SDRAM_NCDL,		u32, le32toh);
63277cb4d3eSLandon J. Fuller 
63377cb4d3eSLandon J. Fuller 	_Static_assert(nitems(bcm->hvars) == 4, "missing initialization for"
63477cb4d3eSLandon J. Fuller 	    "NVRAM header variable(s)");
63577cb4d3eSLandon J. Fuller 
63677cb4d3eSLandon J. Fuller #undef BCM_READ_HDR_VAR
63777cb4d3eSLandon J. Fuller 
63877cb4d3eSLandon J. Fuller 	/* Process the buffer */
63977cb4d3eSLandon J. Fuller 	bcm->count = 0;
64077cb4d3eSLandon J. Fuller 	io_offset = sizeof(hdr);
64177cb4d3eSLandon J. Fuller 	while (io_offset < io_size) {
64277cb4d3eSLandon J. Fuller 		char		*envp;
64377cb4d3eSLandon J. Fuller 		const char	*name, *value;
64477cb4d3eSLandon J. Fuller 		size_t		 envp_len;
64577cb4d3eSLandon J. Fuller 		size_t		 name_len, value_len;
64677cb4d3eSLandon J. Fuller 
64777cb4d3eSLandon J. Fuller 		/* Parse the key=value string */
64877cb4d3eSLandon J. Fuller 		envp = (char *) (p + io_offset);
64977cb4d3eSLandon J. Fuller 		envp_len = strnlen(envp, io_size - io_offset);
65077cb4d3eSLandon J. Fuller 		error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,
65177cb4d3eSLandon J. Fuller 					     &name_len, &value, &value_len);
65277cb4d3eSLandon J. Fuller 		if (error) {
65377cb4d3eSLandon J. Fuller 			BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",
65477cb4d3eSLandon J. Fuller 			    io_offset, error);
65577cb4d3eSLandon J. Fuller 			return (error);
65677cb4d3eSLandon J. Fuller 		}
65777cb4d3eSLandon J. Fuller 
65877cb4d3eSLandon J. Fuller 		/* Insert a '\0' character, replacing the '=' delimiter and
65977cb4d3eSLandon J. Fuller 		 * allowing us to vend references directly to the variable
66077cb4d3eSLandon J. Fuller 		 * name */
66177cb4d3eSLandon J. Fuller 		*(envp + name_len) = '\0';
66277cb4d3eSLandon J. Fuller 
66377cb4d3eSLandon J. Fuller 		/* Record any NVRAM variables that mirror our header variables.
66477cb4d3eSLandon J. Fuller 		 * This is a brute-force search -- for the amount of data we're
66577cb4d3eSLandon J. Fuller 		 * operating on, it shouldn't be an issue. */
66677cb4d3eSLandon J. Fuller 		for (size_t i = 0; i < nitems(bcm->hvars); i++) {
66777cb4d3eSLandon J. Fuller 			struct bhnd_nvram_bcm_hvar	*hvar;
66877cb4d3eSLandon J. Fuller 			union bhnd_nvram_bcm_hvar_value	 hval;
66977cb4d3eSLandon J. Fuller 			size_t				 hval_len;
67077cb4d3eSLandon J. Fuller 
67177cb4d3eSLandon J. Fuller 			hvar = &bcm->hvars[i];
67277cb4d3eSLandon J. Fuller 
67377cb4d3eSLandon J. Fuller 			/* Already matched? */
67477cb4d3eSLandon J. Fuller 			if (hvar->envp != NULL)
67577cb4d3eSLandon J. Fuller 				continue;
67677cb4d3eSLandon J. Fuller 
67777cb4d3eSLandon J. Fuller 			/* Name matches? */
67877cb4d3eSLandon J. Fuller 			if ((strcmp(name, hvar->name)) != 0)
67977cb4d3eSLandon J. Fuller 				continue;
68077cb4d3eSLandon J. Fuller 
68177cb4d3eSLandon J. Fuller 			/* Save pointer to mirrored envp */
68277cb4d3eSLandon J. Fuller 			hvar->envp = envp;
68377cb4d3eSLandon J. Fuller 
68477cb4d3eSLandon J. Fuller 			/* Check for stale value */
68577cb4d3eSLandon J. Fuller 			hval_len = sizeof(hval);
68677cb4d3eSLandon J. Fuller 			error = bhnd_nvram_value_coerce(value, value_len,
68777cb4d3eSLandon J. Fuller 			    BHND_NVRAM_TYPE_STRING, &hval, &hval_len,
68877cb4d3eSLandon J. Fuller 			    hvar->type);
68977cb4d3eSLandon J. Fuller 			if (error) {
69077cb4d3eSLandon J. Fuller 				/* If parsing fails, we can likely only make
69177cb4d3eSLandon J. Fuller 				 * things worse by trying to synchronize the
69277cb4d3eSLandon J. Fuller 				 * variables */
69377cb4d3eSLandon J. Fuller 				BHND_NV_LOG("error parsing header variable "
69477cb4d3eSLandon J. Fuller 				    "'%s=%s': %d\n", name, value, error);
69577cb4d3eSLandon J. Fuller 			} else if (hval_len != hvar->len) {
69677cb4d3eSLandon J. Fuller 				hvar->stale = true;
69777cb4d3eSLandon J. Fuller 			} else if (memcmp(&hval, &hvar->value, hval_len) != 0) {
69877cb4d3eSLandon J. Fuller 				hvar->stale = true;
69977cb4d3eSLandon J. Fuller 			}
70077cb4d3eSLandon J. Fuller 		}
70177cb4d3eSLandon J. Fuller 
70277cb4d3eSLandon J. Fuller 		/* Seek past the value's terminating '\0' */
70377cb4d3eSLandon J. Fuller 		io_offset += envp_len;
70477cb4d3eSLandon J. Fuller 		if (io_offset == io_size) {
70577cb4d3eSLandon J. Fuller 			BHND_NV_LOG("missing terminating NUL at offset %#zx\n",
70677cb4d3eSLandon J. Fuller 			    io_offset);
70777cb4d3eSLandon J. Fuller 			return (EINVAL);
70877cb4d3eSLandon J. Fuller 		}
70977cb4d3eSLandon J. Fuller 
71077cb4d3eSLandon J. Fuller 		if (*(p + io_offset) != '\0') {
71177cb4d3eSLandon J. Fuller 			BHND_NV_LOG("invalid terminator '%#hhx' at offset "
71277cb4d3eSLandon J. Fuller 			    "%#zx\n", *(p + io_offset), io_offset);
71377cb4d3eSLandon J. Fuller 			return (EINVAL);
71477cb4d3eSLandon J. Fuller 		}
71577cb4d3eSLandon J. Fuller 
71677cb4d3eSLandon J. Fuller 		/* Update variable count */
71777cb4d3eSLandon J. Fuller 		bcm->count++;
71877cb4d3eSLandon J. Fuller 
71977cb4d3eSLandon J. Fuller 		/* Seek to the next record */
72077cb4d3eSLandon J. Fuller 		if (++io_offset == io_size) {
72177cb4d3eSLandon J. Fuller 			char ch;
72277cb4d3eSLandon J. Fuller 
72377cb4d3eSLandon J. Fuller 			/* Hit EOF without finding a terminating NUL
72477cb4d3eSLandon J. Fuller 			 * byte; we need to grow our buffer and append
72577cb4d3eSLandon J. Fuller 			 * it */
72677cb4d3eSLandon J. Fuller 			io_size++;
72777cb4d3eSLandon J. Fuller 			if ((error = bhnd_nvram_io_setsize(bcm->data, io_size)))
72877cb4d3eSLandon J. Fuller 				return (error);
72977cb4d3eSLandon J. Fuller 
73077cb4d3eSLandon J. Fuller 			/* Write NUL byte */
73177cb4d3eSLandon J. Fuller 			ch = '\0';
73277cb4d3eSLandon J. Fuller 			error = bhnd_nvram_io_write(bcm->data, io_size-1, &ch,
73377cb4d3eSLandon J. Fuller 			    sizeof(ch));
73477cb4d3eSLandon J. Fuller 			if (error)
73577cb4d3eSLandon J. Fuller 				return (error);
73677cb4d3eSLandon J. Fuller 		}
73777cb4d3eSLandon J. Fuller 
73877cb4d3eSLandon J. Fuller 		/* Check for explicit EOF (encoded as a single empty NUL
73977cb4d3eSLandon J. Fuller 		 * terminated string) */
74077cb4d3eSLandon J. Fuller 		if (*(p + io_offset) == '\0')
74177cb4d3eSLandon J. Fuller 			break;
74277cb4d3eSLandon J. Fuller 	}
74377cb4d3eSLandon J. Fuller 
74477cb4d3eSLandon J. Fuller 	/* Add non-mirrored header variables to total count variable */
74577cb4d3eSLandon J. Fuller 	for (size_t i = 0; i < nitems(bcm->hvars); i++) {
74677cb4d3eSLandon J. Fuller 		if (bcm->hvars[i].envp == NULL)
74777cb4d3eSLandon J. Fuller 			bcm->count++;
74877cb4d3eSLandon J. Fuller 	}
74977cb4d3eSLandon J. Fuller 
750a7c43ebdSLandon J. Fuller 	/* Populate serialization options from our header */
751a7c43ebdSLandon J. Fuller 	bcm_ver = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER);
752a7c43ebdSLandon J. Fuller 	error = bhnd_nvram_plist_append_bytes(bcm->opts,
753a7c43ebdSLandon J. Fuller 	    BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver, sizeof(bcm_ver),
754a7c43ebdSLandon J. Fuller 	    BHND_NVRAM_TYPE_UINT8);
755a7c43ebdSLandon J. Fuller 	if (error)
756a7c43ebdSLandon J. Fuller 		return (error);
757a7c43ebdSLandon J. Fuller 
75877cb4d3eSLandon J. Fuller 	return (0);
75977cb4d3eSLandon J. Fuller }
76077cb4d3eSLandon J. Fuller 
76177cb4d3eSLandon J. Fuller static int
bhnd_nvram_bcm_new(struct bhnd_nvram_data * nv,struct bhnd_nvram_io * io)76277cb4d3eSLandon J. Fuller bhnd_nvram_bcm_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
76377cb4d3eSLandon J. Fuller {
76477cb4d3eSLandon J. Fuller 	struct bhnd_nvram_bcm	*bcm;
76577cb4d3eSLandon J. Fuller 	int			 error;
76677cb4d3eSLandon J. Fuller 
76777cb4d3eSLandon J. Fuller 	bcm = (struct bhnd_nvram_bcm *)nv;
76877cb4d3eSLandon J. Fuller 
76977cb4d3eSLandon J. Fuller 	/* Populate default BCM mirrored header variable set */
77077cb4d3eSLandon J. Fuller 	_Static_assert(sizeof(bcm->hvars) == sizeof(bhnd_nvram_bcm_hvars),
77177cb4d3eSLandon J. Fuller 	    "hvar declarations must match bhnd_nvram_bcm_hvars template");
77277cb4d3eSLandon J. Fuller 	memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars));
77377cb4d3eSLandon J. Fuller 
774a7c43ebdSLandon J. Fuller 	/* Allocate (empty) option list, to be populated by
775a7c43ebdSLandon J. Fuller 	 * bhnd_nvram_bcm_init() */
776a7c43ebdSLandon J. Fuller 	bcm->opts = bhnd_nvram_plist_new();
777a7c43ebdSLandon J. Fuller 	if (bcm->opts == NULL)
778a7c43ebdSLandon J. Fuller 		return (ENOMEM);
779a7c43ebdSLandon J. Fuller 
78077cb4d3eSLandon J. Fuller 	/* Parse the BCM input data and initialize our backing
78177cb4d3eSLandon J. Fuller 	 * data representation */
78277cb4d3eSLandon J. Fuller 	if ((error = bhnd_nvram_bcm_init(bcm, io))) {
78377cb4d3eSLandon J. Fuller 		bhnd_nvram_bcm_free(nv);
78477cb4d3eSLandon J. Fuller 		return (error);
78577cb4d3eSLandon J. Fuller 	}
78677cb4d3eSLandon J. Fuller 
78777cb4d3eSLandon J. Fuller 	return (0);
78877cb4d3eSLandon J. Fuller }
78977cb4d3eSLandon J. Fuller 
79077cb4d3eSLandon J. Fuller static void
bhnd_nvram_bcm_free(struct bhnd_nvram_data * nv)79177cb4d3eSLandon J. Fuller bhnd_nvram_bcm_free(struct bhnd_nvram_data *nv)
79277cb4d3eSLandon J. Fuller {
79377cb4d3eSLandon J. Fuller 	struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
79477cb4d3eSLandon J. Fuller 
79577cb4d3eSLandon J. Fuller 	if (bcm->data != NULL)
79677cb4d3eSLandon J. Fuller 		bhnd_nvram_io_free(bcm->data);
797a7c43ebdSLandon J. Fuller 
798a7c43ebdSLandon J. Fuller 	if (bcm->opts != NULL)
799a7c43ebdSLandon J. Fuller 		bhnd_nvram_plist_release(bcm->opts);
80077cb4d3eSLandon J. Fuller }
80177cb4d3eSLandon J. Fuller 
80277cb4d3eSLandon J. Fuller size_t
bhnd_nvram_bcm_count(struct bhnd_nvram_data * nv)80377cb4d3eSLandon J. Fuller bhnd_nvram_bcm_count(struct bhnd_nvram_data *nv)
80477cb4d3eSLandon J. Fuller {
80577cb4d3eSLandon J. Fuller 	struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
80677cb4d3eSLandon J. Fuller 	return (bcm->count);
80777cb4d3eSLandon J. Fuller }
80877cb4d3eSLandon J. Fuller 
809a7c43ebdSLandon J. Fuller static bhnd_nvram_plist *
bhnd_nvram_bcm_options(struct bhnd_nvram_data * nv)810a7c43ebdSLandon J. Fuller bhnd_nvram_bcm_options(struct bhnd_nvram_data *nv)
811a7c43ebdSLandon J. Fuller {
812a7c43ebdSLandon J. Fuller 	struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
813a7c43ebdSLandon J. Fuller 	return (bcm->opts);
814a7c43ebdSLandon J. Fuller }
815a7c43ebdSLandon J. Fuller 
81677cb4d3eSLandon J. Fuller static uint32_t
bhnd_nvram_bcm_caps(struct bhnd_nvram_data * nv)81777cb4d3eSLandon J. Fuller bhnd_nvram_bcm_caps(struct bhnd_nvram_data *nv)
81877cb4d3eSLandon J. Fuller {
81977cb4d3eSLandon J. Fuller 	return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
82077cb4d3eSLandon J. Fuller }
82177cb4d3eSLandon J. Fuller 
82277cb4d3eSLandon J. Fuller static const char *
bhnd_nvram_bcm_next(struct bhnd_nvram_data * nv,void ** cookiep)82377cb4d3eSLandon J. Fuller bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep)
82477cb4d3eSLandon J. Fuller {
82577cb4d3eSLandon J. Fuller 	struct bhnd_nvram_bcm		*bcm;
82677cb4d3eSLandon J. Fuller 	struct bhnd_nvram_bcm_hvar	*hvar, *hvar_next;
82777cb4d3eSLandon J. Fuller 	const void			*ptr;
82877cb4d3eSLandon J. Fuller 	const char			*envp, *basep;
82977cb4d3eSLandon J. Fuller 	size_t				 io_size, io_offset;
83077cb4d3eSLandon J. Fuller 	int				 error;
83177cb4d3eSLandon J. Fuller 
83277cb4d3eSLandon J. Fuller 	bcm = (struct bhnd_nvram_bcm *)nv;
83377cb4d3eSLandon J. Fuller 
83477cb4d3eSLandon J. Fuller 	io_offset = sizeof(struct bhnd_nvram_bcmhdr);
83577cb4d3eSLandon J. Fuller 	io_size = bhnd_nvram_io_getsize(bcm->data) - io_offset;
83677cb4d3eSLandon J. Fuller 
83777cb4d3eSLandon J. Fuller 	/* Map backing buffer */
83877cb4d3eSLandon J. Fuller 	error = bhnd_nvram_io_read_ptr(bcm->data, io_offset, &ptr, io_size,
83977cb4d3eSLandon J. Fuller 	    NULL);
84077cb4d3eSLandon J. Fuller 	if (error) {
84177cb4d3eSLandon J. Fuller 		BHND_NV_LOG("error mapping backing buffer: %d\n", error);
84277cb4d3eSLandon J. Fuller 		return (NULL);
84377cb4d3eSLandon J. Fuller 	}
84477cb4d3eSLandon J. Fuller 
84577cb4d3eSLandon J. Fuller 	basep = ptr;
84677cb4d3eSLandon J. Fuller 
84777cb4d3eSLandon J. Fuller 	/* If cookiep pointers into our header variable array, handle as header
84877cb4d3eSLandon J. Fuller 	 * variable iteration. */
84977cb4d3eSLandon J. Fuller 	hvar = bhnd_nvram_bcm_to_hdrvar(bcm, *cookiep);
85077cb4d3eSLandon J. Fuller 	if (hvar != NULL) {
85177cb4d3eSLandon J. Fuller 		size_t idx;
85277cb4d3eSLandon J. Fuller 
85377cb4d3eSLandon J. Fuller 		/* Advance to next entry, if any */
85477cb4d3eSLandon J. Fuller 		idx = bhnd_nvram_bcm_hdrvar_index(bcm, hvar) + 1;
85577cb4d3eSLandon J. Fuller 
85677cb4d3eSLandon J. Fuller 		/* Find the next header-defined variable that isn't defined in
85777cb4d3eSLandon J. Fuller 		 * the NVRAM data, start iteration there */
85877cb4d3eSLandon J. Fuller 		for (size_t i = idx; i < nitems(bcm->hvars); i++) {
85977cb4d3eSLandon J. Fuller 			hvar_next = &bcm->hvars[i];
86077cb4d3eSLandon J. Fuller 			if (hvar_next->envp != NULL && !hvar_next->stale)
86177cb4d3eSLandon J. Fuller 				continue;
86277cb4d3eSLandon J. Fuller 
86377cb4d3eSLandon J. Fuller 			*cookiep = hvar_next;
86477cb4d3eSLandon J. Fuller 			return (hvar_next->name);
86577cb4d3eSLandon J. Fuller 		}
86677cb4d3eSLandon J. Fuller 
86777cb4d3eSLandon J. Fuller 		/* No further header-defined variables; iteration
86877cb4d3eSLandon J. Fuller 		 * complete */
86977cb4d3eSLandon J. Fuller 		return (NULL);
87077cb4d3eSLandon J. Fuller 	}
87177cb4d3eSLandon J. Fuller 
87277cb4d3eSLandon J. Fuller 	/* Handle standard NVRAM data iteration */
87377cb4d3eSLandon J. Fuller 	if (*cookiep == NULL) {
87477cb4d3eSLandon J. Fuller 		/* Start at the first NVRAM data record */
87577cb4d3eSLandon J. Fuller 		envp = basep;
87677cb4d3eSLandon J. Fuller 	} else {
87777cb4d3eSLandon J. Fuller 		/* Seek to next record */
87877cb4d3eSLandon J. Fuller 		envp = *cookiep;
87977cb4d3eSLandon J. Fuller 		envp += strlen(envp) + 1;	/* key + '\0' */
88077cb4d3eSLandon J. Fuller 		envp += strlen(envp) + 1;	/* value + '\0' */
88177cb4d3eSLandon J. Fuller 	}
88277cb4d3eSLandon J. Fuller 
88377cb4d3eSLandon J. Fuller 	/*
88477cb4d3eSLandon J. Fuller 	 * Skip entries that have an existing header variable entry that takes
88577cb4d3eSLandon J. Fuller 	 * precedence over the NVRAM data value.
88677cb4d3eSLandon J. Fuller 	 *
88777cb4d3eSLandon J. Fuller 	 * The header's value will be provided when performing header variable
88877cb4d3eSLandon J. Fuller 	 * iteration
88977cb4d3eSLandon J. Fuller 	 */
89077cb4d3eSLandon J. Fuller 	 while ((size_t)(envp - basep) < io_size && *envp != '\0') {
89177cb4d3eSLandon J. Fuller 		/* Locate corresponding header variable */
89277cb4d3eSLandon J. Fuller 		hvar = NULL;
89377cb4d3eSLandon J. Fuller 		for (size_t i = 0; i < nitems(bcm->hvars); i++) {
89477cb4d3eSLandon J. Fuller 			if (bcm->hvars[i].envp != envp)
89577cb4d3eSLandon J. Fuller 				continue;
89677cb4d3eSLandon J. Fuller 
89777cb4d3eSLandon J. Fuller 			hvar = &bcm->hvars[i];
89877cb4d3eSLandon J. Fuller 			break;
89977cb4d3eSLandon J. Fuller 		}
90077cb4d3eSLandon J. Fuller 
90177cb4d3eSLandon J. Fuller 		/* If no corresponding hvar entry, or the entry does not take
90277cb4d3eSLandon J. Fuller 		 * precedence over this NVRAM value, we can safely return this
90377cb4d3eSLandon J. Fuller 		 * value as-is. */
90477cb4d3eSLandon J. Fuller 		if (hvar == NULL || !hvar->stale)
90577cb4d3eSLandon J. Fuller 			break;
90677cb4d3eSLandon J. Fuller 
90777cb4d3eSLandon J. Fuller 		/* Seek to next record */
90877cb4d3eSLandon J. Fuller 		envp += strlen(envp) + 1;	/* key + '\0' */
90977cb4d3eSLandon J. Fuller 		envp += strlen(envp) + 1;	/* value + '\0' */
91077cb4d3eSLandon J. Fuller 	 }
91177cb4d3eSLandon J. Fuller 
91277cb4d3eSLandon J. Fuller 	/* On NVRAM data EOF, try switching to header variables */
91377cb4d3eSLandon J. Fuller 	if ((size_t)(envp - basep) == io_size || *envp == '\0') {
91477cb4d3eSLandon J. Fuller 		/* Find first valid header variable */
91577cb4d3eSLandon J. Fuller 		for (size_t i = 0; i < nitems(bcm->hvars); i++) {
91677cb4d3eSLandon J. Fuller 			if (bcm->hvars[i].envp != NULL)
91777cb4d3eSLandon J. Fuller 				continue;
91877cb4d3eSLandon J. Fuller 
91977cb4d3eSLandon J. Fuller 			*cookiep = &bcm->hvars[i];
92077cb4d3eSLandon J. Fuller 			return (bcm->hvars[i].name);
92177cb4d3eSLandon J. Fuller 		}
92277cb4d3eSLandon J. Fuller 
92377cb4d3eSLandon J. Fuller 		/* No header variables */
92477cb4d3eSLandon J. Fuller 		return (NULL);
92577cb4d3eSLandon J. Fuller 	}
92677cb4d3eSLandon J. Fuller 
92719be09f3SLandon J. Fuller 	*cookiep = __DECONST(void *, envp);
92877cb4d3eSLandon J. Fuller 	return (envp);
92977cb4d3eSLandon J. Fuller }
93077cb4d3eSLandon J. Fuller 
93177cb4d3eSLandon J. Fuller static void *
bhnd_nvram_bcm_find(struct bhnd_nvram_data * nv,const char * name)93277cb4d3eSLandon J. Fuller bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name)
93377cb4d3eSLandon J. Fuller {
93477cb4d3eSLandon J. Fuller 	return (bhnd_nvram_data_generic_find(nv, name));
93577cb4d3eSLandon J. Fuller }
93677cb4d3eSLandon J. Fuller 
93777cb4d3eSLandon J. Fuller static int
bhnd_nvram_bcm_getvar_order(struct bhnd_nvram_data * nv,void * cookiep1,void * cookiep2)93819be09f3SLandon J. Fuller bhnd_nvram_bcm_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
93919be09f3SLandon J. Fuller     void *cookiep2)
94019be09f3SLandon J. Fuller {
94119be09f3SLandon J. Fuller 	struct bhnd_nvram_bcm		*bcm;
94219be09f3SLandon J. Fuller 	struct bhnd_nvram_bcm_hvar	*hvar1, *hvar2;
94319be09f3SLandon J. Fuller 
94419be09f3SLandon J. Fuller 	bcm = (struct bhnd_nvram_bcm *)nv;
94519be09f3SLandon J. Fuller 
94619be09f3SLandon J. Fuller 	hvar1 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep1);
94719be09f3SLandon J. Fuller 	hvar2 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep2);
94819be09f3SLandon J. Fuller 
94919be09f3SLandon J. Fuller 	/* Header variables are always ordered below any variables defined
95019be09f3SLandon J. Fuller 	 * in the BCM data */
95119be09f3SLandon J. Fuller 	if (hvar1 != NULL && hvar2 == NULL) {
95219be09f3SLandon J. Fuller 		return (1);	/* hvar follows non-hvar */
95319be09f3SLandon J. Fuller 	} else if (hvar1 == NULL && hvar2 != NULL) {
95419be09f3SLandon J. Fuller 		return (-1);	/* non-hvar precedes hvar */
95519be09f3SLandon J. Fuller 	}
95619be09f3SLandon J. Fuller 
95719be09f3SLandon J. Fuller 	/* Otherwise, both cookies are either hvars or non-hvars. We can
95819be09f3SLandon J. Fuller 	 * safely fall back on pointer order, which will provide a correct
95919be09f3SLandon J. Fuller 	 * ordering matching the behavior of bhnd_nvram_data_next() for
96019be09f3SLandon J. Fuller 	 * both cases */
96119be09f3SLandon J. Fuller 	if (cookiep1 < cookiep2)
96219be09f3SLandon J. Fuller 		return (-1);
96319be09f3SLandon J. Fuller 
96419be09f3SLandon J. Fuller 	if (cookiep1 > cookiep2)
96519be09f3SLandon J. Fuller 		return (1);
96619be09f3SLandon J. Fuller 
96719be09f3SLandon J. Fuller 	return (0);
96819be09f3SLandon J. Fuller }
96919be09f3SLandon J. Fuller 
97019be09f3SLandon J. Fuller static int
bhnd_nvram_bcm_getvar(struct bhnd_nvram_data * nv,void * cookiep,void * buf,size_t * len,bhnd_nvram_type type)97177cb4d3eSLandon J. Fuller bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
97277cb4d3eSLandon J. Fuller     size_t *len, bhnd_nvram_type type)
97377cb4d3eSLandon J. Fuller {
97477cb4d3eSLandon J. Fuller 	return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
97577cb4d3eSLandon J. Fuller }
97677cb4d3eSLandon J. Fuller 
97719be09f3SLandon J. Fuller static int
bhnd_nvram_bcm_copy_val(struct bhnd_nvram_data * nv,void * cookiep,bhnd_nvram_val ** value)97819be09f3SLandon J. Fuller bhnd_nvram_bcm_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
97919be09f3SLandon J. Fuller     bhnd_nvram_val **value)
98019be09f3SLandon J. Fuller {
98119be09f3SLandon J. Fuller 	return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
98219be09f3SLandon J. Fuller }
98319be09f3SLandon J. Fuller 
98477cb4d3eSLandon J. Fuller static const void *
bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data * nv,void * cookiep,size_t * len,bhnd_nvram_type * type)98577cb4d3eSLandon J. Fuller bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
98677cb4d3eSLandon J. Fuller     size_t *len, bhnd_nvram_type *type)
98777cb4d3eSLandon J. Fuller {
98877cb4d3eSLandon J. Fuller 	struct bhnd_nvram_bcm		*bcm;
98977cb4d3eSLandon J. Fuller 	struct bhnd_nvram_bcm_hvar	*hvar;
99077cb4d3eSLandon J. Fuller 	const char			*envp;
99177cb4d3eSLandon J. Fuller 
99277cb4d3eSLandon J. Fuller 	bcm = (struct bhnd_nvram_bcm *)nv;
99377cb4d3eSLandon J. Fuller 
99477cb4d3eSLandon J. Fuller 	/* Handle header variables */
99577cb4d3eSLandon J. Fuller 	if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
9969be0790dSLandon J. Fuller 		BHND_NV_ASSERT(bhnd_nvram_value_check_aligned(&hvar->value,
9979be0790dSLandon J. Fuller 		    hvar->len, hvar->type) == 0, ("value misaligned"));
99877cb4d3eSLandon J. Fuller 
99977cb4d3eSLandon J. Fuller 		*type = hvar->type;
100077cb4d3eSLandon J. Fuller 		*len = hvar->len;
100177cb4d3eSLandon J. Fuller 		return (&hvar->value);
100277cb4d3eSLandon J. Fuller 	}
100377cb4d3eSLandon J. Fuller 
100477cb4d3eSLandon J. Fuller 	/* Cookie points to key\0value\0 -- get the value address */
100577cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT(cookiep != NULL, ("NULL cookiep"));
100677cb4d3eSLandon J. Fuller 
100777cb4d3eSLandon J. Fuller 	envp = cookiep;
100877cb4d3eSLandon J. Fuller 	envp += strlen(envp) + 1;	/* key + '\0' */
100977cb4d3eSLandon J. Fuller 	*len = strlen(envp) + 1;	/* value + '\0' */
101077cb4d3eSLandon J. Fuller 	*type = BHND_NVRAM_TYPE_STRING;
101177cb4d3eSLandon J. Fuller 
101277cb4d3eSLandon J. Fuller 	return (envp);
101377cb4d3eSLandon J. Fuller }
101477cb4d3eSLandon J. Fuller 
101577cb4d3eSLandon J. Fuller static const char *
bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data * nv,void * cookiep)101677cb4d3eSLandon J. Fuller bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
101777cb4d3eSLandon J. Fuller {
101877cb4d3eSLandon J. Fuller 	struct bhnd_nvram_bcm		*bcm;
101977cb4d3eSLandon J. Fuller 	struct bhnd_nvram_bcm_hvar	*hvar;
102077cb4d3eSLandon J. Fuller 
102177cb4d3eSLandon J. Fuller 	bcm = (struct bhnd_nvram_bcm *)nv;
102277cb4d3eSLandon J. Fuller 
102377cb4d3eSLandon J. Fuller 	/* Handle header variables */
102477cb4d3eSLandon J. Fuller 	if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
102577cb4d3eSLandon J. Fuller 		return (hvar->name);
102677cb4d3eSLandon J. Fuller 	}
102777cb4d3eSLandon J. Fuller 
102877cb4d3eSLandon J. Fuller 	/* Cookie points to key\0value\0 */
102977cb4d3eSLandon J. Fuller 	return (cookiep);
103077cb4d3eSLandon J. Fuller }
103177cb4d3eSLandon J. Fuller 
103219be09f3SLandon J. Fuller static int
bhnd_nvram_bcm_filter_setvar(struct bhnd_nvram_data * nv,const char * name,bhnd_nvram_val * value,bhnd_nvram_val ** result)103319be09f3SLandon J. Fuller bhnd_nvram_bcm_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
103419be09f3SLandon J. Fuller     bhnd_nvram_val *value, bhnd_nvram_val **result)
103519be09f3SLandon J. Fuller {
103619be09f3SLandon J. Fuller 	bhnd_nvram_val	*str;
103719be09f3SLandon J. Fuller 	int		 error;
103819be09f3SLandon J. Fuller 
103919be09f3SLandon J. Fuller 	/* Name (trimmed of any path prefix) must be valid */
104019be09f3SLandon J. Fuller 	if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
104119be09f3SLandon J. Fuller 		return (EINVAL);
104219be09f3SLandon J. Fuller 
104319be09f3SLandon J. Fuller 	/* Value must be bcm-formatted string */
104419be09f3SLandon J. Fuller 	error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
104519be09f3SLandon J. Fuller 	    value, BHND_NVRAM_VAL_DYNAMIC);
104619be09f3SLandon J. Fuller 	if (error)
104719be09f3SLandon J. Fuller 		return (error);
104819be09f3SLandon J. Fuller 
104919be09f3SLandon J. Fuller 	/* Success. Transfer result ownership to the caller. */
105019be09f3SLandon J. Fuller 	*result = str;
105119be09f3SLandon J. Fuller 	return (0);
105219be09f3SLandon J. Fuller }
105319be09f3SLandon J. Fuller 
105419be09f3SLandon J. Fuller static int
bhnd_nvram_bcm_filter_unsetvar(struct bhnd_nvram_data * nv,const char * name)105519be09f3SLandon J. Fuller bhnd_nvram_bcm_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
105619be09f3SLandon J. Fuller {
105719be09f3SLandon J. Fuller 	/* We permit deletion of any variable */
105819be09f3SLandon J. Fuller 	return (0);
105919be09f3SLandon J. Fuller }
106019be09f3SLandon J. Fuller 
106177cb4d3eSLandon J. Fuller /**
106277cb4d3eSLandon J. Fuller  * Return the internal BCM data reference for a header-defined variable
106377cb4d3eSLandon J. Fuller  * with @p name, or NULL if none exists.
106477cb4d3eSLandon J. Fuller  */
106577cb4d3eSLandon J. Fuller static struct bhnd_nvram_bcm_hvar *
bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm * bcm,const char * name)106677cb4d3eSLandon J. Fuller bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm *bcm, const char *name)
106777cb4d3eSLandon J. Fuller {
106877cb4d3eSLandon J. Fuller 	for (size_t i = 0; i < nitems(bcm->hvars); i++) {
106977cb4d3eSLandon J. Fuller 		if (strcmp(bcm->hvars[i].name, name) == 0)
107077cb4d3eSLandon J. Fuller 			return (&bcm->hvars[i]);
107177cb4d3eSLandon J. Fuller 	}
107277cb4d3eSLandon J. Fuller 
107377cb4d3eSLandon J. Fuller 	/* Not found */
107477cb4d3eSLandon J. Fuller 	return (NULL);
107577cb4d3eSLandon J. Fuller }
107677cb4d3eSLandon J. Fuller 
107777cb4d3eSLandon J. Fuller /**
107877cb4d3eSLandon J. Fuller  * If @p cookiep references a header-defined variable, return the
107977cb4d3eSLandon J. Fuller  * internal BCM data reference. Otherwise, returns NULL.
108077cb4d3eSLandon J. Fuller  */
108177cb4d3eSLandon J. Fuller static struct bhnd_nvram_bcm_hvar *
bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm * bcm,void * cookiep)108277cb4d3eSLandon J. Fuller bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm *bcm, void *cookiep)
108377cb4d3eSLandon J. Fuller {
108477cb4d3eSLandon J. Fuller #ifdef BHND_NVRAM_INVARIANTS
108577cb4d3eSLandon J. Fuller 	uintptr_t base, ptr;
108677cb4d3eSLandon J. Fuller #endif
108777cb4d3eSLandon J. Fuller 
108877cb4d3eSLandon J. Fuller 	/* If the cookie falls within the hvar array, it's a
108977cb4d3eSLandon J. Fuller 	 * header variable cookie */
109077cb4d3eSLandon J. Fuller 	if (nitems(bcm->hvars) == 0)
109177cb4d3eSLandon J. Fuller 		return (NULL);
109277cb4d3eSLandon J. Fuller 
109377cb4d3eSLandon J. Fuller 	if (cookiep < (void *)&bcm->hvars[0])
109477cb4d3eSLandon J. Fuller 		return (NULL);
109577cb4d3eSLandon J. Fuller 
109677cb4d3eSLandon J. Fuller 	if (cookiep > (void *)&bcm->hvars[nitems(bcm->hvars)-1])
109777cb4d3eSLandon J. Fuller 		return (NULL);
109877cb4d3eSLandon J. Fuller 
109977cb4d3eSLandon J. Fuller #ifdef BHND_NVRAM_INVARIANTS
110077cb4d3eSLandon J. Fuller 	base = (uintptr_t)bcm->hvars;
110177cb4d3eSLandon J. Fuller 	ptr = (uintptr_t)cookiep;
110277cb4d3eSLandon J. Fuller 
110377cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT((ptr - base) % sizeof(bcm->hvars[0]) == 0,
110477cb4d3eSLandon J. Fuller 	    ("misaligned hvar pointer %p/%p", cookiep, bcm->hvars));
110577cb4d3eSLandon J. Fuller #endif /* INVARIANTS */
110677cb4d3eSLandon J. Fuller 
110777cb4d3eSLandon J. Fuller 	return ((struct bhnd_nvram_bcm_hvar *)cookiep);
110877cb4d3eSLandon J. Fuller }
110977cb4d3eSLandon J. Fuller 
111077cb4d3eSLandon J. Fuller /**
111177cb4d3eSLandon J. Fuller  * Return the index of @p hdrvar within @p bcm's backing hvars array.
111277cb4d3eSLandon J. Fuller  */
111377cb4d3eSLandon J. Fuller static size_t
bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm * bcm,struct bhnd_nvram_bcm_hvar * hdrvar)111477cb4d3eSLandon J. Fuller bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm *bcm,
111577cb4d3eSLandon J. Fuller     struct bhnd_nvram_bcm_hvar *hdrvar)
111677cb4d3eSLandon J. Fuller {
111777cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT(bhnd_nvram_bcm_to_hdrvar(bcm, (void *)hdrvar) != NULL,
111877cb4d3eSLandon J. Fuller 	    ("%p is not a valid hdrvar reference", hdrvar));
111977cb4d3eSLandon J. Fuller 
112077cb4d3eSLandon J. Fuller 	return (hdrvar - &bcm->hvars[0]);
112177cb4d3eSLandon J. Fuller }
1122