xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
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/cdefs.h>
3177cb4d3eSLandon J. Fuller #ifdef _KERNEL
3277cb4d3eSLandon J. Fuller #include <sys/param.h>
3377cb4d3eSLandon J. Fuller #include <sys/ctype.h>
34*91898857SMark Johnston #include <sys/limits.h>
3577cb4d3eSLandon J. Fuller #include <sys/malloc.h>
3677cb4d3eSLandon J. Fuller #include <sys/systm.h>
3777cb4d3eSLandon J. Fuller #else /* !_KERNEL */
3877cb4d3eSLandon J. Fuller #include <ctype.h>
3977cb4d3eSLandon J. Fuller #include <errno.h>
4077cb4d3eSLandon J. Fuller #include <stdint.h>
4177cb4d3eSLandon J. Fuller #include <stdio.h>
4277cb4d3eSLandon J. Fuller #include <stdlib.h>
4377cb4d3eSLandon J. Fuller #include <string.h>
4477cb4d3eSLandon J. Fuller #endif /* _KERNEL */
4577cb4d3eSLandon J. Fuller 
4677cb4d3eSLandon J. Fuller #include "bhnd_nvram_private.h"
4777cb4d3eSLandon J. Fuller 
4877cb4d3eSLandon J. Fuller #include "bhnd_nvram_datavar.h"
4977cb4d3eSLandon J. Fuller 
5077cb4d3eSLandon J. Fuller #include "bhnd_nvram_data_tlvreg.h"
5177cb4d3eSLandon J. Fuller 
5277cb4d3eSLandon J. Fuller /*
5377cb4d3eSLandon J. Fuller  * CFE TLV NVRAM data class.
5477cb4d3eSLandon J. Fuller  *
5577cb4d3eSLandon J. Fuller  * The CFE-defined TLV NVRAM format is used on the WGT634U.
5677cb4d3eSLandon J. Fuller  */
5777cb4d3eSLandon J. Fuller 
5877cb4d3eSLandon J. Fuller struct bhnd_nvram_tlv {
5977cb4d3eSLandon J. Fuller 	struct bhnd_nvram_data	 nv;	/**< common instance state */
6077cb4d3eSLandon J. Fuller 	struct bhnd_nvram_io	*data;	/**< backing buffer */
6177cb4d3eSLandon J. Fuller 	size_t			 count;	/**< variable count */
6277cb4d3eSLandon J. Fuller };
6377cb4d3eSLandon J. Fuller 
64c283839dSLandon J. Fuller BHND_NVRAM_DATA_CLASS_DEFN(tlv, "WGT634U", BHND_NVRAM_DATA_CAP_DEVPATHS,
65c283839dSLandon J. Fuller     sizeof(struct bhnd_nvram_tlv))
6677cb4d3eSLandon J. Fuller 
6777cb4d3eSLandon J. Fuller /** Minimal TLV_ENV record header */
6877cb4d3eSLandon J. Fuller struct bhnd_nvram_tlv_env_hdr {
6977cb4d3eSLandon J. Fuller 	uint8_t		tag;
7077cb4d3eSLandon J. Fuller 	uint8_t		size;
7177cb4d3eSLandon J. Fuller } __packed;
7277cb4d3eSLandon J. Fuller 
7377cb4d3eSLandon J. Fuller /** Minimal TLV_ENV record */
7477cb4d3eSLandon J. Fuller struct bhnd_nvram_tlv_env {
7577cb4d3eSLandon J. Fuller 	struct bhnd_nvram_tlv_env_hdr	hdr;
7677cb4d3eSLandon J. Fuller 	uint8_t				flags;
7777cb4d3eSLandon J. Fuller 	char				envp[];
7877cb4d3eSLandon J. Fuller } __packed;
7977cb4d3eSLandon J. Fuller 
8077cb4d3eSLandon J. Fuller /* Return the length in bytes of an TLV_ENV's envp data */
8177cb4d3eSLandon J. Fuller #define	NVRAM_TLV_ENVP_DATA_LEN(_env)	\
8277cb4d3eSLandon J. Fuller 	(((_env)->hdr.size < sizeof((_env)->flags)) ? 0 :	\
8377cb4d3eSLandon J. Fuller 	    ((_env)->hdr.size - sizeof((_env)->flags)))
8477cb4d3eSLandon J. Fuller 
8519be09f3SLandon J. Fuller /* Maximum supported length of the envp data field, in bytes */
8619be09f3SLandon J. Fuller #define	NVRAM_TLV_ENVP_DATA_MAX_LEN	\
8719be09f3SLandon J. Fuller 	(UINT8_MAX - sizeof(uint8_t) /* flags */)
8819be09f3SLandon J. Fuller 
8977cb4d3eSLandon J. Fuller static int				 bhnd_nvram_tlv_parse_size(
9077cb4d3eSLandon J. Fuller 					     struct bhnd_nvram_io *io,
9177cb4d3eSLandon J. Fuller 					     size_t *size);
9277cb4d3eSLandon J. Fuller 
9377cb4d3eSLandon J. Fuller static int				 bhnd_nvram_tlv_next_record(
9477cb4d3eSLandon J. Fuller 					     struct bhnd_nvram_io *io,
9577cb4d3eSLandon J. Fuller 					     size_t *next, size_t *offset,
9677cb4d3eSLandon J. Fuller 					     uint8_t *tag);
9777cb4d3eSLandon J. Fuller 
9877cb4d3eSLandon J. Fuller static struct bhnd_nvram_tlv_env	*bhnd_nvram_tlv_next_env(
9977cb4d3eSLandon J. Fuller 					     struct bhnd_nvram_tlv *tlv,
10077cb4d3eSLandon J. Fuller 					     size_t *next, void **cookiep);
10177cb4d3eSLandon J. Fuller 
10277cb4d3eSLandon J. Fuller static struct bhnd_nvram_tlv_env	*bhnd_nvram_tlv_get_env(
10377cb4d3eSLandon J. Fuller 					     struct bhnd_nvram_tlv *tlv,
10477cb4d3eSLandon J. Fuller 					     void *cookiep);
10577cb4d3eSLandon J. Fuller 
10677cb4d3eSLandon J. Fuller static void				*bhnd_nvram_tlv_to_cookie(
10777cb4d3eSLandon J. Fuller 					     struct bhnd_nvram_tlv *tlv,
10877cb4d3eSLandon J. Fuller 					     size_t io_offset);
10977cb4d3eSLandon J. Fuller static size_t				 bhnd_nvram_tlv_to_offset(
11077cb4d3eSLandon J. Fuller 					     struct bhnd_nvram_tlv *tlv,
11177cb4d3eSLandon J. Fuller 					     void *cookiep);
11277cb4d3eSLandon J. Fuller 
11377cb4d3eSLandon J. Fuller static int
bhnd_nvram_tlv_probe(struct bhnd_nvram_io * io)11477cb4d3eSLandon J. Fuller bhnd_nvram_tlv_probe(struct bhnd_nvram_io *io)
11577cb4d3eSLandon J. Fuller {
11677cb4d3eSLandon J. Fuller 	struct bhnd_nvram_tlv_env	ident;
11777cb4d3eSLandon J. Fuller 	size_t				nbytes;
11877cb4d3eSLandon J. Fuller 	int				error;
11977cb4d3eSLandon J. Fuller 
12077cb4d3eSLandon J. Fuller 	nbytes = bhnd_nvram_io_getsize(io);
12177cb4d3eSLandon J. Fuller 
12277cb4d3eSLandon J. Fuller 	/* Handle what might be an empty TLV image */
12377cb4d3eSLandon J. Fuller 	if (nbytes < sizeof(ident)) {
12477cb4d3eSLandon J. Fuller 		uint8_t tag;
12577cb4d3eSLandon J. Fuller 
12677cb4d3eSLandon J. Fuller 		/* Fetch just the first tag */
12777cb4d3eSLandon J. Fuller 		error = bhnd_nvram_io_read(io, 0x0, &tag, sizeof(tag));
12877cb4d3eSLandon J. Fuller 		if (error)
12977cb4d3eSLandon J. Fuller 			return (error);
13077cb4d3eSLandon J. Fuller 
13177cb4d3eSLandon J. Fuller 		/* This *could* be an empty TLV image, but all we're
13277cb4d3eSLandon J. Fuller 		 * testing for here is a single 0x0 byte followed by EOF */
13377cb4d3eSLandon J. Fuller 		if (tag == NVRAM_TLV_TYPE_END)
13477cb4d3eSLandon J. Fuller 			return (BHND_NVRAM_DATA_PROBE_MAYBE);
13577cb4d3eSLandon J. Fuller 
13677cb4d3eSLandon J. Fuller 		return (ENXIO);
13777cb4d3eSLandon J. Fuller 	}
13877cb4d3eSLandon J. Fuller 
13977cb4d3eSLandon J. Fuller 	/* Otherwise, look at the initial header for a valid TLV ENV tag,
14077cb4d3eSLandon J. Fuller 	 * plus one byte of the entry data */
14177cb4d3eSLandon J. Fuller 	error = bhnd_nvram_io_read(io, 0x0, &ident,
14277cb4d3eSLandon J. Fuller 	    sizeof(ident) + sizeof(ident.envp[0]));
14377cb4d3eSLandon J. Fuller 	if (error)
14477cb4d3eSLandon J. Fuller 		return (error);
14577cb4d3eSLandon J. Fuller 
14677cb4d3eSLandon J. Fuller 	/* First entry should be a variable record (which we statically
14777cb4d3eSLandon J. Fuller 	 * assert as being defined to use a single byte size field) */
14877cb4d3eSLandon J. Fuller 	if (ident.hdr.tag != NVRAM_TLV_TYPE_ENV)
14977cb4d3eSLandon J. Fuller 		return (ENXIO);
15077cb4d3eSLandon J. Fuller 
15177cb4d3eSLandon J. Fuller 	_Static_assert(NVRAM_TLV_TYPE_ENV & NVRAM_TLV_TF_U8_LEN,
15277cb4d3eSLandon J. Fuller 	    "TYPE_ENV is not a U8-sized field");
15377cb4d3eSLandon J. Fuller 
15477cb4d3eSLandon J. Fuller 	/* The entry must be at least 3 characters ('x=\0') in length */
15577cb4d3eSLandon J. Fuller 	if (ident.hdr.size < 3)
15677cb4d3eSLandon J. Fuller 		return (ENXIO);
15777cb4d3eSLandon J. Fuller 
15877cb4d3eSLandon J. Fuller 	/* The first character should be a valid key char (alpha) */
15977cb4d3eSLandon J. Fuller 	if (!bhnd_nv_isalpha(ident.envp[0]))
16077cb4d3eSLandon J. Fuller 		return (ENXIO);
16177cb4d3eSLandon J. Fuller 
16277cb4d3eSLandon J. Fuller 	return (BHND_NVRAM_DATA_PROBE_DEFAULT);
16377cb4d3eSLandon J. Fuller }
16477cb4d3eSLandon J. Fuller 
165c283839dSLandon J. Fuller static int
bhnd_nvram_tlv_getvar_direct(struct bhnd_nvram_io * io,const char * name,void * buf,size_t * len,bhnd_nvram_type type)166591e79bcSLandon J. Fuller bhnd_nvram_tlv_getvar_direct(struct bhnd_nvram_io *io, const char *name,
167591e79bcSLandon J. Fuller     void *buf, size_t *len, bhnd_nvram_type type)
168591e79bcSLandon J. Fuller {
169591e79bcSLandon J. Fuller 	struct bhnd_nvram_tlv_env	 env;
170591e79bcSLandon J. Fuller 	char				 data[NVRAM_TLV_ENVP_DATA_MAX_LEN];
171591e79bcSLandon J. Fuller 	size_t				 data_len;
172591e79bcSLandon J. Fuller 	const char			*key, *value;
173591e79bcSLandon J. Fuller 	size_t				 keylen, vlen;
174591e79bcSLandon J. Fuller 	size_t				 namelen;
175591e79bcSLandon J. Fuller 	size_t				 next, off;
176591e79bcSLandon J. Fuller 	uint8_t				 tag;
177591e79bcSLandon J. Fuller 	int				 error;
178591e79bcSLandon J. Fuller 
179591e79bcSLandon J. Fuller 	namelen = strlen(name);
180591e79bcSLandon J. Fuller 
181591e79bcSLandon J. Fuller 	/* Iterate over the input looking for the requested variable */
182591e79bcSLandon J. Fuller 	next = 0;
183591e79bcSLandon J. Fuller 	while (!(error = bhnd_nvram_tlv_next_record(io, &next, &off, &tag))) {
184591e79bcSLandon J. Fuller 		switch (tag) {
185591e79bcSLandon J. Fuller 		case NVRAM_TLV_TYPE_END:
186591e79bcSLandon J. Fuller 			/* Not found */
187591e79bcSLandon J. Fuller 			return (ENOENT);
188591e79bcSLandon J. Fuller 
189591e79bcSLandon J. Fuller 		case NVRAM_TLV_TYPE_ENV:
190591e79bcSLandon J. Fuller 			/* Read the record header */
191591e79bcSLandon J. Fuller 			error = bhnd_nvram_io_read(io, off, &env, sizeof(env));
192591e79bcSLandon J. Fuller 			if (error) {
193591e79bcSLandon J. Fuller 				BHND_NV_LOG("error reading TLV_ENV record "
194591e79bcSLandon J. Fuller 				    "header: %d\n", error);
195591e79bcSLandon J. Fuller 				return (error);
196591e79bcSLandon J. Fuller 			}
197591e79bcSLandon J. Fuller 
198591e79bcSLandon J. Fuller 			/* Read the record data */
199591e79bcSLandon J. Fuller 			data_len = NVRAM_TLV_ENVP_DATA_LEN(&env);
200591e79bcSLandon J. Fuller 			error = bhnd_nvram_io_read(io, off + sizeof(env), data,
201591e79bcSLandon J. Fuller 			    data_len);
202591e79bcSLandon J. Fuller 			if (error) {
203591e79bcSLandon J. Fuller 				BHND_NV_LOG("error reading TLV_ENV record "
204591e79bcSLandon J. Fuller 				    "data: %d\n", error);
205591e79bcSLandon J. Fuller 				return (error);
206591e79bcSLandon J. Fuller 			}
207591e79bcSLandon J. Fuller 
208591e79bcSLandon J. Fuller 			/* Parse the key=value string */
209591e79bcSLandon J. Fuller 			error = bhnd_nvram_parse_env(data, data_len, '=', &key,
210591e79bcSLandon J. Fuller 			    &keylen, &value, &vlen);
211591e79bcSLandon J. Fuller 			if (error) {
212591e79bcSLandon J. Fuller 				BHND_NV_LOG("error parsing TLV_ENV data: %d\n",
213591e79bcSLandon J. Fuller 				    error);
214591e79bcSLandon J. Fuller 				return (error);
215591e79bcSLandon J. Fuller 			}
216591e79bcSLandon J. Fuller 
217591e79bcSLandon J. Fuller 			/* Match against requested variable name */
218591e79bcSLandon J. Fuller 			if (keylen == namelen &&
219591e79bcSLandon J. Fuller 			    strncmp(key, name, namelen) == 0)
220591e79bcSLandon J. Fuller 			{
221591e79bcSLandon J. Fuller 				return (bhnd_nvram_value_coerce(value, vlen,
222591e79bcSLandon J. Fuller 				    BHND_NVRAM_TYPE_STRING, buf, len, type));
223591e79bcSLandon J. Fuller 			}
224591e79bcSLandon J. Fuller 
225591e79bcSLandon J. Fuller 			break;
226591e79bcSLandon J. Fuller 
227591e79bcSLandon J. Fuller 		default:
228591e79bcSLandon J. Fuller 			/* Skip unknown tags */
229591e79bcSLandon J. Fuller 			break;
230591e79bcSLandon J. Fuller 		}
231591e79bcSLandon J. Fuller 	}
232591e79bcSLandon J. Fuller 
233591e79bcSLandon J. Fuller 	/* Hit I/O error */
234591e79bcSLandon J. Fuller 	return (error);
235591e79bcSLandon J. Fuller }
236591e79bcSLandon J. Fuller 
237591e79bcSLandon J. Fuller static int
bhnd_nvram_tlv_serialize(bhnd_nvram_data_class * cls,bhnd_nvram_plist * props,bhnd_nvram_plist * options,void * outp,size_t * olen)238c283839dSLandon J. Fuller bhnd_nvram_tlv_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
239c283839dSLandon J. Fuller     bhnd_nvram_plist *options, void *outp, size_t *olen)
240c283839dSLandon J. Fuller {
241c283839dSLandon J. Fuller 	bhnd_nvram_prop	*prop;
242c283839dSLandon J. Fuller 	size_t		 limit, nbytes;
243c283839dSLandon J. Fuller 	int		 error;
244c283839dSLandon J. Fuller 
245c283839dSLandon J. Fuller 	/* Determine output byte limit */
246c283839dSLandon J. Fuller 	if (outp != NULL)
247c283839dSLandon J. Fuller 		limit = *olen;
248c283839dSLandon J. Fuller 	else
249c283839dSLandon J. Fuller 		limit = 0;
250c283839dSLandon J. Fuller 
251c283839dSLandon J. Fuller 	nbytes = 0;
252c283839dSLandon J. Fuller 
253c283839dSLandon J. Fuller 	/* Write all properties */
254c283839dSLandon J. Fuller 	prop = NULL;
255c283839dSLandon J. Fuller 	while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
256c283839dSLandon J. Fuller 		struct bhnd_nvram_tlv_env	 env;
257c283839dSLandon J. Fuller 		const char			*name;
258c283839dSLandon J. Fuller 		uint8_t				*p;
259c283839dSLandon J. Fuller 		size_t				 name_len, value_len;
260c283839dSLandon J. Fuller 		size_t				 rec_size;
261c283839dSLandon J. Fuller 
262c283839dSLandon J. Fuller 		env.hdr.tag = NVRAM_TLV_TYPE_ENV;
263c283839dSLandon J. Fuller 		env.hdr.size = sizeof(env.flags);
264c283839dSLandon J. Fuller 		env.flags = 0x0;
265c283839dSLandon J. Fuller 
266c283839dSLandon J. Fuller 		/* Fetch name value and add to record length */
267c283839dSLandon J. Fuller 		name = bhnd_nvram_prop_name(prop);
268c283839dSLandon J. Fuller 		name_len = strlen(name) + 1 /* '=' */;
269c283839dSLandon J. Fuller 
270c283839dSLandon J. Fuller 		if (UINT8_MAX - env.hdr.size < name_len) {
271c283839dSLandon J. Fuller 			BHND_NV_LOG("%s name exceeds maximum TLV record "
272c283839dSLandon J. Fuller 			    "length\n", name);
273c283839dSLandon J. Fuller 			return (EFTYPE); /* would overflow TLV size */
274c283839dSLandon J. Fuller 		}
275c283839dSLandon J. Fuller 
276c283839dSLandon J. Fuller 		env.hdr.size += name_len;
277c283839dSLandon J. Fuller 
278c283839dSLandon J. Fuller 		/* Add string value to record length */
279c283839dSLandon J. Fuller 		error = bhnd_nvram_prop_encode(prop, NULL, &value_len,
280c283839dSLandon J. Fuller 		    BHND_NVRAM_TYPE_STRING);
281c283839dSLandon J. Fuller 		if (error) {
282c283839dSLandon J. Fuller 			BHND_NV_LOG("error serializing %s to required type "
283c283839dSLandon J. Fuller 			    "%s: %d\n", name,
284c283839dSLandon J. Fuller 			    bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
285c283839dSLandon J. Fuller 			    error);
286c283839dSLandon J. Fuller 			return (error);
287c283839dSLandon J. Fuller 		}
288c283839dSLandon J. Fuller 
289c283839dSLandon J. Fuller 		if (UINT8_MAX - env.hdr.size < value_len) {
290c283839dSLandon J. Fuller 			BHND_NV_LOG("%s value exceeds maximum TLV record "
291c283839dSLandon J. Fuller 			    "length\n", name);
292c283839dSLandon J. Fuller 			return (EFTYPE); /* would overflow TLV size */
293c283839dSLandon J. Fuller 		}
294c283839dSLandon J. Fuller 
295c283839dSLandon J. Fuller 		env.hdr.size += value_len;
296c283839dSLandon J. Fuller 
297c283839dSLandon J. Fuller 		/* Calculate total record size */
298c283839dSLandon J. Fuller 		rec_size = sizeof(env.hdr) + env.hdr.size;
299c283839dSLandon J. Fuller 		if (SIZE_MAX - nbytes < rec_size)
300c283839dSLandon J. Fuller 			return (EFTYPE); /* would overflow size_t */
301c283839dSLandon J. Fuller 
302c283839dSLandon J. Fuller 		/* Calculate our output pointer */
303c283839dSLandon J. Fuller 		if (nbytes > limit || limit - nbytes < rec_size) {
304c283839dSLandon J. Fuller 			/* buffer is full; cannot write */
305c283839dSLandon J. Fuller 			p = NULL;
306c283839dSLandon J. Fuller 		} else {
307c283839dSLandon J. Fuller 			p = (uint8_t *)outp + nbytes;
308c283839dSLandon J. Fuller 		}
309c283839dSLandon J. Fuller 
310c283839dSLandon J. Fuller 		/* Write to output */
311c283839dSLandon J. Fuller 		if (p != NULL) {
312c283839dSLandon J. Fuller 			memcpy(p, &env, sizeof(env));
313c283839dSLandon J. Fuller 			p += sizeof(env);
314c283839dSLandon J. Fuller 
315c283839dSLandon J. Fuller 			memcpy(p, name, name_len - 1);
316c283839dSLandon J. Fuller 			p[name_len - 1] = '=';
317c283839dSLandon J. Fuller 			p += name_len;
318c283839dSLandon J. Fuller 
319c283839dSLandon J. Fuller 			error = bhnd_nvram_prop_encode(prop, p, &value_len,
320c283839dSLandon J. Fuller 			    BHND_NVRAM_TYPE_STRING);
321c283839dSLandon J. Fuller 			if (error) {
322c283839dSLandon J. Fuller 				BHND_NV_LOG("error serializing %s to required "
323c283839dSLandon J. Fuller 				    "type %s: %d\n", name,
324c283839dSLandon J. Fuller 				    bhnd_nvram_type_name(
325c283839dSLandon J. Fuller 					BHND_NVRAM_TYPE_STRING),
326c283839dSLandon J. Fuller 				    error);
327c283839dSLandon J. Fuller 				return (error);
328c283839dSLandon J. Fuller 			}
329c283839dSLandon J. Fuller 		}
330c283839dSLandon J. Fuller 
331c283839dSLandon J. Fuller 		nbytes += rec_size;
332c283839dSLandon J. Fuller 	}
333c283839dSLandon J. Fuller 
334c283839dSLandon J. Fuller 	/* Write terminating END record */
335c283839dSLandon J. Fuller 	if (limit > nbytes)
336c283839dSLandon J. Fuller 		*((uint8_t *)outp + nbytes) = NVRAM_TLV_TYPE_END;
337c283839dSLandon J. Fuller 
338c283839dSLandon J. Fuller 	if (nbytes == SIZE_MAX)
339c283839dSLandon J. Fuller 		return (EFTYPE); /* would overflow size_t */
340c283839dSLandon J. Fuller 	nbytes++;
341c283839dSLandon J. Fuller 
342c283839dSLandon J. Fuller 	/* Provide required length */
343c283839dSLandon J. Fuller 	*olen = nbytes;
344c283839dSLandon J. Fuller 	if (limit < *olen) {
345c283839dSLandon J. Fuller 		if (outp == NULL)
346c283839dSLandon J. Fuller 			return (0);
347c283839dSLandon J. Fuller 
348c283839dSLandon J. Fuller 		return (ENOMEM);
349c283839dSLandon J. Fuller 	}
350c283839dSLandon J. Fuller 
351c283839dSLandon J. Fuller 	return (0);
352c283839dSLandon J. Fuller }
353c283839dSLandon J. Fuller 
35477cb4d3eSLandon J. Fuller /**
35577cb4d3eSLandon J. Fuller  * Initialize @p tlv with the provided NVRAM TLV data mapped by @p src.
35677cb4d3eSLandon J. Fuller  *
35777cb4d3eSLandon J. Fuller  * @param tlv A newly allocated data instance.
35877cb4d3eSLandon J. Fuller  */
35977cb4d3eSLandon J. Fuller static int
bhnd_nvram_tlv_init(struct bhnd_nvram_tlv * tlv,struct bhnd_nvram_io * src)36077cb4d3eSLandon J. Fuller bhnd_nvram_tlv_init(struct bhnd_nvram_tlv *tlv, struct bhnd_nvram_io *src)
36177cb4d3eSLandon J. Fuller {
36277cb4d3eSLandon J. Fuller 	struct bhnd_nvram_tlv_env	*env;
36377cb4d3eSLandon J. Fuller 	size_t				 size;
36477cb4d3eSLandon J. Fuller 	size_t				 next;
36577cb4d3eSLandon J. Fuller 	int				 error;
36677cb4d3eSLandon J. Fuller 
36777cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT(tlv->data == NULL, ("tlv data already initialized"));
36877cb4d3eSLandon J. Fuller 
36977cb4d3eSLandon J. Fuller 	/* Determine the actual size of the TLV source data */
37077cb4d3eSLandon J. Fuller 	if ((error = bhnd_nvram_tlv_parse_size(src, &size)))
37177cb4d3eSLandon J. Fuller 		return (error);
37277cb4d3eSLandon J. Fuller 
37377cb4d3eSLandon J. Fuller 	/* Copy to our own internal buffer */
37477cb4d3eSLandon J. Fuller 	if ((tlv->data = bhnd_nvram_iobuf_copy_range(src, 0x0, size)) == NULL)
37577cb4d3eSLandon J. Fuller 		return (ENOMEM);
37677cb4d3eSLandon J. Fuller 
37777cb4d3eSLandon J. Fuller 	/* Initialize our backing buffer */
37877cb4d3eSLandon J. Fuller 	tlv->count = 0;
37977cb4d3eSLandon J. Fuller 	next = 0;
38077cb4d3eSLandon J. Fuller 	while ((env = bhnd_nvram_tlv_next_env(tlv, &next, NULL)) != NULL) {
38177cb4d3eSLandon J. Fuller 		size_t env_len;
38277cb4d3eSLandon J. Fuller 		size_t name_len;
38377cb4d3eSLandon J. Fuller 
38477cb4d3eSLandon J. Fuller 		/* TLV_ENV data must not be empty */
38577cb4d3eSLandon J. Fuller 		env_len = NVRAM_TLV_ENVP_DATA_LEN(env);
38677cb4d3eSLandon J. Fuller 		if (env_len == 0) {
38777cb4d3eSLandon J. Fuller 			BHND_NV_LOG("cannot parse zero-length TLV_ENV record "
38877cb4d3eSLandon J. Fuller 			    "data\n");
38977cb4d3eSLandon J. Fuller 			return (EINVAL);
39077cb4d3eSLandon J. Fuller 		}
39177cb4d3eSLandon J. Fuller 
39277cb4d3eSLandon J. Fuller 		/* Parse the key=value string, and then replace the '='
39377cb4d3eSLandon J. Fuller 		 * delimiter with '\0' to allow us to provide direct
39477cb4d3eSLandon J. Fuller 		 * name pointers from our backing buffer */
39577cb4d3eSLandon J. Fuller 		error = bhnd_nvram_parse_env(env->envp, env_len, '=', NULL,
39677cb4d3eSLandon J. Fuller 		    &name_len, NULL, NULL);
39777cb4d3eSLandon J. Fuller 		if (error) {
39877cb4d3eSLandon J. Fuller 			BHND_NV_LOG("error parsing TLV_ENV data: %d\n", error);
39977cb4d3eSLandon J. Fuller 			return (error);
40077cb4d3eSLandon J. Fuller 		}
40177cb4d3eSLandon J. Fuller 
40277cb4d3eSLandon J. Fuller 		/* Replace '=' with '\0' */
40377cb4d3eSLandon J. Fuller 		*(env->envp + name_len) = '\0';
40477cb4d3eSLandon J. Fuller 
40577cb4d3eSLandon J. Fuller 		/* Add to variable count */
40677cb4d3eSLandon J. Fuller 		tlv->count++;
40777cb4d3eSLandon J. Fuller 	};
40877cb4d3eSLandon J. Fuller 
40977cb4d3eSLandon J. Fuller 	return (0);
41077cb4d3eSLandon J. Fuller }
41177cb4d3eSLandon J. Fuller 
41277cb4d3eSLandon J. Fuller static int
bhnd_nvram_tlv_new(struct bhnd_nvram_data * nv,struct bhnd_nvram_io * io)41377cb4d3eSLandon J. Fuller bhnd_nvram_tlv_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
41477cb4d3eSLandon J. Fuller {
41577cb4d3eSLandon J. Fuller 
41677cb4d3eSLandon J. Fuller 	struct bhnd_nvram_tlv	*tlv;
41777cb4d3eSLandon J. Fuller 	int			 error;
41877cb4d3eSLandon J. Fuller 
41977cb4d3eSLandon J. Fuller 	/* Allocate and initialize the TLV data instance */
42077cb4d3eSLandon J. Fuller 	tlv = (struct bhnd_nvram_tlv *)nv;
42177cb4d3eSLandon J. Fuller 
42277cb4d3eSLandon J. Fuller 	/* Parse the TLV input data and initialize our backing
42377cb4d3eSLandon J. Fuller 	 * data representation */
42477cb4d3eSLandon J. Fuller 	if ((error = bhnd_nvram_tlv_init(tlv, io))) {
42577cb4d3eSLandon J. Fuller 		bhnd_nvram_tlv_free(nv);
42677cb4d3eSLandon J. Fuller 		return (error);
42777cb4d3eSLandon J. Fuller 	}
42877cb4d3eSLandon J. Fuller 
42977cb4d3eSLandon J. Fuller 	return (0);
43077cb4d3eSLandon J. Fuller }
43177cb4d3eSLandon J. Fuller 
43277cb4d3eSLandon J. Fuller static void
bhnd_nvram_tlv_free(struct bhnd_nvram_data * nv)43377cb4d3eSLandon J. Fuller bhnd_nvram_tlv_free(struct bhnd_nvram_data *nv)
43477cb4d3eSLandon J. Fuller {
43577cb4d3eSLandon J. Fuller 	struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv;
43677cb4d3eSLandon J. Fuller 	if (tlv->data != NULL)
43777cb4d3eSLandon J. Fuller 		bhnd_nvram_io_free(tlv->data);
43877cb4d3eSLandon J. Fuller }
43977cb4d3eSLandon J. Fuller 
44077cb4d3eSLandon J. Fuller size_t
bhnd_nvram_tlv_count(struct bhnd_nvram_data * nv)44177cb4d3eSLandon J. Fuller bhnd_nvram_tlv_count(struct bhnd_nvram_data *nv)
44277cb4d3eSLandon J. Fuller {
44377cb4d3eSLandon J. Fuller 	struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv;
44477cb4d3eSLandon J. Fuller 	return (tlv->count);
44577cb4d3eSLandon J. Fuller }
44677cb4d3eSLandon J. Fuller 
447a7c43ebdSLandon J. Fuller static bhnd_nvram_plist *
bhnd_nvram_tlv_options(struct bhnd_nvram_data * nv)448a7c43ebdSLandon J. Fuller bhnd_nvram_tlv_options(struct bhnd_nvram_data *nv)
449a7c43ebdSLandon J. Fuller {
450a7c43ebdSLandon J. Fuller 	return (NULL);
451a7c43ebdSLandon J. Fuller }
452a7c43ebdSLandon J. Fuller 
45377cb4d3eSLandon J. Fuller static uint32_t
bhnd_nvram_tlv_caps(struct bhnd_nvram_data * nv)45477cb4d3eSLandon J. Fuller bhnd_nvram_tlv_caps(struct bhnd_nvram_data *nv)
45577cb4d3eSLandon J. Fuller {
45677cb4d3eSLandon J. Fuller 	return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
45777cb4d3eSLandon J. Fuller }
45877cb4d3eSLandon J. Fuller 
45977cb4d3eSLandon J. Fuller static const char *
bhnd_nvram_tlv_next(struct bhnd_nvram_data * nv,void ** cookiep)46077cb4d3eSLandon J. Fuller bhnd_nvram_tlv_next(struct bhnd_nvram_data *nv, void **cookiep)
46177cb4d3eSLandon J. Fuller {
46277cb4d3eSLandon J. Fuller 	struct bhnd_nvram_tlv		*tlv;
46377cb4d3eSLandon J. Fuller 	struct bhnd_nvram_tlv_env	*env;
46477cb4d3eSLandon J. Fuller 	size_t				 io_offset;
46577cb4d3eSLandon J. Fuller 
46677cb4d3eSLandon J. Fuller 	tlv = (struct bhnd_nvram_tlv *)nv;
46777cb4d3eSLandon J. Fuller 
468a7c43ebdSLandon J. Fuller 	/* Find next readable TLV record */
469a7c43ebdSLandon J. Fuller 	if (*cookiep == NULL) {
470a7c43ebdSLandon J. Fuller 		/* Start search at offset 0x0 */
471a7c43ebdSLandon J. Fuller 		io_offset = 0x0;
472a7c43ebdSLandon J. Fuller 		env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep);
473a7c43ebdSLandon J. Fuller 	} else {
474a7c43ebdSLandon J. Fuller 		/* Seek past the previous env record */
47577cb4d3eSLandon J. Fuller 		io_offset = bhnd_nvram_tlv_to_offset(tlv, *cookiep);
476a7c43ebdSLandon J. Fuller 		env = bhnd_nvram_tlv_next_env(tlv, &io_offset, NULL);
477a7c43ebdSLandon J. Fuller 		if (env == NULL)
478a7c43ebdSLandon J. Fuller 			BHND_NV_PANIC("invalid cookiep; record missing");
47977cb4d3eSLandon J. Fuller 
480a7c43ebdSLandon J. Fuller 		/* Advance to next env record, update the caller's cookiep */
481a7c43ebdSLandon J. Fuller 		env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep);
48277cb4d3eSLandon J. Fuller 	}
48377cb4d3eSLandon J. Fuller 
484a7c43ebdSLandon J. Fuller 	/* Check for EOF */
485a7c43ebdSLandon J. Fuller 	if (env == NULL)
486a7c43ebdSLandon J. Fuller 		return (NULL);
487a7c43ebdSLandon J. Fuller 
48877cb4d3eSLandon J. Fuller 	/* Return the NUL terminated name */
48977cb4d3eSLandon J. Fuller 	return (env->envp);
49077cb4d3eSLandon J. Fuller }
49177cb4d3eSLandon J. Fuller 
49277cb4d3eSLandon J. Fuller static void *
bhnd_nvram_tlv_find(struct bhnd_nvram_data * nv,const char * name)49377cb4d3eSLandon J. Fuller bhnd_nvram_tlv_find(struct bhnd_nvram_data *nv, const char *name)
49477cb4d3eSLandon J. Fuller {
49577cb4d3eSLandon J. Fuller 	return (bhnd_nvram_data_generic_find(nv, name));
49677cb4d3eSLandon J. Fuller }
49777cb4d3eSLandon J. Fuller 
49877cb4d3eSLandon J. Fuller static int
bhnd_nvram_tlv_getvar_order(struct bhnd_nvram_data * nv,void * cookiep1,void * cookiep2)49919be09f3SLandon J. Fuller bhnd_nvram_tlv_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
50019be09f3SLandon J. Fuller     void *cookiep2)
50119be09f3SLandon J. Fuller {
50219be09f3SLandon J. Fuller 	if (cookiep1 < cookiep2)
50319be09f3SLandon J. Fuller 		return (-1);
50419be09f3SLandon J. Fuller 
50519be09f3SLandon J. Fuller 	if (cookiep1 > cookiep2)
50619be09f3SLandon J. Fuller 		return (1);
50719be09f3SLandon J. Fuller 
50819be09f3SLandon J. Fuller 	return (0);
50919be09f3SLandon J. Fuller }
51019be09f3SLandon J. Fuller 
51119be09f3SLandon J. Fuller static int
bhnd_nvram_tlv_getvar(struct bhnd_nvram_data * nv,void * cookiep,void * buf,size_t * len,bhnd_nvram_type type)51277cb4d3eSLandon J. Fuller bhnd_nvram_tlv_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
51377cb4d3eSLandon J. Fuller     size_t *len, bhnd_nvram_type type)
51477cb4d3eSLandon J. Fuller {
51577cb4d3eSLandon J. Fuller 	return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
51677cb4d3eSLandon J. Fuller }
51777cb4d3eSLandon J. Fuller 
51819be09f3SLandon J. Fuller static int
bhnd_nvram_tlv_copy_val(struct bhnd_nvram_data * nv,void * cookiep,bhnd_nvram_val ** value)51919be09f3SLandon J. Fuller bhnd_nvram_tlv_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
52019be09f3SLandon J. Fuller     bhnd_nvram_val **value)
52119be09f3SLandon J. Fuller {
52219be09f3SLandon J. Fuller 	return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
52319be09f3SLandon J. Fuller }
52419be09f3SLandon J. Fuller 
52577cb4d3eSLandon J. Fuller static const void *
bhnd_nvram_tlv_getvar_ptr(struct bhnd_nvram_data * nv,void * cookiep,size_t * len,bhnd_nvram_type * type)52677cb4d3eSLandon J. Fuller bhnd_nvram_tlv_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
52777cb4d3eSLandon J. Fuller     size_t *len, bhnd_nvram_type *type)
52877cb4d3eSLandon J. Fuller {
52977cb4d3eSLandon J. Fuller 	struct bhnd_nvram_tlv		*tlv;
53077cb4d3eSLandon J. Fuller 	struct bhnd_nvram_tlv_env	*env;
53177cb4d3eSLandon J. Fuller 	const char			*val;
53277cb4d3eSLandon J. Fuller 	int				 error;
53377cb4d3eSLandon J. Fuller 
53477cb4d3eSLandon J. Fuller 	tlv = (struct bhnd_nvram_tlv *)nv;
53577cb4d3eSLandon J. Fuller 
53677cb4d3eSLandon J. Fuller 	/* Fetch pointer to the TLV_ENV record */
53777cb4d3eSLandon J. Fuller 	if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL)
53877cb4d3eSLandon J. Fuller 		BHND_NV_PANIC("invalid cookiep: %p", cookiep);
53977cb4d3eSLandon J. Fuller 
54077cb4d3eSLandon J. Fuller 	/* Parse value pointer and length from key\0value data */
54177cb4d3eSLandon J. Fuller 	error = bhnd_nvram_parse_env(env->envp, NVRAM_TLV_ENVP_DATA_LEN(env),
54277cb4d3eSLandon J. Fuller 	    '\0', NULL, NULL, &val, len);
54377cb4d3eSLandon J. Fuller 	if (error)
54477cb4d3eSLandon J. Fuller 		BHND_NV_PANIC("unexpected error parsing '%s'", env->envp);
54577cb4d3eSLandon J. Fuller 
54677cb4d3eSLandon J. Fuller 	/* Type is always CSTR */
54777cb4d3eSLandon J. Fuller 	*type = BHND_NVRAM_TYPE_STRING;
54877cb4d3eSLandon J. Fuller 
54977cb4d3eSLandon J. Fuller 	return (val);
55077cb4d3eSLandon J. Fuller }
55177cb4d3eSLandon J. Fuller 
55277cb4d3eSLandon J. Fuller static const char *
bhnd_nvram_tlv_getvar_name(struct bhnd_nvram_data * nv,void * cookiep)55377cb4d3eSLandon J. Fuller bhnd_nvram_tlv_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
55477cb4d3eSLandon J. Fuller {
55577cb4d3eSLandon J. Fuller 	struct bhnd_nvram_tlv		*tlv;
55677cb4d3eSLandon J. Fuller 	const struct bhnd_nvram_tlv_env	*env;
55777cb4d3eSLandon J. Fuller 
55877cb4d3eSLandon J. Fuller 	tlv = (struct bhnd_nvram_tlv *)nv;
55977cb4d3eSLandon J. Fuller 
56077cb4d3eSLandon J. Fuller 	/* Fetch pointer to the TLV_ENV record */
56177cb4d3eSLandon J. Fuller 	if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL)
56277cb4d3eSLandon J. Fuller 		BHND_NV_PANIC("invalid cookiep: %p", cookiep);
56377cb4d3eSLandon J. Fuller 
56477cb4d3eSLandon J. Fuller 	/* Return name pointer */
56577cb4d3eSLandon J. Fuller 	return (&env->envp[0]);
56677cb4d3eSLandon J. Fuller }
56777cb4d3eSLandon J. Fuller 
56819be09f3SLandon J. Fuller static int
bhnd_nvram_tlv_filter_setvar(struct bhnd_nvram_data * nv,const char * name,bhnd_nvram_val * value,bhnd_nvram_val ** result)56919be09f3SLandon J. Fuller bhnd_nvram_tlv_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
57019be09f3SLandon J. Fuller     bhnd_nvram_val *value, bhnd_nvram_val **result)
57119be09f3SLandon J. Fuller {
57219be09f3SLandon J. Fuller 	bhnd_nvram_val	*str;
57319be09f3SLandon J. Fuller 	const char	*inp;
57419be09f3SLandon J. Fuller 	bhnd_nvram_type	 itype;
57519be09f3SLandon J. Fuller 	size_t		 ilen;
57619be09f3SLandon J. Fuller 	size_t		 name_len, tlv_nremain;
57719be09f3SLandon J. Fuller 	int		 error;
57819be09f3SLandon J. Fuller 
57919be09f3SLandon J. Fuller 	tlv_nremain = NVRAM_TLV_ENVP_DATA_MAX_LEN;
58019be09f3SLandon J. Fuller 
58119be09f3SLandon J. Fuller 	/* Name (trimmed of any path prefix) must be valid */
58219be09f3SLandon J. Fuller 	if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
58319be09f3SLandon J. Fuller 		return (EINVAL);
58419be09f3SLandon J. Fuller 
58519be09f3SLandon J. Fuller 	/* 'name=' must fit within the maximum TLV_ENV record length */
58619be09f3SLandon J. Fuller 	name_len = strlen(name) + 1; /* '=' */
58719be09f3SLandon J. Fuller 	if (tlv_nremain < name_len) {
58819be09f3SLandon J. Fuller 		BHND_NV_LOG("'%s=' exceeds maximum TLV_ENV record length\n",
58919be09f3SLandon J. Fuller 		    name);
59019be09f3SLandon J. Fuller 		return (EINVAL);
59119be09f3SLandon J. Fuller 	}
59219be09f3SLandon J. Fuller 	tlv_nremain -= name_len;
59319be09f3SLandon J. Fuller 
59419be09f3SLandon J. Fuller 	/* Convert value to a (bcm-formatted) string */
59519be09f3SLandon J. Fuller 	error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
59619be09f3SLandon J. Fuller 	    value, BHND_NVRAM_VAL_DYNAMIC);
59719be09f3SLandon J. Fuller 	if (error)
59819be09f3SLandon J. Fuller 		return (error);
59919be09f3SLandon J. Fuller 
60019be09f3SLandon J. Fuller 	/* The string value must fit within remaining TLV_ENV record length */
60119be09f3SLandon J. Fuller 	inp = bhnd_nvram_val_bytes(str, &ilen, &itype);
60219be09f3SLandon J. Fuller 	if (tlv_nremain < ilen) {
60319be09f3SLandon J. Fuller 		BHND_NV_LOG("'%.*s\\0' exceeds maximum TLV_ENV record length\n",
60419be09f3SLandon J. Fuller 		    BHND_NV_PRINT_WIDTH(ilen), inp);
60519be09f3SLandon J. Fuller 
60619be09f3SLandon J. Fuller 		bhnd_nvram_val_release(str);
60719be09f3SLandon J. Fuller 		return (EINVAL);
60819be09f3SLandon J. Fuller 	}
60919be09f3SLandon J. Fuller 	tlv_nremain -= name_len;
61019be09f3SLandon J. Fuller 
61119be09f3SLandon J. Fuller 	/* Success. Transfer result ownership to the caller. */
61219be09f3SLandon J. Fuller 	*result = str;
61319be09f3SLandon J. Fuller 	return (0);
61419be09f3SLandon J. Fuller }
61519be09f3SLandon J. Fuller 
61619be09f3SLandon J. Fuller static int
bhnd_nvram_tlv_filter_unsetvar(struct bhnd_nvram_data * nv,const char * name)61719be09f3SLandon J. Fuller bhnd_nvram_tlv_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
61819be09f3SLandon J. Fuller {
61919be09f3SLandon J. Fuller 	/* We permit deletion of any variable */
62019be09f3SLandon J. Fuller 	return (0);
62119be09f3SLandon J. Fuller }
62219be09f3SLandon J. Fuller 
62377cb4d3eSLandon J. Fuller /**
62477cb4d3eSLandon J. Fuller  * Iterate over the records starting at @p next, returning the parsed
62577cb4d3eSLandon J. Fuller  * record's @p tag, @p size, and @p offset.
62677cb4d3eSLandon J. Fuller  *
62777cb4d3eSLandon J. Fuller  * @param		io		The I/O context to parse.
62877cb4d3eSLandon J. Fuller  * @param[in,out]	next		The next offset to be parsed, or 0x0
62977cb4d3eSLandon J. Fuller  *					to begin parsing. Upon successful
63077cb4d3eSLandon J. Fuller  *					return, will be set to the offset of the
63177cb4d3eSLandon J. Fuller  *					next record (or EOF, if
63277cb4d3eSLandon J. Fuller  *					NVRAM_TLV_TYPE_END was parsed).
63377cb4d3eSLandon J. Fuller  * @param[out]		offset		The record's value offset.
63477cb4d3eSLandon J. Fuller  * @param[out]		tag		The record's tag.
63577cb4d3eSLandon J. Fuller  *
63677cb4d3eSLandon J. Fuller  * @retval 0		success
63777cb4d3eSLandon J. Fuller  * @retval EINVAL	if parsing @p io as TLV fails.
63877cb4d3eSLandon J. Fuller  * @retval non-zero	if reading @p io otherwise fails, a regular unix error
63977cb4d3eSLandon J. Fuller  *			code will be returned.
64077cb4d3eSLandon J. Fuller  */
64177cb4d3eSLandon J. Fuller static int
bhnd_nvram_tlv_next_record(struct bhnd_nvram_io * io,size_t * next,size_t * offset,uint8_t * tag)64277cb4d3eSLandon J. Fuller bhnd_nvram_tlv_next_record(struct bhnd_nvram_io *io, size_t *next, size_t
64377cb4d3eSLandon J. Fuller     *offset, uint8_t *tag)
64477cb4d3eSLandon J. Fuller {
64577cb4d3eSLandon J. Fuller 	size_t		io_offset, io_size;
64677cb4d3eSLandon J. Fuller 	uint16_t	parsed_len;
64777cb4d3eSLandon J. Fuller 	uint8_t		len_hdr[2];
64877cb4d3eSLandon J. Fuller 	int		error;
64977cb4d3eSLandon J. Fuller 
65077cb4d3eSLandon J. Fuller 	io_offset = *next;
65177cb4d3eSLandon J. Fuller 	io_size = bhnd_nvram_io_getsize(io);
65277cb4d3eSLandon J. Fuller 
65377cb4d3eSLandon J. Fuller 	/* Save the record offset */
65477cb4d3eSLandon J. Fuller 	if (offset != NULL)
65577cb4d3eSLandon J. Fuller 		*offset = io_offset;
65677cb4d3eSLandon J. Fuller 
65777cb4d3eSLandon J. Fuller 	/* Fetch initial tag */
65877cb4d3eSLandon J. Fuller 	error = bhnd_nvram_io_read(io, io_offset, tag, sizeof(*tag));
65977cb4d3eSLandon J. Fuller 	if (error)
66077cb4d3eSLandon J. Fuller 		return (error);
66177cb4d3eSLandon J. Fuller 	io_offset++;
66277cb4d3eSLandon J. Fuller 
66377cb4d3eSLandon J. Fuller 	/* EOF */
66477cb4d3eSLandon J. Fuller 	if (*tag == NVRAM_TLV_TYPE_END) {
66577cb4d3eSLandon J. Fuller 		*next = io_offset;
66677cb4d3eSLandon J. Fuller 		return (0);
66777cb4d3eSLandon J. Fuller 	}
66877cb4d3eSLandon J. Fuller 
66977cb4d3eSLandon J. Fuller 	/* Read length field */
67077cb4d3eSLandon J. Fuller 	if (*tag & NVRAM_TLV_TF_U8_LEN) {
67177cb4d3eSLandon J. Fuller 		error = bhnd_nvram_io_read(io, io_offset, &len_hdr,
67277cb4d3eSLandon J. Fuller 		    sizeof(len_hdr[0]));
67377cb4d3eSLandon J. Fuller 		if (error) {
67477cb4d3eSLandon J. Fuller 			BHND_NV_LOG("error reading TLV record size: %d\n",
67577cb4d3eSLandon J. Fuller 			    error);
67677cb4d3eSLandon J. Fuller 			return (error);
67777cb4d3eSLandon J. Fuller 		}
67877cb4d3eSLandon J. Fuller 
67977cb4d3eSLandon J. Fuller 		parsed_len = len_hdr[0];
68077cb4d3eSLandon J. Fuller 		io_offset++;
68177cb4d3eSLandon J. Fuller 	} else {
68277cb4d3eSLandon J. Fuller 		error = bhnd_nvram_io_read(io, io_offset, &len_hdr,
68377cb4d3eSLandon J. Fuller 		    sizeof(len_hdr));
68477cb4d3eSLandon J. Fuller 		if (error) {
68577cb4d3eSLandon J. Fuller 			BHND_NV_LOG("error reading 16-bit TLV record "
68677cb4d3eSLandon J. Fuller 			    "size: %d\n", error);
68777cb4d3eSLandon J. Fuller 			return (error);
68877cb4d3eSLandon J. Fuller 		}
68977cb4d3eSLandon J. Fuller 
69077cb4d3eSLandon J. Fuller 		parsed_len = (len_hdr[0] << 8) | len_hdr[1];
69177cb4d3eSLandon J. Fuller 		io_offset += 2;
69277cb4d3eSLandon J. Fuller 	}
69377cb4d3eSLandon J. Fuller 
69477cb4d3eSLandon J. Fuller 	/* Advance to next record */
69577cb4d3eSLandon J. Fuller 	if (parsed_len > io_size || io_size - parsed_len < io_offset) {
69677cb4d3eSLandon J. Fuller 		/* Hit early EOF */
69777cb4d3eSLandon J. Fuller 		BHND_NV_LOG("TLV record length %hu truncated by input "
69877cb4d3eSLandon J. Fuller 		    "size of %zu\n", parsed_len, io_size);
69977cb4d3eSLandon J. Fuller 		return (EINVAL);
70077cb4d3eSLandon J. Fuller 	}
70177cb4d3eSLandon J. Fuller 
70277cb4d3eSLandon J. Fuller 	*next = io_offset + parsed_len;
70377cb4d3eSLandon J. Fuller 
70477cb4d3eSLandon J. Fuller 	/* Valid record found */
70577cb4d3eSLandon J. Fuller 	return (0);
70677cb4d3eSLandon J. Fuller }
70777cb4d3eSLandon J. Fuller 
70877cb4d3eSLandon J. Fuller /**
70977cb4d3eSLandon J. Fuller  * Parse the TLV data in @p io to determine the total size of the TLV
71077cb4d3eSLandon J. Fuller  * data mapped by @p io (which may be less than the size of @p io).
71177cb4d3eSLandon J. Fuller  */
71277cb4d3eSLandon J. Fuller static int
bhnd_nvram_tlv_parse_size(struct bhnd_nvram_io * io,size_t * size)71377cb4d3eSLandon J. Fuller bhnd_nvram_tlv_parse_size(struct bhnd_nvram_io *io, size_t *size)
71477cb4d3eSLandon J. Fuller {
71577cb4d3eSLandon J. Fuller 	size_t		next;
71677cb4d3eSLandon J. Fuller 	uint8_t		tag;
71777cb4d3eSLandon J. Fuller 	int		error;
71877cb4d3eSLandon J. Fuller 
71977cb4d3eSLandon J. Fuller 	/* We have to perform a minimal parse to determine the actual length */
72077cb4d3eSLandon J. Fuller 	next = 0x0;
72177cb4d3eSLandon J. Fuller 	*size = 0x0;
72277cb4d3eSLandon J. Fuller 
72377cb4d3eSLandon J. Fuller 	/* Iterate over the input until we hit END tag or the read fails */
72477cb4d3eSLandon J. Fuller 	do {
72577cb4d3eSLandon J. Fuller 		error = bhnd_nvram_tlv_next_record(io, &next, NULL, &tag);
72677cb4d3eSLandon J. Fuller 		if (error)
72777cb4d3eSLandon J. Fuller 			return (error);
72877cb4d3eSLandon J. Fuller 	} while (tag != NVRAM_TLV_TYPE_END);
72977cb4d3eSLandon J. Fuller 
73077cb4d3eSLandon J. Fuller 	/* Offset should now point to EOF */
73177cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT(next <= bhnd_nvram_io_getsize(io),
73277cb4d3eSLandon J. Fuller 	    ("parse returned invalid EOF offset"));
73377cb4d3eSLandon J. Fuller 
73477cb4d3eSLandon J. Fuller 	*size = next;
73577cb4d3eSLandon J. Fuller 	return (0);
73677cb4d3eSLandon J. Fuller }
73777cb4d3eSLandon J. Fuller 
73877cb4d3eSLandon J. Fuller /**
73977cb4d3eSLandon J. Fuller  * Iterate over the records in @p tlv, returning a pointer to the next
74077cb4d3eSLandon J. Fuller  * NVRAM_TLV_TYPE_ENV record, or NULL if EOF is reached.
74177cb4d3eSLandon J. Fuller  *
74277cb4d3eSLandon J. Fuller  * @param		tlv		The TLV instance.
74377cb4d3eSLandon J. Fuller  * @param[in,out]	next		The next offset to be parsed, or 0x0
74477cb4d3eSLandon J. Fuller  *					to begin parsing. Upon successful
74577cb4d3eSLandon J. Fuller  *					return, will be set to the offset of the
74677cb4d3eSLandon J. Fuller  *					next record.
74777cb4d3eSLandon J. Fuller  */
74877cb4d3eSLandon J. Fuller static struct bhnd_nvram_tlv_env *
bhnd_nvram_tlv_next_env(struct bhnd_nvram_tlv * tlv,size_t * next,void ** cookiep)74977cb4d3eSLandon J. Fuller bhnd_nvram_tlv_next_env(struct bhnd_nvram_tlv *tlv, size_t *next,
75077cb4d3eSLandon J. Fuller     void **cookiep)
75177cb4d3eSLandon J. Fuller {
75277cb4d3eSLandon J. Fuller 	uint8_t	tag;
75377cb4d3eSLandon J. Fuller 	int	error;
75477cb4d3eSLandon J. Fuller 
75577cb4d3eSLandon J. Fuller 	/* Find the next TLV_ENV record, starting at @p next */
75677cb4d3eSLandon J. Fuller 	do {
75777cb4d3eSLandon J. Fuller 		void	*c;
75877cb4d3eSLandon J. Fuller 		size_t	 offset;
75977cb4d3eSLandon J. Fuller 
76077cb4d3eSLandon J. Fuller 		/* Fetch the next TLV record */
76177cb4d3eSLandon J. Fuller 		error = bhnd_nvram_tlv_next_record(tlv->data, next, &offset,
76277cb4d3eSLandon J. Fuller 		    &tag);
76377cb4d3eSLandon J. Fuller 		if (error) {
76477cb4d3eSLandon J. Fuller 			BHND_NV_LOG("unexpected error in next_record(): %d\n",
76577cb4d3eSLandon J. Fuller 			    error);
76677cb4d3eSLandon J. Fuller 			return (NULL);
76777cb4d3eSLandon J. Fuller 		}
76877cb4d3eSLandon J. Fuller 
76977cb4d3eSLandon J. Fuller 		/* Only interested in ENV records */
77077cb4d3eSLandon J. Fuller 		if (tag != NVRAM_TLV_TYPE_ENV)
77177cb4d3eSLandon J. Fuller 			continue;
77277cb4d3eSLandon J. Fuller 
77377cb4d3eSLandon J. Fuller 		/* Map and return TLV_ENV record pointer */
77477cb4d3eSLandon J. Fuller 		c = bhnd_nvram_tlv_to_cookie(tlv, offset);
77577cb4d3eSLandon J. Fuller 
77677cb4d3eSLandon J. Fuller 		/* Provide the cookiep value for the returned record */
77777cb4d3eSLandon J. Fuller 		if (cookiep != NULL)
77877cb4d3eSLandon J. Fuller 			*cookiep = c;
77977cb4d3eSLandon J. Fuller 
78077cb4d3eSLandon J. Fuller 		return (bhnd_nvram_tlv_get_env(tlv, c));
78177cb4d3eSLandon J. Fuller 	} while (tag != NVRAM_TLV_TYPE_END);
78277cb4d3eSLandon J. Fuller 
78377cb4d3eSLandon J. Fuller 	/* No remaining ENV records */
78477cb4d3eSLandon J. Fuller 	return (NULL);
78577cb4d3eSLandon J. Fuller }
78677cb4d3eSLandon J. Fuller 
78777cb4d3eSLandon J. Fuller /**
78877cb4d3eSLandon J. Fuller  * Return a pointer to the TLV_ENV record for @p cookiep, or NULL
78977cb4d3eSLandon J. Fuller  * if none vailable.
79077cb4d3eSLandon J. Fuller  */
79177cb4d3eSLandon J. Fuller static struct bhnd_nvram_tlv_env *
bhnd_nvram_tlv_get_env(struct bhnd_nvram_tlv * tlv,void * cookiep)79277cb4d3eSLandon J. Fuller bhnd_nvram_tlv_get_env(struct bhnd_nvram_tlv *tlv, void *cookiep)
79377cb4d3eSLandon J. Fuller {
79477cb4d3eSLandon J. Fuller 	struct bhnd_nvram_tlv_env	*env;
79577cb4d3eSLandon J. Fuller 	void				*ptr;
79677cb4d3eSLandon J. Fuller 	size_t				 navail;
79777cb4d3eSLandon J. Fuller 	size_t				 io_offset, io_size;
79877cb4d3eSLandon J. Fuller 	int				 error;
79977cb4d3eSLandon J. Fuller 
80077cb4d3eSLandon J. Fuller 	io_size = bhnd_nvram_io_getsize(tlv->data);
80177cb4d3eSLandon J. Fuller 	io_offset = bhnd_nvram_tlv_to_offset(tlv, cookiep);
80277cb4d3eSLandon J. Fuller 
80377cb4d3eSLandon J. Fuller 	/* At EOF? */
80477cb4d3eSLandon J. Fuller 	if (io_offset == io_size)
80577cb4d3eSLandon J. Fuller 		return (NULL);
80677cb4d3eSLandon J. Fuller 
80777cb4d3eSLandon J. Fuller 	/* Fetch non-const pointer to the record entry */
80877cb4d3eSLandon J. Fuller 	error = bhnd_nvram_io_write_ptr(tlv->data, io_offset, &ptr,
80977cb4d3eSLandon J. Fuller 	    sizeof(env->hdr), &navail);
81077cb4d3eSLandon J. Fuller 	if (error) {
81177cb4d3eSLandon J. Fuller 		/* Should never occur with a valid cookiep */
81277cb4d3eSLandon J. Fuller 		BHND_NV_LOG("error mapping record for cookiep: %d\n", error);
81377cb4d3eSLandon J. Fuller 		return (NULL);
81477cb4d3eSLandon J. Fuller 	}
81577cb4d3eSLandon J. Fuller 
81677cb4d3eSLandon J. Fuller 	/* Validate the record pointer */
81777cb4d3eSLandon J. Fuller 	env = ptr;
81877cb4d3eSLandon J. Fuller 	if (env->hdr.tag != NVRAM_TLV_TYPE_ENV) {
81977cb4d3eSLandon J. Fuller 		/* Should never occur with a valid cookiep */
82077cb4d3eSLandon J. Fuller 		BHND_NV_LOG("non-ENV record mapped for %p\n", cookiep);
82177cb4d3eSLandon J. Fuller 		return (NULL);
82277cb4d3eSLandon J. Fuller 	}
82377cb4d3eSLandon J. Fuller 
82477cb4d3eSLandon J. Fuller 	/* Is the required variable name data is mapped? */
82577cb4d3eSLandon J. Fuller 	if (navail < sizeof(struct bhnd_nvram_tlv_env_hdr) + env->hdr.size ||
82677cb4d3eSLandon J. Fuller 	    env->hdr.size == sizeof(env->flags))
82777cb4d3eSLandon J. Fuller 	{
82877cb4d3eSLandon J. Fuller 		/* Should never occur with a valid cookiep */
82977cb4d3eSLandon J. Fuller 		BHND_NV_LOG("TLV_ENV variable data not mapped for %p\n",
83077cb4d3eSLandon J. Fuller 		    cookiep);
83177cb4d3eSLandon J. Fuller 		return (NULL);
83277cb4d3eSLandon J. Fuller 	}
83377cb4d3eSLandon J. Fuller 
83477cb4d3eSLandon J. Fuller 	return (env);
83577cb4d3eSLandon J. Fuller }
83677cb4d3eSLandon J. Fuller 
83777cb4d3eSLandon J. Fuller /**
83877cb4d3eSLandon J. Fuller  * Return a cookiep for the given I/O offset.
83977cb4d3eSLandon J. Fuller  */
84077cb4d3eSLandon J. Fuller static void *
bhnd_nvram_tlv_to_cookie(struct bhnd_nvram_tlv * tlv,size_t io_offset)84177cb4d3eSLandon J. Fuller bhnd_nvram_tlv_to_cookie(struct bhnd_nvram_tlv *tlv, size_t io_offset)
84277cb4d3eSLandon J. Fuller {
84319be09f3SLandon J. Fuller 	const void	*ptr;
84419be09f3SLandon J. Fuller 	int		 error;
84519be09f3SLandon J. Fuller 
84677cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(tlv->data),
84777cb4d3eSLandon J. Fuller 	    ("io_offset %zu out-of-range", io_offset));
84877cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT(io_offset < UINTPTR_MAX,
84977cb4d3eSLandon J. Fuller 	    ("io_offset %#zx exceeds UINTPTR_MAX", io_offset));
85077cb4d3eSLandon J. Fuller 
85119be09f3SLandon J. Fuller 	error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_offset, NULL);
85219be09f3SLandon J. Fuller 	if (error)
85319be09f3SLandon J. Fuller 		BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error);
85419be09f3SLandon J. Fuller 
85519be09f3SLandon J. Fuller 	ptr = (const uint8_t *)ptr + io_offset;
85619be09f3SLandon J. Fuller 	return (__DECONST(void *, ptr));
85777cb4d3eSLandon J. Fuller }
85877cb4d3eSLandon J. Fuller 
85977cb4d3eSLandon J. Fuller /* Convert a cookiep back to an I/O offset */
86077cb4d3eSLandon J. Fuller static size_t
bhnd_nvram_tlv_to_offset(struct bhnd_nvram_tlv * tlv,void * cookiep)86177cb4d3eSLandon J. Fuller bhnd_nvram_tlv_to_offset(struct bhnd_nvram_tlv *tlv, void *cookiep)
86277cb4d3eSLandon J. Fuller {
86319be09f3SLandon J. Fuller 	const void	*ptr;
86419be09f3SLandon J. Fuller 	intptr_t	 offset;
86577cb4d3eSLandon J. Fuller 	size_t		 io_size;
86619be09f3SLandon J. Fuller 	int		 error;
86719be09f3SLandon J. Fuller 
86819be09f3SLandon J. Fuller 	BHND_NV_ASSERT(cookiep != NULL, ("null cookiep"));
86977cb4d3eSLandon J. Fuller 
87077cb4d3eSLandon J. Fuller 	io_size = bhnd_nvram_io_getsize(tlv->data);
87177cb4d3eSLandon J. Fuller 
87219be09f3SLandon J. Fuller 	error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_size, NULL);
87319be09f3SLandon J. Fuller 	if (error)
87419be09f3SLandon J. Fuller 		BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error);
87577cb4d3eSLandon J. Fuller 
87619be09f3SLandon J. Fuller 	offset = (const uint8_t *)cookiep - (const uint8_t *)ptr;
87719be09f3SLandon J. Fuller 	BHND_NV_ASSERT(offset >= 0, ("invalid cookiep"));
87819be09f3SLandon J. Fuller 	BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)"));
87919be09f3SLandon J. Fuller 	BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)"));
88019be09f3SLandon J. Fuller 
88119be09f3SLandon J. Fuller 	return ((size_t)offset);
88277cb4d3eSLandon J. Fuller }
883