1 /*- 2 * Copyright (c) 2015-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/endian.h> 34 35 #ifdef _KERNEL 36 37 #include <sys/param.h> 38 #include <sys/ctype.h> 39 #include <sys/malloc.h> 40 #include <sys/systm.h> 41 42 #else /* !_KERNEL */ 43 44 #include <ctype.h> 45 #include <stdint.h> 46 #include <stdlib.h> 47 #include <string.h> 48 49 #endif /* _KERNEL */ 50 51 #include "bhnd_nvram_private.h" 52 53 #include "bhnd_nvram_datavar.h" 54 55 #include "bhnd_nvram_data_bcmreg.h" /* for BCM_NVRAM_MAGIC */ 56 57 /** 58 * Broadcom "Board Text" data class. 59 * 60 * This format is used to provide external NVRAM data for some 61 * fullmac WiFi devices, and as an input format when programming 62 * NVRAM/SPROM/OTP. 63 */ 64 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 */ 69 }; 70 71 BHND_NVRAM_DATA_CLASS_DEFN(btxt, "Broadcom Board Text", 72 BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_btxt)) 73 74 /** Minimal identification header */ 75 union bhnd_nvram_btxt_ident { 76 uint32_t bcm_magic; 77 char btxt[8]; 78 }; 79 80 static void *bhnd_nvram_btxt_offset_to_cookiep(struct bhnd_nvram_btxt *btxt, 81 size_t io_offset); 82 static size_t bhnd_nvram_btxt_cookiep_to_offset(struct bhnd_nvram_btxt *btxt, 83 void *cookiep); 84 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, 88 size_t *offset); 89 static int bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io, 90 size_t *offset); 91 92 static int 93 bhnd_nvram_btxt_probe(struct bhnd_nvram_io *io) 94 { 95 union bhnd_nvram_btxt_ident ident; 96 char c; 97 int error; 98 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)))) 102 return (error); 103 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) 107 return (ENXIO); 108 109 /* Don't match on non-ASCII/non-printable data */ 110 for (size_t i = 0; i < nitems(ident.btxt); i++) { 111 c = ident.btxt[i]; 112 if (!bhnd_nv_isprint(c)) 113 return (ENXIO); 114 } 115 116 /* The first character should either be a valid key char (alpha), 117 * whitespace, or the start of a comment ('#') */ 118 c = ident.btxt[0]; 119 if (!bhnd_nv_isspace(c) && !bhnd_nv_isalpha(c) && c != '#') 120 return (ENXIO); 121 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); 125 } 126 127 static int 128 bhnd_nvram_btxt_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props, 129 bhnd_nvram_plist *options, void *outp, size_t *olen) 130 { 131 bhnd_nvram_prop *prop; 132 size_t limit, nbytes; 133 int error; 134 135 /* Determine output byte limit */ 136 if (outp != NULL) 137 limit = *olen; 138 else 139 limit = 0; 140 141 nbytes = 0; 142 143 /* Write all properties */ 144 prop = NULL; 145 while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) { 146 const char *name; 147 char *p; 148 size_t prop_limit; 149 size_t name_len, value_len; 150 151 if (outp == NULL || limit < nbytes) { 152 p = NULL; 153 prop_limit = 0; 154 } else { 155 p = ((char *)outp) + nbytes; 156 prop_limit = limit - nbytes; 157 } 158 159 /* Fetch and write 'name=' to output */ 160 name = bhnd_nvram_prop_name(prop); 161 name_len = strlen(name) + 1; 162 163 if (prop_limit > name_len) { 164 memcpy(p, name, name_len - 1); 165 p[name_len - 1] = '='; 166 167 prop_limit -= name_len; 168 p += name_len; 169 } else { 170 prop_limit = 0; 171 p = NULL; 172 } 173 174 /* Advance byte count */ 175 if (SIZE_MAX - nbytes < name_len) 176 return (EFTYPE); /* would overflow size_t */ 177 178 nbytes += name_len; 179 180 /* Write NUL-terminated value to output, rewrite NUL as 181 * '\n' record delimiter */ 182 value_len = prop_limit; 183 error = bhnd_nvram_prop_encode(prop, p, &value_len, 184 BHND_NVRAM_TYPE_STRING); 185 if (p != NULL && error == 0) { 186 /* Replace trailing '\0' with newline */ 187 BHND_NV_ASSERT(value_len > 0, ("string length missing " 188 "minimum required trailing NUL")); 189 190 *(p + (value_len - 1)) = '\n'; 191 } else if (error && error != ENOMEM) { 192 /* If encoding failed for any reason other than ENOMEM 193 * (which we'll detect and report after encoding all 194 * properties), return immediately */ 195 BHND_NV_LOG("error serializing %s to required type " 196 "%s: %d\n", name, 197 bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING), 198 error); 199 return (error); 200 } 201 202 /* Advance byte count */ 203 if (SIZE_MAX - nbytes < value_len) 204 return (EFTYPE); /* would overflow size_t */ 205 206 nbytes += value_len; 207 } 208 209 /* Provide required length */ 210 *olen = nbytes; 211 if (limit < *olen) { 212 if (outp == NULL) 213 return (0); 214 215 return (ENOMEM); 216 } 217 218 return (0); 219 } 220 221 /** 222 * Initialize @p btxt with the provided board text data mapped by @p src. 223 * 224 * @param btxt A newly allocated data instance. 225 */ 226 static int 227 bhnd_nvram_btxt_init(struct bhnd_nvram_btxt *btxt, struct bhnd_nvram_io *src) 228 { 229 const void *ptr; 230 const char *name, *value; 231 size_t name_len, value_len; 232 size_t line_len, env_len; 233 size_t io_offset, io_size, str_size; 234 int error; 235 236 BHND_NV_ASSERT(btxt->data == NULL, ("btxt data already allocated")); 237 238 if ((btxt->data = bhnd_nvram_iobuf_copy(src)) == NULL) 239 return (ENOMEM); 240 241 io_size = bhnd_nvram_io_getsize(btxt->data); 242 io_offset = 0; 243 244 /* Fetch a pointer mapping the entirity of the board text data */ 245 error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_size, NULL); 246 if (error) 247 return (error); 248 249 /* Determine the actual size, minus any terminating NUL. We 250 * parse NUL-terminated C strings, but do not include NUL termination 251 * in our internal or serialized representations */ 252 str_size = strnlen(ptr, io_size); 253 254 /* If the terminating NUL is not found at the end of the buffer, 255 * this is BCM-RAW or other NUL-delimited NVRAM format. */ 256 if (str_size < io_size && str_size + 1 < io_size) 257 return (EINVAL); 258 259 /* Adjust buffer size to account for NUL termination (if any) */ 260 io_size = str_size; 261 if ((error = bhnd_nvram_io_setsize(btxt->data, io_size))) 262 return (error); 263 264 /* Process the buffer */ 265 btxt->count = 0; 266 while (io_offset < io_size) { 267 const void *envp; 268 269 /* Seek to the next key=value entry */ 270 if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset))) 271 return (error); 272 273 /* Determine the entry and line length */ 274 error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset, 275 &line_len, &env_len); 276 if (error) 277 return (error); 278 279 /* EOF? */ 280 if (env_len == 0) { 281 BHND_NV_ASSERT(io_offset == io_size, 282 ("zero-length record returned from " 283 "bhnd_nvram_btxt_seek_next()")); 284 break; 285 } 286 287 /* Fetch a pointer to the line start */ 288 error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &envp, 289 env_len, NULL); 290 if (error) 291 return (error); 292 293 /* Parse the key=value string */ 294 error = bhnd_nvram_parse_env(envp, env_len, '=', &name, 295 &name_len, &value, &value_len); 296 if (error) { 297 return (error); 298 } 299 300 /* Insert a '\0' character, replacing the '=' delimiter and 301 * allowing us to vend references directly to the variable 302 * name */ 303 error = bhnd_nvram_io_write(btxt->data, io_offset+name_len, 304 &(char){'\0'}, 1); 305 if (error) 306 return (error); 307 308 /* Add to variable count */ 309 btxt->count++; 310 311 /* Advance past EOL */ 312 io_offset += line_len; 313 } 314 315 return (0); 316 } 317 318 static int 319 bhnd_nvram_btxt_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) 320 { 321 struct bhnd_nvram_btxt *btxt; 322 int error; 323 324 /* Allocate and initialize the BTXT data instance */ 325 btxt = (struct bhnd_nvram_btxt *)nv; 326 327 /* Parse the BTXT input data and initialize our backing 328 * data representation */ 329 if ((error = bhnd_nvram_btxt_init(btxt, io))) { 330 bhnd_nvram_btxt_free(nv); 331 return (error); 332 } 333 334 return (0); 335 } 336 337 static void 338 bhnd_nvram_btxt_free(struct bhnd_nvram_data *nv) 339 { 340 struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv; 341 if (btxt->data != NULL) 342 bhnd_nvram_io_free(btxt->data); 343 } 344 345 size_t 346 bhnd_nvram_btxt_count(struct bhnd_nvram_data *nv) 347 { 348 struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv; 349 return (btxt->count); 350 } 351 352 static bhnd_nvram_plist * 353 bhnd_nvram_btxt_options(struct bhnd_nvram_data *nv) 354 { 355 return (NULL); 356 } 357 358 static uint32_t 359 bhnd_nvram_btxt_caps(struct bhnd_nvram_data *nv) 360 { 361 return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); 362 } 363 364 static void * 365 bhnd_nvram_btxt_find(struct bhnd_nvram_data *nv, const char *name) 366 { 367 return (bhnd_nvram_data_generic_find(nv, name)); 368 } 369 370 static const char * 371 bhnd_nvram_btxt_next(struct bhnd_nvram_data *nv, void **cookiep) 372 { 373 struct bhnd_nvram_btxt *btxt; 374 const void *nptr; 375 size_t io_offset, io_size; 376 int error; 377 378 btxt = (struct bhnd_nvram_btxt *)nv; 379 380 io_size = bhnd_nvram_io_getsize(btxt->data); 381 382 if (*cookiep == NULL) { 383 /* Start search at initial file offset */ 384 io_offset = 0x0; 385 } else { 386 /* Start search after the current entry */ 387 io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, *cookiep); 388 389 /* Scan past the current entry by finding the next newline */ 390 error = bhnd_nvram_btxt_seek_eol(btxt->data, &io_offset); 391 if (error) { 392 BHND_NV_LOG("unexpected error in seek_eol(): %d\n", 393 error); 394 return (NULL); 395 } 396 } 397 398 /* Already at EOF? */ 399 if (io_offset == io_size) 400 return (NULL); 401 402 /* Seek to the first valid entry, or EOF */ 403 if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset))) { 404 BHND_NV_LOG("unexpected error in seek_next(): %d\n", error); 405 return (NULL); 406 } 407 408 /* Hit EOF? */ 409 if (io_offset == io_size) 410 return (NULL); 411 412 /* Provide the new cookie for this offset */ 413 *cookiep = bhnd_nvram_btxt_offset_to_cookiep(btxt, io_offset); 414 415 /* Fetch the name pointer; it must be at least 1 byte long */ 416 error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &nptr, 1, NULL); 417 if (error) { 418 BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error); 419 return (NULL); 420 } 421 422 /* Return the name pointer */ 423 return (nptr); 424 } 425 426 static int 427 bhnd_nvram_btxt_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1, 428 void *cookiep2) 429 { 430 if (cookiep1 < cookiep2) 431 return (-1); 432 433 if (cookiep1 > cookiep2) 434 return (1); 435 436 return (0); 437 } 438 439 static int 440 bhnd_nvram_btxt_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, 441 size_t *len, bhnd_nvram_type type) 442 { 443 return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); 444 } 445 446 static int 447 bhnd_nvram_btxt_copy_val(struct bhnd_nvram_data *nv, void *cookiep, 448 bhnd_nvram_val **value) 449 { 450 return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value)); 451 } 452 453 const void * 454 bhnd_nvram_btxt_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, 455 size_t *len, bhnd_nvram_type *type) 456 { 457 struct bhnd_nvram_btxt *btxt; 458 const void *eptr; 459 const char *vptr; 460 size_t io_offset, io_size; 461 size_t line_len, env_len; 462 int error; 463 464 btxt = (struct bhnd_nvram_btxt *)nv; 465 466 io_size = bhnd_nvram_io_getsize(btxt->data); 467 io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, cookiep); 468 469 /* At EOF? */ 470 if (io_offset == io_size) 471 return (NULL); 472 473 /* Determine the entry length */ 474 error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset, &line_len, 475 &env_len); 476 if (error) { 477 BHND_NV_LOG("unexpected error in entry_len(): %d\n", error); 478 return (NULL); 479 } 480 481 /* Fetch the entry's value pointer and length */ 482 error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &eptr, env_len, 483 NULL); 484 if (error) { 485 BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error); 486 return (NULL); 487 } 488 489 error = bhnd_nvram_parse_env(eptr, env_len, '\0', NULL, NULL, &vptr, 490 len); 491 if (error) { 492 BHND_NV_LOG("unexpected error in parse_env(): %d\n", error); 493 return (NULL); 494 } 495 496 /* Type is always CSTR */ 497 *type = BHND_NVRAM_TYPE_STRING; 498 499 return (vptr); 500 } 501 502 static const char * 503 bhnd_nvram_btxt_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) 504 { 505 struct bhnd_nvram_btxt *btxt; 506 const void *ptr; 507 size_t io_offset, io_size; 508 int error; 509 510 btxt = (struct bhnd_nvram_btxt *)nv; 511 512 io_size = bhnd_nvram_io_getsize(btxt->data); 513 io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, cookiep); 514 515 /* At EOF? */ 516 if (io_offset == io_size) 517 BHND_NV_PANIC("invalid cookiep: %p", cookiep); 518 519 /* Variable name is found directly at the given offset; trailing 520 * NUL means we can assume that it's at least 1 byte long */ 521 error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &ptr, 1, NULL); 522 if (error) 523 BHND_NV_PANIC("unexpected error in read_ptr(): %d\n", error); 524 525 return (ptr); 526 } 527 528 /** 529 * Return a cookiep for the given I/O offset. 530 */ 531 static void * 532 bhnd_nvram_btxt_offset_to_cookiep(struct bhnd_nvram_btxt *btxt, 533 size_t io_offset) 534 { 535 const void *ptr; 536 int error; 537 538 BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(btxt->data), 539 ("io_offset %zu out-of-range", io_offset)); 540 BHND_NV_ASSERT(io_offset < UINTPTR_MAX, 541 ("io_offset %#zx exceeds UINTPTR_MAX", io_offset)); 542 543 error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_offset, NULL); 544 if (error) 545 BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error); 546 547 ptr = (const uint8_t *)ptr + io_offset; 548 return (__DECONST(void *, ptr)); 549 } 550 551 /* Convert a cookiep back to an I/O offset */ 552 static size_t 553 bhnd_nvram_btxt_cookiep_to_offset(struct bhnd_nvram_btxt *btxt, void *cookiep) 554 { 555 const void *ptr; 556 intptr_t offset; 557 size_t io_size; 558 int error; 559 560 BHND_NV_ASSERT(cookiep != NULL, ("null cookiep")); 561 562 io_size = bhnd_nvram_io_getsize(btxt->data); 563 error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_size, NULL); 564 if (error) 565 BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error); 566 567 offset = (const uint8_t *)cookiep - (const uint8_t *)ptr; 568 BHND_NV_ASSERT(offset >= 0, ("invalid cookiep")); 569 BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)")); 570 BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)")); 571 572 return ((size_t)offset); 573 } 574 575 /* Determine the entry length and env 'key=value' string length of the entry 576 * at @p offset */ 577 static int 578 bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io, size_t offset, 579 size_t *line_len, size_t *env_len) 580 { 581 const uint8_t *baseptr, *p; 582 const void *rbuf; 583 size_t nbytes; 584 int error; 585 586 /* Fetch read buffer */ 587 if ((error = bhnd_nvram_io_read_ptr(io, offset, &rbuf, 0, &nbytes))) 588 return (error); 589 590 /* Find record termination (EOL, or '#') */ 591 p = rbuf; 592 baseptr = rbuf; 593 while ((size_t)(p - baseptr) < nbytes) { 594 if (*p == '#' || *p == '\n' || *p == '\r') 595 break; 596 597 p++; 598 } 599 600 /* Got line length, now trim any trailing whitespace to determine 601 * actual env length */ 602 *line_len = p - baseptr; 603 *env_len = *line_len; 604 605 for (size_t i = 0; i < *line_len; i++) { 606 char c = baseptr[*line_len - i - 1]; 607 if (!bhnd_nv_isspace(c)) 608 break; 609 610 *env_len -= 1; 611 } 612 613 return (0); 614 } 615 616 /* Seek past the next line ending (\r, \r\n, or \n) */ 617 static int 618 bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io, size_t *offset) 619 { 620 const uint8_t *baseptr, *p; 621 const void *rbuf; 622 size_t nbytes; 623 int error; 624 625 /* Fetch read buffer */ 626 if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes))) 627 return (error); 628 629 baseptr = rbuf; 630 p = rbuf; 631 while ((size_t)(p - baseptr) < nbytes) { 632 char c = *p; 633 634 /* Advance to next char. The next position may be EOF, in which 635 * case a read will be invalid */ 636 p++; 637 638 if (c == '\r') { 639 /* CR, check for optional LF */ 640 if ((size_t)(p - baseptr) < nbytes) { 641 if (*p == '\n') 642 p++; 643 } 644 645 break; 646 } else if (c == '\n') { 647 break; 648 } 649 } 650 651 /* Hit newline or EOF */ 652 *offset += (p - baseptr); 653 return (0); 654 } 655 656 /* Seek to the next valid non-comment line (or EOF) */ 657 static int 658 bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io, size_t *offset) 659 { 660 const uint8_t *baseptr, *p; 661 const void *rbuf; 662 size_t nbytes; 663 int error; 664 665 /* Fetch read buffer */ 666 if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes))) 667 return (error); 668 669 /* Skip leading whitespace and comments */ 670 baseptr = rbuf; 671 p = rbuf; 672 while ((size_t)(p - baseptr) < nbytes) { 673 char c = *p; 674 675 /* Skip whitespace */ 676 if (bhnd_nv_isspace(c)) { 677 p++; 678 continue; 679 } 680 681 /* Skip entire comment line */ 682 if (c == '#') { 683 size_t line_off = *offset + (p - baseptr); 684 685 if ((error = bhnd_nvram_btxt_seek_eol(io, &line_off))) 686 return (error); 687 688 p = baseptr + (line_off - *offset); 689 continue; 690 } 691 692 /* Non-whitespace, non-comment */ 693 break; 694 } 695 696 *offset += (p - baseptr); 697 return (0); 698 } 699 700 static int 701 bhnd_nvram_btxt_filter_setvar(struct bhnd_nvram_data *nv, const char *name, 702 bhnd_nvram_val *value, bhnd_nvram_val **result) 703 { 704 bhnd_nvram_val *str; 705 const char *inp; 706 bhnd_nvram_type itype; 707 size_t ilen; 708 int error; 709 710 /* Name (trimmed of any path prefix) must be valid */ 711 if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name))) 712 return (EINVAL); 713 714 /* Value must be bcm-formatted string */ 715 error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt, 716 value, BHND_NVRAM_VAL_DYNAMIC); 717 if (error) 718 return (error); 719 720 /* Value string must not contain our record delimiter character ('\n'), 721 * or our comment character ('#') */ 722 inp = bhnd_nvram_val_bytes(str, &ilen, &itype); 723 BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_STRING, ("non-string value")); 724 for (size_t i = 0; i < ilen; i++) { 725 switch (inp[i]) { 726 case '\n': 727 case '#': 728 BHND_NV_LOG("invalid character (%#hhx) in value\n", 729 inp[i]); 730 bhnd_nvram_val_release(str); 731 return (EINVAL); 732 } 733 } 734 735 /* Success. Transfer result ownership to the caller. */ 736 *result = str; 737 return (0); 738 } 739 740 static int 741 bhnd_nvram_btxt_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name) 742 { 743 /* We permit deletion of any variable */ 744 return (0); 745 } 746