xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c (revision 6c925b9c81036a86db387f75a32b423420eadf6c)
1 /*-
2  * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/endian.h>
35 
36 #ifdef _KERNEL
37 
38 #include <sys/bus.h>
39 #include <sys/ctype.h>
40 #include <sys/malloc.h>
41 #include <sys/systm.h>
42 
43 #else /* !_KERNEL */
44 
45 #include <ctype.h>
46 #include <stdint.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 
51 #endif /* _KERNEL */
52 
53 #include "bhnd_nvram_private.h"
54 
55 #include "bhnd_nvram_datavar.h"
56 
57 #include "bhnd_nvram_data_bcmreg.h"
58 #include "bhnd_nvram_data_bcmvar.h"
59 
60 /*
61  * Broadcom NVRAM data class.
62  *
63  * The Broadcom NVRAM NUL-delimited ASCII format is used by most
64  * Broadcom SoCs.
65  *
66  * The NVRAM data is encoded as a standard header, followed by series of
67  * NUL-terminated 'key=value' strings; the end of the stream is denoted
68  * by a single extra NUL character.
69  */
70 
71 struct bhnd_nvram_bcm;
72 
73 static struct bhnd_nvram_bcm_hvar	*bhnd_nvram_bcm_gethdrvar(
74 					     struct bhnd_nvram_bcm *bcm,
75 					     const char *name);
76 static struct bhnd_nvram_bcm_hvar	*bhnd_nvram_bcm_to_hdrvar(
77 					     struct bhnd_nvram_bcm *bcm,
78 					     void *cookiep);
79 static size_t				 bhnd_nvram_bcm_hdrvar_index(
80 					     struct bhnd_nvram_bcm *bcm,
81 					     struct bhnd_nvram_bcm_hvar *hvar);
82 /*
83  * Set of BCM NVRAM header values that are required to be mirrored in the
84  * NVRAM data itself.
85  *
86  * If they're not included in the parsed NVRAM data, we need to vend the
87  * header-parsed values with their appropriate keys, and add them in any
88  * updates to the NVRAM data.
89  *
90  * If they're modified in NVRAM, we need to sync the changes with the
91  * the NVRAM header values.
92  */
93 static const struct bhnd_nvram_bcm_hvar bhnd_nvram_bcm_hvars[] = {
94 	{
95 		.name	= BCM_NVRAM_CFG0_SDRAM_INIT_VAR,
96 		.type	= BHND_NVRAM_TYPE_UINT16,
97 		.len	= sizeof(uint16_t),
98 		.nelem	= 1,
99 	},
100 	{
101 		.name	= BCM_NVRAM_CFG1_SDRAM_CFG_VAR,
102 		.type	= BHND_NVRAM_TYPE_UINT16,
103 		.len	= sizeof(uint16_t),
104 		.nelem	= 1,
105 	},
106 	{
107 		.name	= BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR,
108 		.type	= BHND_NVRAM_TYPE_UINT16,
109 		.len	= sizeof(uint16_t),
110 		.nelem	= 1,
111 	},
112 	{
113 		.name	= BCM_NVRAM_SDRAM_NCDL_VAR,
114 		.type	= BHND_NVRAM_TYPE_UINT32,
115 		.len	= sizeof(uint32_t),
116 		.nelem	= 1,
117 	},
118 };
119 
120 /** BCM NVRAM data class instance */
121 struct bhnd_nvram_bcm {
122 	struct bhnd_nvram_data		 nv;	/**< common instance state */
123 	struct bhnd_nvram_io		*data;	/**< backing buffer */
124 
125 	/** BCM header values */
126 	struct bhnd_nvram_bcm_hvar	 hvars[nitems(bhnd_nvram_bcm_hvars)];
127 
128 	size_t				 count;	/**< total variable count */
129 };
130 
131 BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", sizeof(struct bhnd_nvram_bcm))
132 
133 static int
134 bhnd_nvram_bcm_probe(struct bhnd_nvram_io *io)
135 {
136 	struct bhnd_nvram_bcmhdr	hdr;
137 	int				error;
138 
139 	if ((error = bhnd_nvram_io_read(io, 0x0, &hdr, sizeof(hdr))))
140 		return (error);
141 
142 	if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
143 		return (ENXIO);
144 
145 	return (BHND_NVRAM_DATA_PROBE_DEFAULT);
146 }
147 
148 /**
149  * Initialize @p bcm with the provided NVRAM data mapped by @p src.
150  *
151  * @param bcm A newly allocated data instance.
152  */
153 static int
154 bhnd_nvram_bcm_init(struct bhnd_nvram_bcm *bcm, struct bhnd_nvram_io *src)
155 {
156 	struct bhnd_nvram_bcmhdr	 hdr;
157 	uint8_t				*p;
158 	void				*ptr;
159 	size_t				 io_offset, io_size;
160 	uint8_t				 crc, valid;
161 	int				 error;
162 
163 	if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr))))
164 		return (error);
165 
166 	if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
167 		return (ENXIO);
168 
169 	/* Fetch the actual NVRAM image size */
170 	io_size = le32toh(hdr.size);
171 	if (io_size < sizeof(hdr)) {
172 		/* The header size must include the header itself */
173 		BHND_NV_LOG("corrupt header size: %zu\n", io_size);
174 		return (EINVAL);
175 	}
176 
177 	if (io_size > bhnd_nvram_io_getsize(src)) {
178 		BHND_NV_LOG("header size %zu exceeds input size %zu\n",
179 		    io_size, bhnd_nvram_io_getsize(src));
180 		return (EINVAL);
181 	}
182 
183 	/* Allocate a buffer large enough to hold the NVRAM image, and
184 	 * an extra EOF-signaling NUL (on the chance it's missing from the
185 	 * source data) */
186 	if (io_size == SIZE_MAX)
187 		return (ENOMEM);
188 
189 	bcm->data = bhnd_nvram_iobuf_empty(io_size, io_size + 1);
190 	if (bcm->data == NULL)
191 		return (ENOMEM);
192 
193 	/* Fetch a pointer into our backing buffer and copy in the
194 	 * NVRAM image. */
195 	error = bhnd_nvram_io_write_ptr(bcm->data, 0x0, &ptr, io_size, NULL);
196 	if (error)
197 		return (error);
198 
199 	p = ptr;
200 	if ((error = bhnd_nvram_io_read(src, 0x0, p, io_size)))
201 		return (error);
202 
203 	/* Verify the CRC */
204 	valid = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC);
205 	crc = bhnd_nvram_crc8(p + BCM_NVRAM_CRC_SKIP,
206 	    io_size - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
207 
208 	if (crc != valid) {
209 		BHND_NV_LOG("warning: NVRAM CRC error (crc=%#hhx, "
210 		    "expected=%hhx)\n", crc, valid);
211 	}
212 
213 	/* Populate header variable definitions */
214 #define	BCM_READ_HDR_VAR(_name, _dest, _swap) do {		\
215 	struct bhnd_nvram_bcm_hvar *data;				\
216 	data = bhnd_nvram_bcm_gethdrvar(bcm, _name ##_VAR);		\
217 	BHND_NV_ASSERT(data != NULL,						\
218 	    ("no such header variable: " __STRING(_name)));		\
219 									\
220 									\
221 	data->value. _dest = _swap(BCM_NVRAM_GET_BITS(			\
222 	    hdr. _name ## _FIELD, _name));				\
223 } while(0)
224 
225 	BCM_READ_HDR_VAR(BCM_NVRAM_CFG0_SDRAM_INIT,	u16, le16toh);
226 	BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_CFG,	u16, le16toh);
227 	BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_REFRESH,	u16, le16toh);
228 	BCM_READ_HDR_VAR(BCM_NVRAM_SDRAM_NCDL,		u32, le32toh);
229 
230 	_Static_assert(nitems(bcm->hvars) == 4, "missing initialization for"
231 	    "NVRAM header variable(s)");
232 
233 #undef BCM_READ_HDR_VAR
234 
235 	/* Process the buffer */
236 	bcm->count = 0;
237 	io_offset = sizeof(hdr);
238 	while (io_offset < io_size) {
239 		char		*envp;
240 		const char	*name, *value;
241 		size_t		 envp_len;
242 		size_t		 name_len, value_len;
243 
244 		/* Parse the key=value string */
245 		envp = (char *) (p + io_offset);
246 		envp_len = strnlen(envp, io_size - io_offset);
247 		error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,
248 					     &name_len, &value, &value_len);
249 		if (error) {
250 			BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",
251 			    io_offset, error);
252 			return (error);
253 		}
254 
255 		/* Insert a '\0' character, replacing the '=' delimiter and
256 		 * allowing us to vend references directly to the variable
257 		 * name */
258 		*(envp + name_len) = '\0';
259 
260 		/* Record any NVRAM variables that mirror our header variables.
261 		 * This is a brute-force search -- for the amount of data we're
262 		 * operating on, it shouldn't be an issue. */
263 		for (size_t i = 0; i < nitems(bcm->hvars); i++) {
264 			struct bhnd_nvram_bcm_hvar	*hvar;
265 			union bhnd_nvram_bcm_hvar_value	 hval;
266 			size_t				 hval_len;
267 
268 			hvar = &bcm->hvars[i];
269 
270 			/* Already matched? */
271 			if (hvar->envp != NULL)
272 				continue;
273 
274 			/* Name matches? */
275 			if ((strcmp(name, hvar->name)) != 0)
276 				continue;
277 
278 			/* Save pointer to mirrored envp */
279 			hvar->envp = envp;
280 
281 			/* Check for stale value */
282 			hval_len = sizeof(hval);
283 			error = bhnd_nvram_value_coerce(value, value_len,
284 			    BHND_NVRAM_TYPE_STRING, &hval, &hval_len,
285 			    hvar->type);
286 			if (error) {
287 				/* If parsing fails, we can likely only make
288 				 * things worse by trying to synchronize the
289 				 * variables */
290 				BHND_NV_LOG("error parsing header variable "
291 				    "'%s=%s': %d\n", name, value, error);
292 			} else if (hval_len != hvar->len) {
293 				hvar->stale = true;
294 			} else if (memcmp(&hval, &hvar->value, hval_len) != 0) {
295 				hvar->stale = true;
296 			}
297 		}
298 
299 		/* Seek past the value's terminating '\0' */
300 		io_offset += envp_len;
301 		if (io_offset == io_size) {
302 			BHND_NV_LOG("missing terminating NUL at offset %#zx\n",
303 			    io_offset);
304 			return (EINVAL);
305 		}
306 
307 		if (*(p + io_offset) != '\0') {
308 			BHND_NV_LOG("invalid terminator '%#hhx' at offset "
309 			    "%#zx\n", *(p + io_offset), io_offset);
310 			return (EINVAL);
311 		}
312 
313 		/* Update variable count */
314 		bcm->count++;
315 
316 		/* Seek to the next record */
317 		if (++io_offset == io_size) {
318 			char ch;
319 
320 			/* Hit EOF without finding a terminating NUL
321 			 * byte; we need to grow our buffer and append
322 			 * it */
323 			io_size++;
324 			if ((error = bhnd_nvram_io_setsize(bcm->data, io_size)))
325 				return (error);
326 
327 			/* Write NUL byte */
328 			ch = '\0';
329 			error = bhnd_nvram_io_write(bcm->data, io_size-1, &ch,
330 			    sizeof(ch));
331 			if (error)
332 				return (error);
333 		}
334 
335 		/* Check for explicit EOF (encoded as a single empty NUL
336 		 * terminated string) */
337 		if (*(p + io_offset) == '\0')
338 			break;
339 	}
340 
341 	/* Add non-mirrored header variables to total count variable */
342 	for (size_t i = 0; i < nitems(bcm->hvars); i++) {
343 		if (bcm->hvars[i].envp == NULL)
344 			bcm->count++;
345 	}
346 
347 	return (0);
348 }
349 
350 static int
351 bhnd_nvram_bcm_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
352 {
353 	struct bhnd_nvram_bcm	*bcm;
354 	int			 error;
355 
356 	bcm = (struct bhnd_nvram_bcm *)nv;
357 
358 	/* Populate default BCM mirrored header variable set */
359 	_Static_assert(sizeof(bcm->hvars) == sizeof(bhnd_nvram_bcm_hvars),
360 	    "hvar declarations must match bhnd_nvram_bcm_hvars template");
361 	memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars));
362 
363 	/* Parse the BCM input data and initialize our backing
364 	 * data representation */
365 	if ((error = bhnd_nvram_bcm_init(bcm, io))) {
366 		bhnd_nvram_bcm_free(nv);
367 		return (error);
368 	}
369 
370 	return (0);
371 }
372 
373 static void
374 bhnd_nvram_bcm_free(struct bhnd_nvram_data *nv)
375 {
376 	struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
377 
378 	if (bcm->data != NULL)
379 		bhnd_nvram_io_free(bcm->data);
380 }
381 
382 size_t
383 bhnd_nvram_bcm_count(struct bhnd_nvram_data *nv)
384 {
385 	struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
386 	return (bcm->count);
387 }
388 
389 static int
390 bhnd_nvram_bcm_size(struct bhnd_nvram_data *nv, size_t *size)
391 {
392 	return (bhnd_nvram_bcm_serialize(nv, NULL, size));
393 }
394 
395 static int
396 bhnd_nvram_bcm_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len)
397 {
398 	struct bhnd_nvram_bcm		*bcm;
399 	struct bhnd_nvram_bcmhdr	 hdr;
400 	void				*cookiep;
401 	const char			*name;
402 	size_t				 nbytes, limit;
403 	uint8_t				 crc;
404 	int				 error;
405 
406 	bcm = (struct bhnd_nvram_bcm *)nv;
407 	nbytes = 0;
408 
409 	/* Save the output buffer limit */
410 	if (buf == NULL)
411 		limit = 0;
412 	else
413 		limit = *len;
414 
415 	/* Reserve space for the NVRAM header */
416 	nbytes += sizeof(struct bhnd_nvram_bcmhdr);
417 
418 	/* Write all variables to the output buffer */
419 	cookiep = NULL;
420 	while ((name = bhnd_nvram_data_next(nv, &cookiep))) {
421 		uint8_t		*outp;
422 		size_t		 olen;
423 		size_t		 name_len, val_len;
424 
425 		if (limit > nbytes) {
426 			outp = (uint8_t *)buf + nbytes;
427 			olen = limit - nbytes;
428 		} else {
429 			outp = NULL;
430 			olen = 0;
431 		}
432 
433 		/* Determine length of variable name */
434 		name_len = strlen(name) + 1;
435 
436 		/* Write the variable name and '=' delimiter */
437 		if (olen >= name_len) {
438 			/* Copy name */
439 			memcpy(outp, name, name_len - 1);
440 
441 			/* Append '=' */
442 			*(outp + name_len - 1) = '=';
443 		}
444 
445 		/* Adjust byte counts */
446 		if (SIZE_MAX - name_len < nbytes)
447 			return (ERANGE);
448 
449 		nbytes += name_len;
450 
451 		/* Reposition output */
452 		if (limit > nbytes) {
453 			outp = (uint8_t *)buf + nbytes;
454 			olen = limit - nbytes;
455 		} else {
456 			outp = NULL;
457 			olen = 0;
458 		}
459 
460 		/* Coerce to NUL-terminated C string, writing to the output
461 		 * buffer (or just calculating the length if outp is NULL) */
462 		val_len = olen;
463 		error = bhnd_nvram_data_getvar(nv, cookiep, outp, &val_len,
464 		    BHND_NVRAM_TYPE_STRING);
465 
466 		if (error && error != ENOMEM)
467 			return (error);
468 
469 		/* Adjust byte counts */
470 		if (SIZE_MAX - val_len < nbytes)
471 			return (ERANGE);
472 
473 		nbytes += val_len;
474 	}
475 
476 	/* Write terminating NUL */
477 	if (nbytes < limit)
478 		*((uint8_t *)buf + nbytes) = '\0';
479 	nbytes++;
480 
481 	/* Provide actual size */
482 	*len = nbytes;
483 	if (buf == NULL || nbytes > limit) {
484 		if (buf != NULL)
485 			return (ENOMEM);
486 
487 		return (0);
488 	}
489 
490 	/* Fetch current NVRAM header */
491 	if ((error = bhnd_nvram_io_read(bcm->data, 0x0, &hdr, sizeof(hdr))))
492 		return (error);
493 
494 	/* Update values covered by CRC and write to output buffer */
495 	hdr.size = htole32(*len);
496 	memcpy(buf, &hdr, sizeof(hdr));
497 
498 	/* Calculate new CRC */
499 	crc = bhnd_nvram_crc8((uint8_t *)buf + BCM_NVRAM_CRC_SKIP,
500 	    *len - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
501 
502 	/* Update header with valid CRC */
503 	hdr.cfg0 &= ~BCM_NVRAM_CFG0_CRC_MASK;
504 	hdr.cfg0 |= (crc << BCM_NVRAM_CFG0_CRC_SHIFT);
505 	memcpy(buf, &hdr, sizeof(hdr));
506 
507 	return (0);
508 }
509 
510 static uint32_t
511 bhnd_nvram_bcm_caps(struct bhnd_nvram_data *nv)
512 {
513 	return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
514 }
515 
516 static const char *
517 bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep)
518 {
519 	struct bhnd_nvram_bcm		*bcm;
520 	struct bhnd_nvram_bcm_hvar	*hvar, *hvar_next;
521 	const void			*ptr;
522 	const char			*envp, *basep;
523 	size_t				 io_size, io_offset;
524 	int				 error;
525 
526 	bcm = (struct bhnd_nvram_bcm *)nv;
527 
528 	io_offset = sizeof(struct bhnd_nvram_bcmhdr);
529 	io_size = bhnd_nvram_io_getsize(bcm->data) - io_offset;
530 
531 	/* Map backing buffer */
532 	error = bhnd_nvram_io_read_ptr(bcm->data, io_offset, &ptr, io_size,
533 	    NULL);
534 	if (error) {
535 		BHND_NV_LOG("error mapping backing buffer: %d\n", error);
536 		return (NULL);
537 	}
538 
539 	basep = ptr;
540 
541 	/* If cookiep pointers into our header variable array, handle as header
542 	 * variable iteration. */
543 	hvar = bhnd_nvram_bcm_to_hdrvar(bcm, *cookiep);
544 	if (hvar != NULL) {
545 		size_t idx;
546 
547 		/* Advance to next entry, if any */
548 		idx = bhnd_nvram_bcm_hdrvar_index(bcm, hvar) + 1;
549 
550 		/* Find the next header-defined variable that isn't defined in
551 		 * the NVRAM data, start iteration there */
552 		for (size_t i = idx; i < nitems(bcm->hvars); i++) {
553 			hvar_next = &bcm->hvars[i];
554 			if (hvar_next->envp != NULL && !hvar_next->stale)
555 				continue;
556 
557 			*cookiep = hvar_next;
558 			return (hvar_next->name);
559 		}
560 
561 		/* No further header-defined variables; iteration
562 		 * complete */
563 		return (NULL);
564 	}
565 
566 	/* Handle standard NVRAM data iteration */
567 	if (*cookiep == NULL) {
568 		/* Start at the first NVRAM data record */
569 		envp = basep;
570 	} else {
571 		/* Seek to next record */
572 		envp = *cookiep;
573 		envp += strlen(envp) + 1;	/* key + '\0' */
574 		envp += strlen(envp) + 1;	/* value + '\0' */
575 	}
576 
577 	/*
578 	 * Skip entries that have an existing header variable entry that takes
579 	 * precedence over the NVRAM data value.
580 	 *
581 	 * The header's value will be provided when performing header variable
582 	 * iteration
583 	 */
584 	 while ((size_t)(envp - basep) < io_size && *envp != '\0') {
585 		/* Locate corresponding header variable */
586 		hvar = NULL;
587 		for (size_t i = 0; i < nitems(bcm->hvars); i++) {
588 			if (bcm->hvars[i].envp != envp)
589 				continue;
590 
591 			hvar = &bcm->hvars[i];
592 			break;
593 		}
594 
595 		/* If no corresponding hvar entry, or the entry does not take
596 		 * precedence over this NVRAM value, we can safely return this
597 		 * value as-is. */
598 		if (hvar == NULL || !hvar->stale)
599 			break;
600 
601 		/* Seek to next record */
602 		envp += strlen(envp) + 1;	/* key + '\0' */
603 		envp += strlen(envp) + 1;	/* value + '\0' */
604 	 }
605 
606 	/* On NVRAM data EOF, try switching to header variables */
607 	if ((size_t)(envp - basep) == io_size || *envp == '\0') {
608 		/* Find first valid header variable */
609 		for (size_t i = 0; i < nitems(bcm->hvars); i++) {
610 			if (bcm->hvars[i].envp != NULL)
611 				continue;
612 
613 			*cookiep = &bcm->hvars[i];
614 			return (bcm->hvars[i].name);
615 		}
616 
617 		/* No header variables */
618 		return (NULL);
619 	}
620 
621 	*cookiep = (void *)(uintptr_t)envp;
622 	return (envp);
623 }
624 
625 static void *
626 bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name)
627 {
628 	return (bhnd_nvram_data_generic_find(nv, name));
629 }
630 
631 static int
632 bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
633     size_t *len, bhnd_nvram_type type)
634 {
635 	return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
636 }
637 
638 static const void *
639 bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
640     size_t *len, bhnd_nvram_type *type)
641 {
642 	struct bhnd_nvram_bcm		*bcm;
643 	struct bhnd_nvram_bcm_hvar	*hvar;
644 	const char			*envp;
645 
646 	bcm = (struct bhnd_nvram_bcm *)nv;
647 
648 	/* Handle header variables */
649 	if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
650 		BHND_NV_ASSERT(
651 		    hvar->len % bhnd_nvram_value_size(hvar->type, NULL, 0,
652 			hvar->nelem) == 0,
653 		    ("length is not aligned to type width"));
654 
655 		*type = hvar->type;
656 		*len = hvar->len;
657 		return (&hvar->value);
658 	}
659 
660 	/* Cookie points to key\0value\0 -- get the value address */
661 	BHND_NV_ASSERT(cookiep != NULL, ("NULL cookiep"));
662 
663 	envp = cookiep;
664 	envp += strlen(envp) + 1;	/* key + '\0' */
665 	*len = strlen(envp) + 1;	/* value + '\0' */
666 	*type = BHND_NVRAM_TYPE_STRING;
667 
668 	return (envp);
669 }
670 
671 static const char *
672 bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
673 {
674 	struct bhnd_nvram_bcm		*bcm;
675 	struct bhnd_nvram_bcm_hvar	*hvar;
676 
677 	bcm = (struct bhnd_nvram_bcm *)nv;
678 
679 	/* Handle header variables */
680 	if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
681 		return (hvar->name);
682 	}
683 
684 	/* Cookie points to key\0value\0 */
685 	return (cookiep);
686 }
687 
688 /**
689  * Return the internal BCM data reference for a header-defined variable
690  * with @p name, or NULL if none exists.
691  */
692 static struct bhnd_nvram_bcm_hvar *
693 bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm *bcm, const char *name)
694 {
695 	for (size_t i = 0; i < nitems(bcm->hvars); i++) {
696 		if (strcmp(bcm->hvars[i].name, name) == 0)
697 			return (&bcm->hvars[i]);
698 	}
699 
700 	/* Not found */
701 	return (NULL);
702 }
703 
704 /**
705  * If @p cookiep references a header-defined variable, return the
706  * internal BCM data reference. Otherwise, returns NULL.
707  */
708 static struct bhnd_nvram_bcm_hvar *
709 bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm *bcm, void *cookiep)
710 {
711 #ifdef BHND_NVRAM_INVARIANTS
712 	uintptr_t base, ptr;
713 #endif
714 
715 	/* If the cookie falls within the hvar array, it's a
716 	 * header variable cookie */
717 	if (nitems(bcm->hvars) == 0)
718 		return (NULL);
719 
720 	if (cookiep < (void *)&bcm->hvars[0])
721 		return (NULL);
722 
723 	if (cookiep > (void *)&bcm->hvars[nitems(bcm->hvars)-1])
724 		return (NULL);
725 
726 #ifdef BHND_NVRAM_INVARIANTS
727 	base = (uintptr_t)bcm->hvars;
728 	ptr = (uintptr_t)cookiep;
729 
730 	BHND_NV_ASSERT((ptr - base) % sizeof(bcm->hvars[0]) == 0,
731 	    ("misaligned hvar pointer %p/%p", cookiep, bcm->hvars));
732 #endif /* INVARIANTS */
733 
734 	return ((struct bhnd_nvram_bcm_hvar *)cookiep);
735 }
736 
737 /**
738  * Return the index of @p hdrvar within @p bcm's backing hvars array.
739  */
740 static size_t
741 bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm *bcm,
742     struct bhnd_nvram_bcm_hvar *hdrvar)
743 {
744 	BHND_NV_ASSERT(bhnd_nvram_bcm_to_hdrvar(bcm, (void *)hdrvar) != NULL,
745 	    ("%p is not a valid hdrvar reference", hdrvar));
746 
747 	return (hdrvar - &bcm->hvars[0]);
748 }
749