2 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
34 #include <sys/endian.h>
39 #include <sys/ctype.h>
40 #include <sys/malloc.h>
41 #include <sys/systm.h>
53 #include "bhnd_nvram_private.h"
55 #include "bhnd_nvram_datavar.h"
57 #include "bhnd_nvram_data_bcmreg.h"
58 #include "bhnd_nvram_data_bcmvar.h"
61 * Broadcom NVRAM data class.
63 * The Broadcom NVRAM NUL-delimited ASCII format is used by most
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.
71 struct bhnd_nvram_bcm;
73 static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_gethdrvar(
74 struct bhnd_nvram_bcm *bcm,
76 static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_to_hdrvar(
77 struct bhnd_nvram_bcm *bcm,
79 static size_t bhnd_nvram_bcm_hdrvar_index(
80 struct bhnd_nvram_bcm *bcm,
81 struct bhnd_nvram_bcm_hvar *hvar);
83 * Set of BCM NVRAM header values that are required to be mirrored in the
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.
90 * If they're modified in NVRAM, we need to sync the changes with the
91 * the NVRAM header values.
93 static const struct bhnd_nvram_bcm_hvar bhnd_nvram_bcm_hvars[] = {
95 .name = BCM_NVRAM_CFG0_SDRAM_INIT_VAR,
96 .type = BHND_NVRAM_TYPE_UINT16,
97 .len = sizeof(uint16_t),
101 .name = BCM_NVRAM_CFG1_SDRAM_CFG_VAR,
102 .type = BHND_NVRAM_TYPE_UINT16,
103 .len = sizeof(uint16_t),
107 .name = BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR,
108 .type = BHND_NVRAM_TYPE_UINT16,
109 .len = sizeof(uint16_t),
113 .name = BCM_NVRAM_SDRAM_NCDL_VAR,
114 .type = BHND_NVRAM_TYPE_UINT32,
115 .len = sizeof(uint32_t),
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 bhnd_nvram_plist *opts; /**< serialization options */
126 /** BCM header values */
127 struct bhnd_nvram_bcm_hvar hvars[nitems(bhnd_nvram_bcm_hvars)];
129 size_t count; /**< total variable count */
132 BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", BHND_NVRAM_DATA_CAP_DEVPATHS,
133 sizeof(struct bhnd_nvram_bcm))
136 bhnd_nvram_bcm_probe(struct bhnd_nvram_io *io)
138 struct bhnd_nvram_bcmhdr hdr;
141 if ((error = bhnd_nvram_io_read(io, 0x0, &hdr, sizeof(hdr))))
144 if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
147 if (le32toh(hdr.size) > bhnd_nvram_io_getsize(io))
150 return (BHND_NVRAM_DATA_PROBE_DEFAULT);
154 * Parser states for bhnd_nvram_bcm_getvar_direct_common().
161 BCM_PARSE_VALUE_START,
166 bhnd_nvram_bcm_getvar_direct(struct bhnd_nvram_io *io, const char *name,
167 void *outp, size_t *olen, bhnd_nvram_type otype)
169 return (bhnd_nvram_bcm_getvar_direct_common(io, name, outp, olen, otype,
174 * Common BCM/BCMRAW implementation of bhnd_nvram_getvar_direct().
177 bhnd_nvram_bcm_getvar_direct_common(struct bhnd_nvram_io *io, const char *name,
178 void *outp, size_t *olen, bhnd_nvram_type otype, bool have_header)
180 struct bhnd_nvram_bcmhdr hdr;
182 bcm_parse_state pstate;
183 size_t limit, offset;
184 size_t buflen, bufpos;
185 size_t namelen, namepos;
189 limit = bhnd_nvram_io_getsize(io);
192 /* Fetch and validate the header */
194 if ((error = bhnd_nvram_io_read(io, offset, &hdr, sizeof(hdr))))
197 if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
200 offset += sizeof(hdr);
201 limit = bhnd_nv_ummin(le32toh(hdr.size), limit);
204 /* Loop our parser until we find the requested variable, or hit EOF */
205 pstate = BCM_PARSE_KEY_START;
208 namelen = strlen(name);
212 while ((offset - bufpos) < limit) {
213 BHND_NV_ASSERT(bufpos <= buflen,
214 ("buf position invalid (%zu > %zu)", bufpos, buflen));
215 BHND_NV_ASSERT(buflen <= sizeof(buf),
216 ("buf length invalid (%zu > %zu", buflen, sizeof(buf)));
218 /* Repopulate our parse buffer? */
219 if (buflen - bufpos == 0) {
220 BHND_NV_ASSERT(offset < limit, ("offset overrun"));
222 buflen = bhnd_nv_ummin(sizeof(buf), limit - offset);
225 error = bhnd_nvram_io_read(io, offset, buf, buflen);
233 case BCM_PARSE_KEY_START:
234 BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!"));
236 /* An extra '\0' denotes NVRAM EOF */
237 if (buf[bufpos] == '\0')
240 /* Reset name matching position */
243 /* Start name matching */
244 pstate = BCM_PARSE_KEY_CONT;
247 case BCM_PARSE_KEY_CONT: {
248 size_t navail, nleft;
250 nleft = namelen - namepos;
251 navail = bhnd_nv_ummin(buflen - bufpos, nleft);
253 if (strncmp(name+namepos, buf+bufpos, navail) == 0) {
258 /* If we've matched the full variable name,
259 * look for its trailing delimiter */
260 if (namepos == namelen)
261 pstate = BCM_PARSE_KEY;
263 /* No match; advance to next entry and restart
265 pstate = BCM_PARSE_NEXT_KEY;
272 BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!"));
274 if (buf[bufpos] == '=') {
275 /* Key fully matched; advance past '=' and
278 pstate = BCM_PARSE_VALUE_START;
280 /* No match; advance to next entry and restart
282 pstate = BCM_PARSE_NEXT_KEY;
287 case BCM_PARSE_NEXT_KEY: {
290 /* Scan for a '\0' terminator */
291 p = memchr(buf+bufpos, '\0', buflen - bufpos);
294 /* Found entry terminator; restart name
295 * matching at next entry */
296 pstate = BCM_PARSE_KEY_START;
297 bufpos = (p - buf) + 1 /* skip '\0' */;
299 /* Consumed full buffer looking for '\0';
300 * force repopulation of the buffer and
308 case BCM_PARSE_VALUE_START: {
311 /* Scan for a '\0' terminator */
312 p = memchr(buf+bufpos, '\0', buflen - bufpos);
315 /* Found entry terminator; parse the value */
316 vlen = p - &buf[bufpos];
317 pstate = BCM_PARSE_VALUE;
319 } else if (p == NULL && offset == limit) {
320 /* Hit EOF without a terminating '\0';
321 * treat the entry as implicitly terminated */
322 vlen = buflen - bufpos;
323 pstate = BCM_PARSE_VALUE;
325 } else if (p == NULL && bufpos > 0) {
328 /* Move existing value data to start of
330 memmove(buf, buf+bufpos, buflen - bufpos);
334 /* Populate full buffer to allow retry of
336 nread = bhnd_nv_ummin(sizeof(buf) - buflen,
339 error = bhnd_nvram_io_read(io, offset,
347 /* Value exceeds our buffer capacity */
348 BHND_NV_LOG("cannot parse value for '%s' "
349 "(exceeds %zu byte limit)\n", name,
358 case BCM_PARSE_VALUE:
359 BHND_NV_ASSERT(vlen <= buflen, ("value buf overrun"));
361 return (bhnd_nvram_value_coerce(buf+bufpos, vlen,
362 BHND_NVRAM_TYPE_STRING, outp, olen, otype));
366 /* Variable not found */
371 bhnd_nvram_bcm_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
372 bhnd_nvram_plist *options, void *outp, size_t *olen)
374 struct bhnd_nvram_bcmhdr hdr;
375 bhnd_nvram_prop *prop;
376 size_t limit, nbytes;
378 uint16_t sdram_init, sdram_cfg, sdram_refresh;
379 uint8_t bcm_ver, crc8;
382 /* Determine output byte limit */
388 /* Fetch required header variables */
389 #define PROPS_GET_HDRVAR(_name, _dest, _type) do { \
390 const char *name = BCM_NVRAM_ ## _name ## _VAR; \
391 if (!bhnd_nvram_plist_contains(props, name)) { \
392 BHND_NV_LOG("missing required property: %s\n", \
397 error = bhnd_nvram_plist_get_encoded(props, name, \
398 (_dest), sizeof(*(_dest)), \
399 BHND_NVRAM_TYPE_ ##_type); \
401 BHND_NV_LOG("error reading required header " \
402 "%s property: %d\n", name, error); \
407 PROPS_GET_HDRVAR(SDRAM_NCDL, &sdram_ncdl, UINT32);
408 PROPS_GET_HDRVAR(CFG0_SDRAM_INIT, &sdram_init, UINT16);
409 PROPS_GET_HDRVAR(CFG1_SDRAM_CFG, &sdram_cfg, UINT16);
410 PROPS_GET_HDRVAR(CFG1_SDRAM_REFRESH, &sdram_refresh, UINT16);
412 #undef PROPS_GET_HDRVAR
414 /* Fetch BCM nvram version from options */
415 if (options != NULL &&
416 bhnd_nvram_plist_contains(options, BCM_NVRAM_ENCODE_OPT_VERSION))
418 error = bhnd_nvram_plist_get_uint8(options,
419 BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver);
421 BHND_NV_LOG("error reading %s uint8 option value: %d\n",
422 BCM_NVRAM_ENCODE_OPT_VERSION, error);
426 bcm_ver = BCM_NVRAM_CFG0_VER_DEFAULT;
429 /* Construct our header */
430 hdr = (struct bhnd_nvram_bcmhdr) {
431 .magic = htole32(BCM_NVRAM_MAGIC),
435 .sdram_ncdl = htole32(sdram_ncdl)
438 hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, 0x0);
439 hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER, bcm_ver);
440 hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_SDRAM_INIT,
441 htole16(sdram_init));
443 hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_CFG,
445 hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_REFRESH,
446 htole16(sdram_refresh));
448 /* Write the header */
449 nbytes = sizeof(hdr);
451 memcpy(outp, &hdr, sizeof(hdr));
453 /* Write all properties */
455 while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
459 size_t name_len, value_len;
461 if (outp == NULL || limit < nbytes) {
465 p = ((char *)outp) + nbytes;
466 prop_limit = limit - nbytes;
469 /* Fetch and write name + '=' to output */
470 name = bhnd_nvram_prop_name(prop);
471 name_len = strlen(name) + 1;
473 if (prop_limit > name_len) {
474 memcpy(p, name, name_len - 1);
475 p[name_len - 1] = '=';
477 prop_limit -= name_len;
484 /* Advance byte count */
485 if (SIZE_MAX - nbytes < name_len)
486 return (EFTYPE); /* would overflow size_t */
490 /* Attempt to write NUL-terminated value to output */
491 value_len = prop_limit;
492 error = bhnd_nvram_prop_encode(prop, p, &value_len,
493 BHND_NVRAM_TYPE_STRING);
495 /* If encoding failed for any reason other than ENOMEM (which
496 * we'll detect and report after encoding all properties),
497 * return immediately */
498 if (error && error != ENOMEM) {
499 BHND_NV_LOG("error serializing %s to required type "
501 bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
506 /* Advance byte count */
507 if (SIZE_MAX - nbytes < value_len)
508 return (EFTYPE); /* would overflow size_t */
513 /* Write terminating '\0' */
515 *((char *)outp + nbytes) = '\0';
517 if (nbytes == SIZE_MAX)
518 return (EFTYPE); /* would overflow size_t */
522 /* Update header length; this must fit within the header's 32-bit size
524 if (nbytes <= UINT32_MAX) {
525 hdr.size = (uint32_t)nbytes;
527 BHND_NV_LOG("size %zu exceeds maximum supported size of %u "
528 "bytes\n", nbytes, UINT32_MAX);
532 /* Provide required length */
541 /* Calculate the CRC value */
542 BHND_NV_ASSERT(nbytes >= BCM_NVRAM_CRC_SKIP, ("invalid output size"));
543 crc8 = bhnd_nvram_crc8((uint8_t *)outp + BCM_NVRAM_CRC_SKIP,
544 nbytes - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
546 /* Update CRC and write the finalized header */
547 BHND_NV_ASSERT(nbytes >= sizeof(hdr), ("invalid output size"));
548 hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, crc8);
549 memcpy(outp, &hdr, sizeof(hdr));
555 * Initialize @p bcm with the provided NVRAM data mapped by @p src.
557 * @param bcm A newly allocated data instance.
560 bhnd_nvram_bcm_init(struct bhnd_nvram_bcm *bcm, struct bhnd_nvram_io *src)
562 struct bhnd_nvram_bcmhdr hdr;
565 size_t io_offset, io_size;
566 uint8_t crc, valid, bcm_ver;
569 if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr))))
572 if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
575 /* Fetch the actual NVRAM image size */
576 io_size = le32toh(hdr.size);
577 if (io_size < sizeof(hdr)) {
578 /* The header size must include the header itself */
579 BHND_NV_LOG("corrupt header size: %zu\n", io_size);
583 if (io_size > bhnd_nvram_io_getsize(src)) {
584 BHND_NV_LOG("header size %zu exceeds input size %zu\n",
585 io_size, bhnd_nvram_io_getsize(src));
589 /* Allocate a buffer large enough to hold the NVRAM image, and
590 * an extra EOF-signaling NUL (on the chance it's missing from the
592 if (io_size == SIZE_MAX)
595 bcm->data = bhnd_nvram_iobuf_empty(io_size, io_size + 1);
596 if (bcm->data == NULL)
599 /* Fetch a pointer into our backing buffer and copy in the
601 error = bhnd_nvram_io_write_ptr(bcm->data, 0x0, &ptr, io_size, NULL);
606 if ((error = bhnd_nvram_io_read(src, 0x0, p, io_size)))
610 valid = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC);
611 crc = bhnd_nvram_crc8(p + BCM_NVRAM_CRC_SKIP,
612 io_size - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
615 BHND_NV_LOG("warning: NVRAM CRC error (crc=%#hhx, "
616 "expected=%hhx)\n", crc, valid);
619 /* Populate header variable definitions */
620 #define BCM_READ_HDR_VAR(_name, _dest, _swap) do { \
621 struct bhnd_nvram_bcm_hvar *data; \
622 data = bhnd_nvram_bcm_gethdrvar(bcm, _name ##_VAR); \
623 BHND_NV_ASSERT(data != NULL, \
624 ("no such header variable: " __STRING(_name))); \
627 data->value. _dest = _swap(BCM_NVRAM_GET_BITS( \
628 hdr. _name ## _FIELD, _name)); \
631 BCM_READ_HDR_VAR(BCM_NVRAM_CFG0_SDRAM_INIT, u16, le16toh);
632 BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_CFG, u16, le16toh);
633 BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_REFRESH, u16, le16toh);
634 BCM_READ_HDR_VAR(BCM_NVRAM_SDRAM_NCDL, u32, le32toh);
636 _Static_assert(nitems(bcm->hvars) == 4, "missing initialization for"
637 "NVRAM header variable(s)");
639 #undef BCM_READ_HDR_VAR
641 /* Process the buffer */
643 io_offset = sizeof(hdr);
644 while (io_offset < io_size) {
646 const char *name, *value;
648 size_t name_len, value_len;
650 /* Parse the key=value string */
651 envp = (char *) (p + io_offset);
652 envp_len = strnlen(envp, io_size - io_offset);
653 error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,
654 &name_len, &value, &value_len);
656 BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",
661 /* Insert a '\0' character, replacing the '=' delimiter and
662 * allowing us to vend references directly to the variable
664 *(envp + name_len) = '\0';
666 /* Record any NVRAM variables that mirror our header variables.
667 * This is a brute-force search -- for the amount of data we're
668 * operating on, it shouldn't be an issue. */
669 for (size_t i = 0; i < nitems(bcm->hvars); i++) {
670 struct bhnd_nvram_bcm_hvar *hvar;
671 union bhnd_nvram_bcm_hvar_value hval;
674 hvar = &bcm->hvars[i];
676 /* Already matched? */
677 if (hvar->envp != NULL)
681 if ((strcmp(name, hvar->name)) != 0)
684 /* Save pointer to mirrored envp */
687 /* Check for stale value */
688 hval_len = sizeof(hval);
689 error = bhnd_nvram_value_coerce(value, value_len,
690 BHND_NVRAM_TYPE_STRING, &hval, &hval_len,
693 /* If parsing fails, we can likely only make
694 * things worse by trying to synchronize the
696 BHND_NV_LOG("error parsing header variable "
697 "'%s=%s': %d\n", name, value, error);
698 } else if (hval_len != hvar->len) {
700 } else if (memcmp(&hval, &hvar->value, hval_len) != 0) {
705 /* Seek past the value's terminating '\0' */
706 io_offset += envp_len;
707 if (io_offset == io_size) {
708 BHND_NV_LOG("missing terminating NUL at offset %#zx\n",
713 if (*(p + io_offset) != '\0') {
714 BHND_NV_LOG("invalid terminator '%#hhx' at offset "
715 "%#zx\n", *(p + io_offset), io_offset);
719 /* Update variable count */
722 /* Seek to the next record */
723 if (++io_offset == io_size) {
726 /* Hit EOF without finding a terminating NUL
727 * byte; we need to grow our buffer and append
730 if ((error = bhnd_nvram_io_setsize(bcm->data, io_size)))
735 error = bhnd_nvram_io_write(bcm->data, io_size-1, &ch,
741 /* Check for explicit EOF (encoded as a single empty NUL
742 * terminated string) */
743 if (*(p + io_offset) == '\0')
747 /* Add non-mirrored header variables to total count variable */
748 for (size_t i = 0; i < nitems(bcm->hvars); i++) {
749 if (bcm->hvars[i].envp == NULL)
753 /* Populate serialization options from our header */
754 bcm_ver = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER);
755 error = bhnd_nvram_plist_append_bytes(bcm->opts,
756 BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver, sizeof(bcm_ver),
757 BHND_NVRAM_TYPE_UINT8);
765 bhnd_nvram_bcm_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
767 struct bhnd_nvram_bcm *bcm;
770 bcm = (struct bhnd_nvram_bcm *)nv;
772 /* Populate default BCM mirrored header variable set */
773 _Static_assert(sizeof(bcm->hvars) == sizeof(bhnd_nvram_bcm_hvars),
774 "hvar declarations must match bhnd_nvram_bcm_hvars template");
775 memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars));
777 /* Allocate (empty) option list, to be populated by
778 * bhnd_nvram_bcm_init() */
779 bcm->opts = bhnd_nvram_plist_new();
780 if (bcm->opts == NULL)
783 /* Parse the BCM input data and initialize our backing
784 * data representation */
785 if ((error = bhnd_nvram_bcm_init(bcm, io))) {
786 bhnd_nvram_bcm_free(nv);
794 bhnd_nvram_bcm_free(struct bhnd_nvram_data *nv)
796 struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
798 if (bcm->data != NULL)
799 bhnd_nvram_io_free(bcm->data);
801 if (bcm->opts != NULL)
802 bhnd_nvram_plist_release(bcm->opts);
806 bhnd_nvram_bcm_count(struct bhnd_nvram_data *nv)
808 struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
812 static bhnd_nvram_plist *
813 bhnd_nvram_bcm_options(struct bhnd_nvram_data *nv)
815 struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
820 bhnd_nvram_bcm_caps(struct bhnd_nvram_data *nv)
822 return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
826 bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep)
828 struct bhnd_nvram_bcm *bcm;
829 struct bhnd_nvram_bcm_hvar *hvar, *hvar_next;
831 const char *envp, *basep;
832 size_t io_size, io_offset;
835 bcm = (struct bhnd_nvram_bcm *)nv;
837 io_offset = sizeof(struct bhnd_nvram_bcmhdr);
838 io_size = bhnd_nvram_io_getsize(bcm->data) - io_offset;
840 /* Map backing buffer */
841 error = bhnd_nvram_io_read_ptr(bcm->data, io_offset, &ptr, io_size,
844 BHND_NV_LOG("error mapping backing buffer: %d\n", error);
850 /* If cookiep pointers into our header variable array, handle as header
851 * variable iteration. */
852 hvar = bhnd_nvram_bcm_to_hdrvar(bcm, *cookiep);
856 /* Advance to next entry, if any */
857 idx = bhnd_nvram_bcm_hdrvar_index(bcm, hvar) + 1;
859 /* Find the next header-defined variable that isn't defined in
860 * the NVRAM data, start iteration there */
861 for (size_t i = idx; i < nitems(bcm->hvars); i++) {
862 hvar_next = &bcm->hvars[i];
863 if (hvar_next->envp != NULL && !hvar_next->stale)
866 *cookiep = hvar_next;
867 return (hvar_next->name);
870 /* No further header-defined variables; iteration
875 /* Handle standard NVRAM data iteration */
876 if (*cookiep == NULL) {
877 /* Start at the first NVRAM data record */
880 /* Seek to next record */
882 envp += strlen(envp) + 1; /* key + '\0' */
883 envp += strlen(envp) + 1; /* value + '\0' */
887 * Skip entries that have an existing header variable entry that takes
888 * precedence over the NVRAM data value.
890 * The header's value will be provided when performing header variable
893 while ((size_t)(envp - basep) < io_size && *envp != '\0') {
894 /* Locate corresponding header variable */
896 for (size_t i = 0; i < nitems(bcm->hvars); i++) {
897 if (bcm->hvars[i].envp != envp)
900 hvar = &bcm->hvars[i];
904 /* If no corresponding hvar entry, or the entry does not take
905 * precedence over this NVRAM value, we can safely return this
907 if (hvar == NULL || !hvar->stale)
910 /* Seek to next record */
911 envp += strlen(envp) + 1; /* key + '\0' */
912 envp += strlen(envp) + 1; /* value + '\0' */
915 /* On NVRAM data EOF, try switching to header variables */
916 if ((size_t)(envp - basep) == io_size || *envp == '\0') {
917 /* Find first valid header variable */
918 for (size_t i = 0; i < nitems(bcm->hvars); i++) {
919 if (bcm->hvars[i].envp != NULL)
922 *cookiep = &bcm->hvars[i];
923 return (bcm->hvars[i].name);
926 /* No header variables */
930 *cookiep = __DECONST(void *, envp);
935 bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name)
937 return (bhnd_nvram_data_generic_find(nv, name));
941 bhnd_nvram_bcm_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
944 struct bhnd_nvram_bcm *bcm;
945 struct bhnd_nvram_bcm_hvar *hvar1, *hvar2;
947 bcm = (struct bhnd_nvram_bcm *)nv;
949 hvar1 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep1);
950 hvar2 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep2);
952 /* Header variables are always ordered below any variables defined
954 if (hvar1 != NULL && hvar2 == NULL) {
955 return (1); /* hvar follows non-hvar */
956 } else if (hvar1 == NULL && hvar2 != NULL) {
957 return (-1); /* non-hvar precedes hvar */
960 /* Otherwise, both cookies are either hvars or non-hvars. We can
961 * safely fall back on pointer order, which will provide a correct
962 * ordering matching the behavior of bhnd_nvram_data_next() for
964 if (cookiep1 < cookiep2)
967 if (cookiep1 > cookiep2)
974 bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
975 size_t *len, bhnd_nvram_type type)
977 return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
981 bhnd_nvram_bcm_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
982 bhnd_nvram_val **value)
984 return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
988 bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
989 size_t *len, bhnd_nvram_type *type)
991 struct bhnd_nvram_bcm *bcm;
992 struct bhnd_nvram_bcm_hvar *hvar;
995 bcm = (struct bhnd_nvram_bcm *)nv;
997 /* Handle header variables */
998 if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
999 BHND_NV_ASSERT(bhnd_nvram_value_check_aligned(&hvar->value,
1000 hvar->len, hvar->type) == 0, ("value misaligned"));
1004 return (&hvar->value);
1007 /* Cookie points to key\0value\0 -- get the value address */
1008 BHND_NV_ASSERT(cookiep != NULL, ("NULL cookiep"));
1011 envp += strlen(envp) + 1; /* key + '\0' */
1012 *len = strlen(envp) + 1; /* value + '\0' */
1013 *type = BHND_NVRAM_TYPE_STRING;
1019 bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
1021 struct bhnd_nvram_bcm *bcm;
1022 struct bhnd_nvram_bcm_hvar *hvar;
1024 bcm = (struct bhnd_nvram_bcm *)nv;
1026 /* Handle header variables */
1027 if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
1028 return (hvar->name);
1031 /* Cookie points to key\0value\0 */
1036 bhnd_nvram_bcm_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
1037 bhnd_nvram_val *value, bhnd_nvram_val **result)
1039 bhnd_nvram_val *str;
1042 /* Name (trimmed of any path prefix) must be valid */
1043 if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
1046 /* Value must be bcm-formatted string */
1047 error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
1048 value, BHND_NVRAM_VAL_DYNAMIC);
1052 /* Success. Transfer result ownership to the caller. */
1058 bhnd_nvram_bcm_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
1060 /* We permit deletion of any variable */
1065 * Return the internal BCM data reference for a header-defined variable
1066 * with @p name, or NULL if none exists.
1068 static struct bhnd_nvram_bcm_hvar *
1069 bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm *bcm, const char *name)
1071 for (size_t i = 0; i < nitems(bcm->hvars); i++) {
1072 if (strcmp(bcm->hvars[i].name, name) == 0)
1073 return (&bcm->hvars[i]);
1081 * If @p cookiep references a header-defined variable, return the
1082 * internal BCM data reference. Otherwise, returns NULL.
1084 static struct bhnd_nvram_bcm_hvar *
1085 bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm *bcm, void *cookiep)
1087 #ifdef BHND_NVRAM_INVARIANTS
1088 uintptr_t base, ptr;
1091 /* If the cookie falls within the hvar array, it's a
1092 * header variable cookie */
1093 if (nitems(bcm->hvars) == 0)
1096 if (cookiep < (void *)&bcm->hvars[0])
1099 if (cookiep > (void *)&bcm->hvars[nitems(bcm->hvars)-1])
1102 #ifdef BHND_NVRAM_INVARIANTS
1103 base = (uintptr_t)bcm->hvars;
1104 ptr = (uintptr_t)cookiep;
1106 BHND_NV_ASSERT((ptr - base) % sizeof(bcm->hvars[0]) == 0,
1107 ("misaligned hvar pointer %p/%p", cookiep, bcm->hvars));
1108 #endif /* INVARIANTS */
1110 return ((struct bhnd_nvram_bcm_hvar *)cookiep);
1114 * Return the index of @p hdrvar within @p bcm's backing hvars array.
1117 bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm *bcm,
1118 struct bhnd_nvram_bcm_hvar *hdrvar)
1120 BHND_NV_ASSERT(bhnd_nvram_bcm_to_hdrvar(bcm, (void *)hdrvar) != NULL,
1121 ("%p is not a valid hdrvar reference", hdrvar));
1123 return (hdrvar - &bcm->hvars[0]);