2 * Copyright (c) 2015-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/endian.h>
37 #include <sys/param.h>
38 #include <sys/ctype.h>
39 #include <sys/malloc.h>
40 #include <sys/systm.h>
51 #include "bhnd_nvram_private.h"
53 #include "bhnd_nvram_datavar.h"
55 #include "bhnd_nvram_data_bcmreg.h" /* for BCM_NVRAM_MAGIC */
58 * Broadcom "Board Text" data class.
60 * This format is used to provide external NVRAM data for some
61 * fullmac WiFi devices, and as an input format when programming
65 struct bhnd_nvram_btxt {
66 struct bhnd_nvram_data nv; /**< common instance state */
67 struct bhnd_nvram_io *data; /**< memory-backed board text data */
68 size_t count; /**< variable count */
71 BHND_NVRAM_DATA_CLASS_DEFN(btxt, "Broadcom Board Text",
72 BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_btxt))
74 /** Minimal identification header */
75 union bhnd_nvram_btxt_ident {
80 static void *bhnd_nvram_btxt_offset_to_cookiep(struct bhnd_nvram_btxt *btxt,
82 static size_t bhnd_nvram_btxt_cookiep_to_offset(struct bhnd_nvram_btxt *btxt,
85 static int bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io,
86 size_t offset, size_t *line_len, size_t *env_len);
87 static int bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io,
89 static int bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io,
93 bhnd_nvram_btxt_probe(struct bhnd_nvram_io *io)
95 union bhnd_nvram_btxt_ident ident;
99 /* Look at the initial header for something that looks like
100 * an ASCII board text file */
101 if ((error = bhnd_nvram_io_read(io, 0x0, &ident, sizeof(ident))))
104 /* The BCM NVRAM format uses a 'FLSH' little endian magic value, which
105 * shouldn't be interpreted as BTXT */
106 if (le32toh(ident.bcm_magic) == BCM_NVRAM_MAGIC)
109 /* Don't match on non-ASCII/non-printable data */
110 for (size_t i = 0; i < nitems(ident.btxt); i++) {
112 if (!bhnd_nv_isprint(c))
116 /* The first character should either be a valid key char (alpha),
117 * whitespace, or the start of a comment ('#') */
119 if (!bhnd_nv_isspace(c) && !bhnd_nv_isalpha(c) && c != '#')
122 /* We assert a low priority, given that we've only scanned an
123 * initial few bytes of the file. */
124 return (BHND_NVRAM_DATA_PROBE_MAYBE);
129 * Parser states for bhnd_nvram_bcm_getvar_direct_common().
132 BTXT_PARSE_LINE_START,
135 BTXT_PARSE_NEXT_LINE,
136 BTXT_PARSE_VALUE_START,
141 bhnd_nvram_btxt_getvar_direct(struct bhnd_nvram_io *io, const char *name,
142 void *outp, size_t *olen, bhnd_nvram_type otype)
145 btxt_parse_state pstate;
146 size_t limit, offset;
147 size_t buflen, bufpos;
148 size_t namelen, namepos;
152 limit = bhnd_nvram_io_getsize(io);
155 /* Loop our parser until we find the requested variable, or hit EOF */
156 pstate = BTXT_PARSE_LINE_START;
159 namelen = strlen(name);
163 while ((offset - bufpos) < limit) {
164 BHND_NV_ASSERT(bufpos <= buflen,
165 ("buf position invalid (%zu > %zu)", bufpos, buflen));
166 BHND_NV_ASSERT(buflen <= sizeof(buf),
167 ("buf length invalid (%zu > %zu", buflen, sizeof(buf)));
169 /* Repopulate our parse buffer? */
170 if (buflen - bufpos == 0) {
171 BHND_NV_ASSERT(offset < limit, ("offset overrun"));
173 buflen = bhnd_nv_ummin(sizeof(buf), limit - offset);
176 error = bhnd_nvram_io_read(io, offset, buf, buflen);
184 case BTXT_PARSE_LINE_START:
185 BHND_NV_ASSERT(bufpos < buflen, ("empty buffer!"));
187 /* Reset name matching position */
190 /* Trim any leading whitespace */
191 while (bufpos < buflen && bhnd_nv_isspace(buf[bufpos]))
196 if (bufpos == buflen) {
197 /* Continue parsing the line */
198 pstate = BTXT_PARSE_LINE_START;
199 } else if (bufpos < buflen && buf[bufpos] == '#') {
200 /* Comment; skip to next line */
201 pstate = BTXT_PARSE_NEXT_LINE;
203 /* Start name matching */
204 pstate = BTXT_PARSE_KEY;
210 case BTXT_PARSE_KEY: {
211 size_t navail, nleft;
213 nleft = namelen - namepos;
214 navail = bhnd_nv_ummin(buflen - bufpos, nleft);
216 if (strncmp(name+namepos, buf+bufpos, navail) == 0) {
221 if (namepos == namelen) {
222 /* Matched the full variable; look for
223 * its trailing delimiter */
224 pstate = BTXT_PARSE_KEY_END;
226 /* Continue matching the name */
227 pstate = BTXT_PARSE_KEY;
230 /* No match; advance to next entry and restart
232 pstate = BTXT_PARSE_NEXT_LINE;
238 case BTXT_PARSE_KEY_END:
239 BHND_NV_ASSERT(bufpos < buflen, ("empty buffer!"));
241 if (buf[bufpos] == '=') {
242 /* Key fully matched; advance past '=' and
245 pstate = BTXT_PARSE_VALUE_START;
247 /* No match; advance to next line and restart
249 pstate = BTXT_PARSE_NEXT_LINE;
254 case BTXT_PARSE_NEXT_LINE: {
257 /* Scan for a '\r', '\n', or '\r\n' terminator */
258 p = memchr(buf+bufpos, '\n', buflen - bufpos);
260 p = memchr(buf+bufpos, '\r', buflen - bufpos);
263 /* Found entry terminator; restart name
264 * matching at next line */
265 pstate = BTXT_PARSE_LINE_START;
268 /* Consumed full buffer looking for newline;
269 * force repopulation of the buffer and
271 pstate = BTXT_PARSE_NEXT_LINE;
278 case BTXT_PARSE_VALUE_START: {
281 /* Scan for a terminating newline */
282 p = memchr(buf+bufpos, '\n', buflen - bufpos);
284 p = memchr(buf+bufpos, '\r', buflen - bufpos);
287 /* Found entry terminator; parse the value */
288 vlen = p - &buf[bufpos];
289 pstate = BTXT_PARSE_VALUE;
291 } else if (p == NULL && offset == limit) {
292 /* Hit EOF without a terminating newline;
293 * treat the entry as implicitly terminated */
294 vlen = buflen - bufpos;
295 pstate = BTXT_PARSE_VALUE;
297 } else if (p == NULL && bufpos > 0) {
300 /* Move existing value data to start of
302 memmove(buf, buf+bufpos, buflen - bufpos);
306 /* Populate full buffer to allow retry of
308 nread = bhnd_nv_ummin(sizeof(buf) - buflen,
311 error = bhnd_nvram_io_read(io, offset,
319 /* Value exceeds our buffer capacity */
320 BHND_NV_LOG("cannot parse value for '%s' "
321 "(exceeds %zu byte limit)\n", name,
330 case BTXT_PARSE_VALUE:
331 BHND_NV_ASSERT(vlen <= buflen, ("value buf overrun"));
333 /* Trim any trailing whitespace */
334 while (vlen > 0 && bhnd_nv_isspace(buf[bufpos+vlen-1]))
337 /* Write the value to the caller's buffer */
338 return (bhnd_nvram_value_coerce(buf+bufpos, vlen,
339 BHND_NVRAM_TYPE_STRING, outp, olen, otype));
343 /* Variable not found */
348 bhnd_nvram_btxt_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
349 bhnd_nvram_plist *options, void *outp, size_t *olen)
351 bhnd_nvram_prop *prop;
352 size_t limit, nbytes;
355 /* Determine output byte limit */
363 /* Write all properties */
365 while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
369 size_t name_len, value_len;
371 if (outp == NULL || limit < nbytes) {
375 p = ((char *)outp) + nbytes;
376 prop_limit = limit - nbytes;
379 /* Fetch and write 'name=' to output */
380 name = bhnd_nvram_prop_name(prop);
381 name_len = strlen(name) + 1;
383 if (prop_limit > name_len) {
384 memcpy(p, name, name_len - 1);
385 p[name_len - 1] = '=';
387 prop_limit -= name_len;
394 /* Advance byte count */
395 if (SIZE_MAX - nbytes < name_len)
396 return (EFTYPE); /* would overflow size_t */
400 /* Write NUL-terminated value to output, rewrite NUL as
401 * '\n' record delimiter */
402 value_len = prop_limit;
403 error = bhnd_nvram_prop_encode(prop, p, &value_len,
404 BHND_NVRAM_TYPE_STRING);
405 if (p != NULL && error == 0) {
406 /* Replace trailing '\0' with newline */
407 BHND_NV_ASSERT(value_len > 0, ("string length missing "
408 "minimum required trailing NUL"));
410 *(p + (value_len - 1)) = '\n';
411 } else if (error && error != ENOMEM) {
412 /* If encoding failed for any reason other than ENOMEM
413 * (which we'll detect and report after encoding all
414 * properties), return immediately */
415 BHND_NV_LOG("error serializing %s to required type "
417 bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
422 /* Advance byte count */
423 if (SIZE_MAX - nbytes < value_len)
424 return (EFTYPE); /* would overflow size_t */
429 /* Provide required length */
442 * Initialize @p btxt with the provided board text data mapped by @p src.
444 * @param btxt A newly allocated data instance.
447 bhnd_nvram_btxt_init(struct bhnd_nvram_btxt *btxt, struct bhnd_nvram_io *src)
450 const char *name, *value;
451 size_t name_len, value_len;
452 size_t line_len, env_len;
453 size_t io_offset, io_size, str_size;
456 BHND_NV_ASSERT(btxt->data == NULL, ("btxt data already allocated"));
458 if ((btxt->data = bhnd_nvram_iobuf_copy(src)) == NULL)
461 io_size = bhnd_nvram_io_getsize(btxt->data);
464 /* Fetch a pointer mapping the entirity of the board text data */
465 error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_size, NULL);
469 /* Determine the actual size, minus any terminating NUL. We
470 * parse NUL-terminated C strings, but do not include NUL termination
471 * in our internal or serialized representations */
472 str_size = strnlen(ptr, io_size);
474 /* If the terminating NUL is not found at the end of the buffer,
475 * this is BCM-RAW or other NUL-delimited NVRAM format. */
476 if (str_size < io_size && str_size + 1 < io_size)
479 /* Adjust buffer size to account for NUL termination (if any) */
481 if ((error = bhnd_nvram_io_setsize(btxt->data, io_size)))
484 /* Process the buffer */
486 while (io_offset < io_size) {
489 /* Seek to the next key=value entry */
490 if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset)))
493 /* Determine the entry and line length */
494 error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset,
495 &line_len, &env_len);
501 BHND_NV_ASSERT(io_offset == io_size,
502 ("zero-length record returned from "
503 "bhnd_nvram_btxt_seek_next()"));
507 /* Fetch a pointer to the line start */
508 error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &envp,
513 /* Parse the key=value string */
514 error = bhnd_nvram_parse_env(envp, env_len, '=', &name,
515 &name_len, &value, &value_len);
520 /* Insert a '\0' character, replacing the '=' delimiter and
521 * allowing us to vend references directly to the variable
523 error = bhnd_nvram_io_write(btxt->data, io_offset+name_len,
528 /* Add to variable count */
531 /* Advance past EOL */
532 io_offset += line_len;
539 bhnd_nvram_btxt_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
541 struct bhnd_nvram_btxt *btxt;
544 /* Allocate and initialize the BTXT data instance */
545 btxt = (struct bhnd_nvram_btxt *)nv;
547 /* Parse the BTXT input data and initialize our backing
548 * data representation */
549 if ((error = bhnd_nvram_btxt_init(btxt, io))) {
550 bhnd_nvram_btxt_free(nv);
558 bhnd_nvram_btxt_free(struct bhnd_nvram_data *nv)
560 struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv;
561 if (btxt->data != NULL)
562 bhnd_nvram_io_free(btxt->data);
566 bhnd_nvram_btxt_count(struct bhnd_nvram_data *nv)
568 struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv;
569 return (btxt->count);
572 static bhnd_nvram_plist *
573 bhnd_nvram_btxt_options(struct bhnd_nvram_data *nv)
579 bhnd_nvram_btxt_caps(struct bhnd_nvram_data *nv)
581 return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
585 bhnd_nvram_btxt_find(struct bhnd_nvram_data *nv, const char *name)
587 return (bhnd_nvram_data_generic_find(nv, name));
591 bhnd_nvram_btxt_next(struct bhnd_nvram_data *nv, void **cookiep)
593 struct bhnd_nvram_btxt *btxt;
595 size_t io_offset, io_size;
598 btxt = (struct bhnd_nvram_btxt *)nv;
600 io_size = bhnd_nvram_io_getsize(btxt->data);
602 if (*cookiep == NULL) {
603 /* Start search at initial file offset */
606 /* Start search after the current entry */
607 io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, *cookiep);
609 /* Scan past the current entry by finding the next newline */
610 error = bhnd_nvram_btxt_seek_eol(btxt->data, &io_offset);
612 BHND_NV_LOG("unexpected error in seek_eol(): %d\n",
618 /* Already at EOF? */
619 if (io_offset == io_size)
622 /* Seek to the first valid entry, or EOF */
623 if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset))) {
624 BHND_NV_LOG("unexpected error in seek_next(): %d\n", error);
629 if (io_offset == io_size)
632 /* Provide the new cookie for this offset */
633 *cookiep = bhnd_nvram_btxt_offset_to_cookiep(btxt, io_offset);
635 /* Fetch the name pointer; it must be at least 1 byte long */
636 error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &nptr, 1, NULL);
638 BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error);
642 /* Return the name pointer */
647 bhnd_nvram_btxt_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
650 if (cookiep1 < cookiep2)
653 if (cookiep1 > cookiep2)
660 bhnd_nvram_btxt_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
661 size_t *len, bhnd_nvram_type type)
663 return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
667 bhnd_nvram_btxt_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
668 bhnd_nvram_val **value)
670 return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
674 bhnd_nvram_btxt_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
675 size_t *len, bhnd_nvram_type *type)
677 struct bhnd_nvram_btxt *btxt;
680 size_t io_offset, io_size;
681 size_t line_len, env_len;
684 btxt = (struct bhnd_nvram_btxt *)nv;
686 io_size = bhnd_nvram_io_getsize(btxt->data);
687 io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, cookiep);
690 if (io_offset == io_size)
693 /* Determine the entry length */
694 error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset, &line_len,
697 BHND_NV_LOG("unexpected error in entry_len(): %d\n", error);
701 /* Fetch the entry's value pointer and length */
702 error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &eptr, env_len,
705 BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error);
709 error = bhnd_nvram_parse_env(eptr, env_len, '\0', NULL, NULL, &vptr,
712 BHND_NV_LOG("unexpected error in parse_env(): %d\n", error);
716 /* Type is always CSTR */
717 *type = BHND_NVRAM_TYPE_STRING;
723 bhnd_nvram_btxt_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
725 struct bhnd_nvram_btxt *btxt;
727 size_t io_offset, io_size;
730 btxt = (struct bhnd_nvram_btxt *)nv;
732 io_size = bhnd_nvram_io_getsize(btxt->data);
733 io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, cookiep);
736 if (io_offset == io_size)
737 BHND_NV_PANIC("invalid cookiep: %p", cookiep);
739 /* Variable name is found directly at the given offset; trailing
740 * NUL means we can assume that it's at least 1 byte long */
741 error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &ptr, 1, NULL);
743 BHND_NV_PANIC("unexpected error in read_ptr(): %d\n", error);
749 * Return a cookiep for the given I/O offset.
752 bhnd_nvram_btxt_offset_to_cookiep(struct bhnd_nvram_btxt *btxt,
758 BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(btxt->data),
759 ("io_offset %zu out-of-range", io_offset));
760 BHND_NV_ASSERT(io_offset < UINTPTR_MAX,
761 ("io_offset %#zx exceeds UINTPTR_MAX", io_offset));
763 error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_offset, NULL);
765 BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error);
767 ptr = (const uint8_t *)ptr + io_offset;
768 return (__DECONST(void *, ptr));
771 /* Convert a cookiep back to an I/O offset */
773 bhnd_nvram_btxt_cookiep_to_offset(struct bhnd_nvram_btxt *btxt, void *cookiep)
780 BHND_NV_ASSERT(cookiep != NULL, ("null cookiep"));
782 io_size = bhnd_nvram_io_getsize(btxt->data);
783 error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_size, NULL);
785 BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error);
787 offset = (const uint8_t *)cookiep - (const uint8_t *)ptr;
788 BHND_NV_ASSERT(offset >= 0, ("invalid cookiep"));
789 BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)"));
790 BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)"));
792 return ((size_t)offset);
795 /* Determine the entry length and env 'key=value' string length of the entry
798 bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io, size_t offset,
799 size_t *line_len, size_t *env_len)
801 const uint8_t *baseptr, *p;
806 /* Fetch read buffer */
807 if ((error = bhnd_nvram_io_read_ptr(io, offset, &rbuf, 0, &nbytes)))
810 /* Find record termination (EOL, or '#') */
813 while ((size_t)(p - baseptr) < nbytes) {
814 if (*p == '#' || *p == '\n' || *p == '\r')
820 /* Got line length, now trim any trailing whitespace to determine
821 * actual env length */
822 *line_len = p - baseptr;
823 *env_len = *line_len;
825 for (size_t i = 0; i < *line_len; i++) {
826 char c = baseptr[*line_len - i - 1];
827 if (!bhnd_nv_isspace(c))
836 /* Seek past the next line ending (\r, \r\n, or \n) */
838 bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io, size_t *offset)
840 const uint8_t *baseptr, *p;
845 /* Fetch read buffer */
846 if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes)))
851 while ((size_t)(p - baseptr) < nbytes) {
854 /* Advance to next char. The next position may be EOF, in which
855 * case a read will be invalid */
859 /* CR, check for optional LF */
860 if ((size_t)(p - baseptr) < nbytes) {
866 } else if (c == '\n') {
871 /* Hit newline or EOF */
872 *offset += (p - baseptr);
876 /* Seek to the next valid non-comment line (or EOF) */
878 bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io, size_t *offset)
880 const uint8_t *baseptr, *p;
885 /* Fetch read buffer */
886 if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes)))
889 /* Skip leading whitespace and comments */
892 while ((size_t)(p - baseptr) < nbytes) {
895 /* Skip whitespace */
896 if (bhnd_nv_isspace(c)) {
901 /* Skip entire comment line */
903 size_t line_off = *offset + (p - baseptr);
905 if ((error = bhnd_nvram_btxt_seek_eol(io, &line_off)))
908 p = baseptr + (line_off - *offset);
912 /* Non-whitespace, non-comment */
916 *offset += (p - baseptr);
921 bhnd_nvram_btxt_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
922 bhnd_nvram_val *value, bhnd_nvram_val **result)
926 bhnd_nvram_type itype;
930 /* Name (trimmed of any path prefix) must be valid */
931 if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
934 /* Value must be bcm-formatted string */
935 error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
936 value, BHND_NVRAM_VAL_DYNAMIC);
940 /* Value string must not contain our record delimiter character ('\n'),
941 * or our comment character ('#') */
942 inp = bhnd_nvram_val_bytes(str, &ilen, &itype);
943 BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_STRING, ("non-string value"));
944 for (size_t i = 0; i < ilen; i++) {
948 BHND_NV_LOG("invalid character (%#hhx) in value\n",
950 bhnd_nvram_val_release(str);
955 /* Success. Transfer result ownership to the caller. */
961 bhnd_nvram_btxt_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
963 /* We permit deletion of any variable */