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 #include <sys/param.h> 37 #include <sys/ctype.h> 38 #include <sys/malloc.h> 39 #include <sys/systm.h> 40 41 #include <machine/_inttypes.h> 42 #else /* !_KERNEL */ 43 #include <ctype.h> 44 #include <errno.h> 45 #include <inttypes.h> 46 #include <stdint.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #endif /* _KERNEL */ 51 52 #include "bhnd_nvram_map.h" 53 54 #include "bhnd_nvram_private.h" 55 #include "bhnd_nvram_datavar.h" 56 57 #include "bhnd_nvram_data_spromvar.h" 58 59 /* 60 * BHND SPROM NVRAM data class 61 * 62 * The SPROM data format is a fixed-layout, non-self-descriptive binary format, 63 * used on Broadcom wireless and wired adapters, that provides a subset of the 64 * variables defined by Broadcom SoC NVRAM formats. 65 */ 66 67 static const bhnd_sprom_layout *bhnd_nvram_sprom_get_layout(uint8_t sromrev); 68 69 static int bhnd_nvram_sprom_ident( 70 struct bhnd_nvram_io *io, 71 const bhnd_sprom_layout **ident, 72 struct bhnd_nvram_io **shadow); 73 74 static int bhnd_nvram_sprom_write_var( 75 bhnd_sprom_opcode_state *state, 76 bhnd_sprom_opcode_idx_entry *entry, 77 bhnd_nvram_val *value, 78 struct bhnd_nvram_io *io); 79 80 static int bhnd_nvram_sprom_write_offset( 81 const struct bhnd_nvram_vardefn *var, 82 struct bhnd_nvram_io *data, 83 bhnd_nvram_type type, size_t offset, 84 uint32_t mask, int8_t shift, 85 uint32_t value); 86 87 static int bhnd_nvram_sprom_read_offset( 88 const struct bhnd_nvram_vardefn *var, 89 struct bhnd_nvram_io *data, 90 bhnd_nvram_type type, size_t offset, 91 uint32_t mask, int8_t shift, 92 uint32_t *value); 93 94 static bool bhnd_sprom_is_external_immutable( 95 const char *name); 96 97 BHND_NVRAM_DATA_CLASS_DEFN(sprom, "Broadcom SPROM", 98 BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_sprom)) 99 100 #define SPROM_COOKIE_TO_VID(_cookie) \ 101 (((struct bhnd_sprom_opcode_idx_entry *)(_cookie))->vid) 102 103 #define SPROM_COOKIE_TO_NVRAM_VAR(_cookie) \ 104 bhnd_nvram_get_vardefn(SPROM_COOKIE_TO_VID(_cookie)) 105 106 /** 107 * Read the magic value from @p io, and verify that it matches 108 * the @p layout's expected magic value. 109 * 110 * If @p layout does not defined a magic value, @p magic is set to 0x0 111 * and success is returned. 112 * 113 * @param io An I/O context mapping the SPROM data to be identified. 114 * @param layout The SPROM layout against which @p io should be verified. 115 * @param[out] magic On success, the SPROM magic value. 116 * 117 * @retval 0 success 118 * @retval non-zero If checking @p io otherwise fails, a regular unix 119 * error code will be returned. 120 */ 121 static int 122 bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io *io, 123 const bhnd_sprom_layout *layout, uint16_t *magic) 124 { 125 int error; 126 127 /* Skip if layout does not define a magic value */ 128 if (layout->flags & SPROM_LAYOUT_MAGIC_NONE) 129 return (0); 130 131 /* Read the magic value */ 132 error = bhnd_nvram_io_read(io, layout->magic_offset, magic, 133 sizeof(*magic)); 134 if (error) 135 return (error); 136 137 *magic = le16toh(*magic); 138 139 /* If the signature does not match, skip to next layout */ 140 if (*magic != layout->magic_value) 141 return (ENXIO); 142 143 return (0); 144 } 145 146 /** 147 * Attempt to identify the format of the SPROM data mapped by @p io. 148 * 149 * The SPROM data format does not provide any identifying information at a 150 * known offset, instead requiring that we iterate over the known SPROM image 151 * sizes until we are able to compute a valid checksum (and, for later 152 * revisions, validate a signature at a revision-specific offset). 153 * 154 * @param io An I/O context mapping the SPROM data to be identified. 155 * @param[out] ident On success, the identified SPROM layout. 156 * @param[out] shadow On success, a correctly sized iobuf instance mapping 157 * a copy of the identified SPROM image. The caller is 158 * responsible for deallocating this instance via 159 * bhnd_nvram_io_free() 160 * 161 * @retval 0 success 162 * @retval non-zero If identifying @p io otherwise fails, a regular unix 163 * error code will be returned. 164 */ 165 static int 166 bhnd_nvram_sprom_ident(struct bhnd_nvram_io *io, 167 const bhnd_sprom_layout **ident, struct bhnd_nvram_io **shadow) 168 { 169 struct bhnd_nvram_io *buf; 170 uint8_t crc; 171 size_t crc_errors; 172 size_t sprom_sz_max; 173 int error; 174 175 /* Find the largest SPROM layout size */ 176 sprom_sz_max = 0; 177 for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) { 178 sprom_sz_max = bhnd_nv_ummax(sprom_sz_max, 179 bhnd_sprom_layouts[i].size); 180 } 181 182 /* Allocate backing buffer and initialize CRC state */ 183 buf = bhnd_nvram_iobuf_empty(0, sprom_sz_max); 184 crc = BHND_NVRAM_CRC8_INITIAL; 185 crc_errors = 0; 186 187 /* We iterate the SPROM layouts smallest to largest, allowing us to 188 * perform incremental checksum calculation */ 189 for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) { 190 const bhnd_sprom_layout *layout; 191 void *ptr; 192 size_t nbytes, nr; 193 uint16_t magic; 194 uint8_t srev; 195 bool crc_valid; 196 bool have_magic; 197 198 layout = &bhnd_sprom_layouts[i]; 199 nbytes = bhnd_nvram_io_getsize(buf); 200 201 if ((layout->flags & SPROM_LAYOUT_MAGIC_NONE)) { 202 have_magic = false; 203 } else { 204 have_magic = true; 205 } 206 207 /* Layout instances must be ordered from smallest to largest by 208 * the nvram_map compiler */ 209 if (nbytes > layout->size) 210 BHND_NV_PANIC("SPROM layout is defined out-of-order"); 211 212 /* Calculate number of additional bytes to be read */ 213 nr = layout->size - nbytes; 214 215 /* Adjust the buffer size and fetch a write pointer */ 216 if ((error = bhnd_nvram_io_setsize(buf, layout->size))) 217 goto failed; 218 219 error = bhnd_nvram_io_write_ptr(buf, nbytes, &ptr, nr, NULL); 220 if (error) 221 goto failed; 222 223 /* Read image data and update CRC (errors are reported 224 * after the signature check) */ 225 if ((error = bhnd_nvram_io_read(io, nbytes, ptr, nr))) 226 goto failed; 227 228 crc = bhnd_nvram_crc8(ptr, nr, crc); 229 crc_valid = (crc == BHND_NVRAM_CRC8_VALID); 230 if (!crc_valid) 231 crc_errors++; 232 233 /* Fetch SPROM revision */ 234 error = bhnd_nvram_io_read(buf, layout->srev_offset, &srev, 235 sizeof(srev)); 236 if (error) 237 goto failed; 238 239 /* Early sromrev 1 devices (specifically some BCM440x enet 240 * cards) are reported to have been incorrectly programmed 241 * with a revision of 0x10. */ 242 if (layout->rev == 1 && srev == 0x10) 243 srev = 0x1; 244 245 /* Check revision against the layout definition */ 246 if (srev != layout->rev) 247 continue; 248 249 /* Check the magic value, skipping to the next layout on 250 * failure. */ 251 error = bhnd_nvram_sprom_check_magic(buf, layout, &magic); 252 if (error) { 253 /* If the CRC is was valid, log the mismatch */ 254 if (crc_valid || BHND_NV_VERBOSE) { 255 BHND_NV_LOG("invalid sprom %hhu signature: " 256 "0x%hx (expected 0x%hx)\n", srev, 257 magic, layout->magic_value); 258 259 error = ENXIO; 260 goto failed; 261 } 262 263 continue; 264 } 265 266 /* Check for an earlier CRC error */ 267 if (!crc_valid) { 268 /* If the magic check succeeded, then we may just have 269 * data corruption -- log the CRC error */ 270 if (have_magic || BHND_NV_VERBOSE) { 271 BHND_NV_LOG("sprom %hhu CRC error (crc=%#hhx, " 272 "expected=%#x)\n", srev, crc, 273 BHND_NVRAM_CRC8_VALID); 274 } 275 276 continue; 277 } 278 279 /* Identified */ 280 *shadow = buf; 281 *ident = layout; 282 return (0); 283 } 284 285 /* No match -- set error and fallthrough */ 286 error = ENXIO; 287 if (crc_errors > 0 && BHND_NV_VERBOSE) { 288 BHND_NV_LOG("sprom parsing failed with %zu CRC errors\n", 289 crc_errors); 290 } 291 292 failed: 293 bhnd_nvram_io_free(buf); 294 return (error); 295 } 296 297 static int 298 bhnd_nvram_sprom_probe(struct bhnd_nvram_io *io) 299 { 300 const bhnd_sprom_layout *layout; 301 struct bhnd_nvram_io *shadow; 302 int error; 303 304 /* Try to parse the input */ 305 if ((error = bhnd_nvram_sprom_ident(io, &layout, &shadow))) 306 return (error); 307 308 /* Clean up the shadow iobuf */ 309 bhnd_nvram_io_free(shadow); 310 311 return (BHND_NVRAM_DATA_PROBE_DEFAULT); 312 } 313 314 315 /** 316 * Return the SPROM layout definition for the given @p sromrev, or NULL if 317 * not found. 318 */ 319 static const bhnd_sprom_layout * 320 bhnd_nvram_sprom_get_layout(uint8_t sromrev) 321 { 322 /* Find matching SPROM layout definition */ 323 for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) { 324 if (bhnd_sprom_layouts[i].rev == sromrev) 325 return (&bhnd_sprom_layouts[i]); 326 } 327 328 /* Not found */ 329 return (NULL); 330 } 331 332 /** 333 * Serialize a SPROM variable. 334 * 335 * @param state The SPROM opcode state describing the layout of @p io. 336 * @param entry The variable's SPROM opcode index entry. 337 * @param value The value to encode to @p io as per @p entry. 338 * @param io I/O context to which @p value should be written, or NULL 339 * if no output should be produced. This may be used to validate 340 * values prior to write. 341 * 342 * @retval 0 success 343 * @retval EFTYPE If value coercion from @p value to the type required by 344 * @p entry is unsupported. 345 * @retval ERANGE If value coercion from @p value would overflow 346 * (or underflow) the type required by @p entry. 347 * @retval non-zero If serialization otherwise fails, a regular unix error 348 * code will be returned. 349 */ 350 static int 351 bhnd_nvram_sprom_write_var(bhnd_sprom_opcode_state *state, 352 bhnd_sprom_opcode_idx_entry *entry, bhnd_nvram_val *value, 353 struct bhnd_nvram_io *io) 354 { 355 const struct bhnd_nvram_vardefn *var; 356 uint32_t u32[BHND_SPROM_ARRAY_MAXLEN]; 357 bhnd_nvram_type itype, var_base_type; 358 size_t ipos, ilen, nelem; 359 int error; 360 361 /* Fetch variable definition and the native element type */ 362 var = bhnd_nvram_get_vardefn(entry->vid); 363 BHND_NV_ASSERT(var != NULL, ("missing variable definition")); 364 365 var_base_type = bhnd_nvram_base_type(var->type); 366 367 /* Fetch the element count from the SPROM variable layout definition */ 368 if ((error = bhnd_sprom_opcode_parse_var(state, entry))) 369 return (error); 370 371 nelem = state->var.nelem; 372 BHND_NV_ASSERT(nelem <= var->nelem, ("SPROM nelem=%zu exceeds maximum " 373 "NVRAM nelem=%hhu", nelem, var->nelem)); 374 375 /* Promote the data to a common 32-bit representation */ 376 if (bhnd_nvram_is_signed_type(var_base_type)) 377 itype = BHND_NVRAM_TYPE_INT32_ARRAY; 378 else 379 itype = BHND_NVRAM_TYPE_UINT32_ARRAY; 380 381 /* Calculate total size of the 32-bit promoted representation */ 382 if ((ilen = bhnd_nvram_value_size(NULL, 0, itype, nelem)) == 0) { 383 /* Variable-width types are unsupported */ 384 BHND_NV_LOG("invalid %s SPROM variable type %d\n", 385 var->name, var->type); 386 return (EFTYPE); 387 } 388 389 /* The native representation must fit within our scratch array */ 390 if (ilen > sizeof(u32)) { 391 BHND_NV_LOG("error encoding '%s', SPROM_ARRAY_MAXLEN " 392 "incorrect\n", var->name); 393 return (EFTYPE); 394 } 395 396 /* Initialize our common 32-bit value representation */ 397 if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) { 398 /* No value provided; can this variable be encoded as missing 399 * by setting all bits to one? */ 400 if (!(var->flags & BHND_NVRAM_VF_IGNALL1)) { 401 BHND_NV_LOG("missing required property: %s\n", 402 var->name); 403 return (EINVAL); 404 } 405 406 /* Set all bits */ 407 memset(u32, 0xFF, ilen); 408 } else { 409 bhnd_nvram_val bcm_val; 410 const void *var_ptr; 411 bhnd_nvram_type var_type, raw_type; 412 size_t var_len, enc_nelem; 413 414 /* Try to coerce the value to the native variable format. */ 415 error = bhnd_nvram_val_convert_init(&bcm_val, var->fmt, value, 416 BHND_NVRAM_VAL_DYNAMIC|BHND_NVRAM_VAL_BORROW_DATA); 417 if (error) { 418 BHND_NV_LOG("error converting input type %s to %s " 419 "format\n", 420 bhnd_nvram_type_name(bhnd_nvram_val_type(value)), 421 bhnd_nvram_val_fmt_name(var->fmt)); 422 return (error); 423 } 424 425 var_ptr = bhnd_nvram_val_bytes(&bcm_val, &var_len, &var_type); 426 427 /* 428 * Promote to a common 32-bit representation. 429 * 430 * We must use the raw type to interpret the input data as its 431 * underlying integer representation -- otherwise, coercion 432 * would attempt to parse the input as its complex 433 * representation. 434 * 435 * For example, direct CHAR -> UINT32 coercion would attempt to 436 * parse the character as a decimal integer, rather than 437 * promoting the raw UTF8 byte value to a 32-bit value. 438 */ 439 raw_type = bhnd_nvram_raw_type(var_type); 440 error = bhnd_nvram_value_coerce(var_ptr, var_len, raw_type, 441 u32, &ilen, itype); 442 443 /* Clean up temporary value representation */ 444 bhnd_nvram_val_release(&bcm_val); 445 446 /* Report coercion failure */ 447 if (error) { 448 BHND_NV_LOG("error promoting %s to %s: %d\n", 449 bhnd_nvram_type_name(var_type), 450 bhnd_nvram_type_name(itype), error); 451 return (error); 452 } 453 454 /* Encoded element count must match SPROM's definition */ 455 error = bhnd_nvram_value_nelem(u32, ilen, itype, &enc_nelem); 456 if (error) 457 return (error); 458 459 if (enc_nelem != nelem) { 460 const char *type_name; 461 462 type_name = bhnd_nvram_type_name(var_base_type); 463 BHND_NV_LOG("invalid %s property value '%s[%zu]': " 464 "required %s[%zu]", var->name, type_name, 465 enc_nelem, type_name, nelem); 466 return (EFTYPE); 467 } 468 } 469 470 /* 471 * Seek to the start of the variable's SPROM layout definition and 472 * iterate over all bindings. 473 */ 474 if ((error = bhnd_sprom_opcode_seek(state, entry))) { 475 BHND_NV_LOG("variable seek failed: %d\n", error); 476 return (error); 477 } 478 479 ipos = 0; 480 while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) { 481 bhnd_sprom_opcode_bind *binding; 482 bhnd_sprom_opcode_var *binding_var; 483 size_t offset; 484 uint32_t skip_out_bytes; 485 486 BHND_NV_ASSERT( 487 state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN, 488 ("invalid var state")); 489 BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state")); 490 491 binding_var = &state->var; 492 binding = &state->var.bind; 493 494 /* Calculate output skip bytes for this binding. 495 * 496 * Skip directions are defined in terms of decoding, and 497 * reversed when encoding. */ 498 skip_out_bytes = binding->skip_in; 499 error = bhnd_sprom_opcode_apply_scale(state, &skip_out_bytes); 500 if (error) 501 return (error); 502 503 /* Bind */ 504 offset = state->offset; 505 for (size_t i = 0; i < binding->count; i++) { 506 if (ipos >= nelem) { 507 BHND_NV_LOG("input skip %u positioned %zu " 508 "beyond nelem %zu\n", binding->skip_out, 509 ipos, nelem); 510 return (EINVAL); 511 } 512 513 /* Write next offset */ 514 if (io != NULL) { 515 error = bhnd_nvram_sprom_write_offset(var, io, 516 binding_var->base_type, 517 offset, 518 binding_var->mask, 519 binding_var->shift, 520 u32[ipos]); 521 if (error) 522 return (error); 523 } 524 525 /* Adjust output position; this was already verified to 526 * not overflow/underflow during SPROM opcode 527 * evaluation */ 528 if (binding->skip_in_negative) { 529 offset -= skip_out_bytes; 530 } else { 531 offset += skip_out_bytes; 532 } 533 534 /* Skip advancing input if additional bindings are 535 * required to fully encode intv */ 536 if (binding->skip_out == 0) 537 continue; 538 539 /* Advance input position */ 540 if (SIZE_MAX - binding->skip_out < ipos) { 541 BHND_NV_LOG("output skip %u would overflow " 542 "%zu\n", binding->skip_out, ipos); 543 return (EINVAL); 544 } 545 546 ipos += binding->skip_out; 547 } 548 } 549 550 /* Did we iterate all bindings until hitting end of the variable 551 * definition? */ 552 BHND_NV_ASSERT(error != 0, ("loop terminated early")); 553 if (error != ENOENT) 554 return (error); 555 556 return (0); 557 } 558 559 static int 560 bhnd_nvram_sprom_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props, 561 bhnd_nvram_plist *options, void *outp, size_t *olen) 562 { 563 bhnd_sprom_opcode_state state; 564 struct bhnd_nvram_io *io; 565 bhnd_nvram_prop *prop; 566 bhnd_sprom_opcode_idx_entry *entry; 567 const bhnd_sprom_layout *layout; 568 size_t limit; 569 uint8_t crc; 570 uint8_t sromrev; 571 int error; 572 573 limit = *olen; 574 layout = NULL; 575 io = NULL; 576 577 /* Fetch sromrev property */ 578 if (!bhnd_nvram_plist_contains(props, BHND_NVAR_SROMREV)) { 579 BHND_NV_LOG("missing required property: %s\n", 580 BHND_NVAR_SROMREV); 581 return (EINVAL); 582 } 583 584 error = bhnd_nvram_plist_get_uint8(props, BHND_NVAR_SROMREV, &sromrev); 585 if (error) { 586 BHND_NV_LOG("error reading sromrev property: %d\n", error); 587 return (EFTYPE); 588 } 589 590 /* Find SPROM layout definition */ 591 if ((layout = bhnd_nvram_sprom_get_layout(sromrev)) == NULL) { 592 BHND_NV_LOG("unsupported sromrev: %hhu\n", sromrev); 593 return (EFTYPE); 594 } 595 596 /* Provide required size to caller */ 597 *olen = layout->size; 598 if (outp == NULL) 599 return (0); 600 else if (limit < *olen) 601 return (ENOMEM); 602 603 /* Initialize SPROM layout interpreter */ 604 if ((error = bhnd_sprom_opcode_init(&state, layout))) { 605 BHND_NV_LOG("error initializing opcode state: %d\n", error); 606 return (ENXIO); 607 } 608 609 /* Check for unsupported properties */ 610 prop = NULL; 611 while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) { 612 const char *name; 613 614 /* Fetch the corresponding SPROM layout index entry */ 615 name = bhnd_nvram_prop_name(prop); 616 entry = bhnd_sprom_opcode_index_find(&state, name); 617 if (entry == NULL) { 618 BHND_NV_LOG("property '%s' unsupported by sromrev " 619 "%hhu\n", name, layout->rev); 620 error = EINVAL; 621 goto finished; 622 } 623 } 624 625 /* Zero-initialize output */ 626 memset(outp, 0, *olen); 627 628 /* Allocate wrapping I/O context for output buffer */ 629 io = bhnd_nvram_ioptr_new(outp, *olen, *olen, BHND_NVRAM_IOPTR_RDWR); 630 if (io == NULL) { 631 error = ENOMEM; 632 goto finished; 633 } 634 635 /* 636 * Serialize all SPROM variable data. 637 */ 638 entry = NULL; 639 while ((entry = bhnd_sprom_opcode_index_next(&state, entry)) != NULL) { 640 const struct bhnd_nvram_vardefn *var; 641 bhnd_nvram_val *val; 642 643 var = bhnd_nvram_get_vardefn(entry->vid); 644 BHND_NV_ASSERT(var != NULL, ("missing variable definition")); 645 646 /* Fetch prop; will be NULL if unavailable */ 647 prop = bhnd_nvram_plist_get_prop(props, var->name); 648 if (prop != NULL) { 649 val = bhnd_nvram_prop_val(prop); 650 } else { 651 val = BHND_NVRAM_VAL_NULL; 652 } 653 654 /* Attempt to serialize the property value to the appropriate 655 * offset within the output buffer */ 656 error = bhnd_nvram_sprom_write_var(&state, entry, val, io); 657 if (error) { 658 BHND_NV_LOG("error serializing %s to required type " 659 "%s: %d\n", var->name, 660 bhnd_nvram_type_name(var->type), error); 661 662 /* ENOMEM is reserved for signaling that the output 663 * buffer capacity is insufficient */ 664 if (error == ENOMEM) 665 error = EINVAL; 666 667 goto finished; 668 } 669 } 670 671 /* 672 * Write magic value, if any. 673 */ 674 if (!(layout->flags & SPROM_LAYOUT_MAGIC_NONE)) { 675 uint16_t magic; 676 677 magic = htole16(layout->magic_value); 678 error = bhnd_nvram_io_write(io, layout->magic_offset, &magic, 679 sizeof(magic)); 680 if (error) { 681 BHND_NV_LOG("error writing magic value: %d\n", error); 682 goto finished; 683 } 684 } 685 686 /* Calculate the CRC over all SPROM data, not including the CRC byte. */ 687 crc = ~bhnd_nvram_crc8(outp, layout->crc_offset, 688 BHND_NVRAM_CRC8_INITIAL); 689 690 /* Write the checksum. */ 691 error = bhnd_nvram_io_write(io, layout->crc_offset, &crc, sizeof(crc)); 692 if (error) { 693 BHND_NV_LOG("error writing CRC value: %d\n", error); 694 goto finished; 695 } 696 697 /* 698 * Success! 699 */ 700 error = 0; 701 702 finished: 703 bhnd_sprom_opcode_fini(&state); 704 705 if (io != NULL) 706 bhnd_nvram_io_free(io); 707 708 return (error); 709 } 710 711 static int 712 bhnd_nvram_sprom_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) 713 { 714 struct bhnd_nvram_sprom *sp; 715 int error; 716 717 sp = (struct bhnd_nvram_sprom *)nv; 718 719 /* Identify the SPROM input data */ 720 if ((error = bhnd_nvram_sprom_ident(io, &sp->layout, &sp->data))) 721 goto failed; 722 723 /* Initialize SPROM binding eval state */ 724 if ((error = bhnd_sprom_opcode_init(&sp->state, sp->layout))) 725 goto failed; 726 727 return (0); 728 729 failed: 730 if (sp->data != NULL) 731 bhnd_nvram_io_free(sp->data); 732 733 return (error); 734 } 735 736 static void 737 bhnd_nvram_sprom_free(struct bhnd_nvram_data *nv) 738 { 739 struct bhnd_nvram_sprom *sp = (struct bhnd_nvram_sprom *)nv; 740 741 bhnd_sprom_opcode_fini(&sp->state); 742 bhnd_nvram_io_free(sp->data); 743 } 744 745 size_t 746 bhnd_nvram_sprom_count(struct bhnd_nvram_data *nv) 747 { 748 struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv; 749 return (sprom->layout->num_vars); 750 } 751 752 static bhnd_nvram_plist * 753 bhnd_nvram_sprom_options(struct bhnd_nvram_data *nv) 754 { 755 return (NULL); 756 } 757 758 static uint32_t 759 bhnd_nvram_sprom_caps(struct bhnd_nvram_data *nv) 760 { 761 return (BHND_NVRAM_DATA_CAP_INDEXED); 762 } 763 764 static const char * 765 bhnd_nvram_sprom_next(struct bhnd_nvram_data *nv, void **cookiep) 766 { 767 struct bhnd_nvram_sprom *sp; 768 bhnd_sprom_opcode_idx_entry *entry; 769 const struct bhnd_nvram_vardefn *var; 770 771 sp = (struct bhnd_nvram_sprom *)nv; 772 773 /* Find next index entry that is not disabled by virtue of IGNALL1 */ 774 entry = *cookiep; 775 while ((entry = bhnd_sprom_opcode_index_next(&sp->state, entry))) { 776 /* Update cookiep and fetch variable definition */ 777 *cookiep = entry; 778 var = SPROM_COOKIE_TO_NVRAM_VAR(*cookiep); 779 780 /* We might need to parse the variable's value to determine 781 * whether it should be treated as unset */ 782 if (var->flags & BHND_NVRAM_VF_IGNALL1) { 783 int error; 784 size_t len; 785 786 error = bhnd_nvram_sprom_getvar(nv, *cookiep, NULL, 787 &len, var->type); 788 if (error) { 789 BHND_NV_ASSERT(error == ENOENT, ("unexpected " 790 "error parsing variable: %d", error)); 791 continue; 792 } 793 } 794 795 /* Found! */ 796 return (var->name); 797 } 798 799 /* Reached end of index entries */ 800 return (NULL); 801 } 802 803 static void * 804 bhnd_nvram_sprom_find(struct bhnd_nvram_data *nv, const char *name) 805 { 806 struct bhnd_nvram_sprom *sp; 807 bhnd_sprom_opcode_idx_entry *entry; 808 809 sp = (struct bhnd_nvram_sprom *)nv; 810 811 entry = bhnd_sprom_opcode_index_find(&sp->state, name); 812 return (entry); 813 } 814 815 /** 816 * Write @p value of @p type to the SPROM @p data at @p offset, applying 817 * @p mask and @p shift, and OR with the existing data. 818 * 819 * @param var The NVRAM variable definition. 820 * @param data The SPROM data to be modified. 821 * @param type The type to write at @p offset. 822 * @param offset The data offset to be written. 823 * @param mask The mask to be applied to @p value after shifting. 824 * @param shift The shift to be applied to @p value; if positive, a left 825 * shift will be applied, if negative, a right shift (this is the reverse of the 826 * decoding behavior) 827 * @param value The value to be written. The parsed value will be OR'd with the 828 * current contents of @p data at @p offset. 829 */ 830 static int 831 bhnd_nvram_sprom_write_offset(const struct bhnd_nvram_vardefn *var, 832 struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset, 833 uint32_t mask, int8_t shift, uint32_t value) 834 { 835 union bhnd_nvram_sprom_storage scratch; 836 int error; 837 838 #define NV_WRITE_INT(_widen, _repr, _swap) do { \ 839 /* Narrow the 32-bit representation */ \ 840 scratch._repr[1] = (_widen)value; \ 841 \ 842 /* Shift and mask the new value */ \ 843 if (shift > 0) \ 844 scratch._repr[1] <<= shift; \ 845 else if (shift < 0) \ 846 scratch._repr[1] >>= -shift; \ 847 scratch._repr[1] &= mask; \ 848 \ 849 /* Swap to output byte order */ \ 850 scratch._repr[1] = _swap(scratch._repr[1]); \ 851 \ 852 /* Fetch the current value */ \ 853 error = bhnd_nvram_io_read(data, offset, \ 854 &scratch._repr[0], sizeof(scratch._repr[0])); \ 855 if (error) { \ 856 BHND_NV_LOG("error reading %s SPROM offset " \ 857 "%#zx: %d\n", var->name, offset, error); \ 858 return (EFTYPE); \ 859 } \ 860 \ 861 /* Mask and set our new value's bits in the current \ 862 * value */ \ 863 if (shift >= 0) \ 864 scratch._repr[0] &= ~_swap(mask << shift); \ 865 else if (shift < 0) \ 866 scratch._repr[0] &= ~_swap(mask >> (-shift)); \ 867 scratch._repr[0] |= scratch._repr[1]; \ 868 \ 869 /* Perform write */ \ 870 error = bhnd_nvram_io_write(data, offset, \ 871 &scratch._repr[0], sizeof(scratch._repr[0])); \ 872 if (error) { \ 873 BHND_NV_LOG("error writing %s SPROM offset " \ 874 "%#zx: %d\n", var->name, offset, error); \ 875 return (EFTYPE); \ 876 } \ 877 } while(0) 878 879 /* Apply mask/shift and widen to a common 32bit representation */ 880 switch (type) { 881 case BHND_NVRAM_TYPE_UINT8: 882 NV_WRITE_INT(uint32_t, u8, ); 883 break; 884 case BHND_NVRAM_TYPE_UINT16: 885 NV_WRITE_INT(uint32_t, u16, htole16); 886 break; 887 case BHND_NVRAM_TYPE_UINT32: 888 NV_WRITE_INT(uint32_t, u32, htole32); 889 break; 890 case BHND_NVRAM_TYPE_INT8: 891 NV_WRITE_INT(int32_t, i8, ); 892 break; 893 case BHND_NVRAM_TYPE_INT16: 894 NV_WRITE_INT(int32_t, i16, htole16); 895 break; 896 case BHND_NVRAM_TYPE_INT32: 897 NV_WRITE_INT(int32_t, i32, htole32); 898 break; 899 case BHND_NVRAM_TYPE_CHAR: 900 NV_WRITE_INT(uint32_t, u8, ); 901 break; 902 default: 903 BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type); 904 return (EFTYPE); 905 } 906 #undef NV_WRITE_INT 907 908 return (0); 909 } 910 911 /** 912 * Read the value of @p type from the SPROM @p data at @p offset, apply @p mask 913 * and @p shift, and OR with the existing @p value. 914 * 915 * @param var The NVRAM variable definition. 916 * @param data The SPROM data to be decoded. 917 * @param type The type to read at @p offset 918 * @param offset The data offset to be read. 919 * @param mask The mask to be applied to the value read at @p offset. 920 * @param shift The shift to be applied after masking; if positive, a right 921 * shift will be applied, if negative, a left shift. 922 * @param value The read destination; the parsed value will be OR'd with the 923 * current contents of @p value. 924 */ 925 static int 926 bhnd_nvram_sprom_read_offset(const struct bhnd_nvram_vardefn *var, 927 struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset, 928 uint32_t mask, int8_t shift, uint32_t *value) 929 { 930 union bhnd_nvram_sprom_storage scratch; 931 int error; 932 933 #define NV_PARSE_INT(_widen, _repr, _swap) do { \ 934 /* Perform read */ \ 935 error = bhnd_nvram_io_read(data, offset, \ 936 &scratch._repr[0], sizeof(scratch._repr[0])); \ 937 if (error) { \ 938 BHND_NV_LOG("error reading %s SPROM offset " \ 939 "%#zx: %d\n", var->name, offset, error); \ 940 return (EFTYPE); \ 941 } \ 942 \ 943 /* Swap to host byte order */ \ 944 scratch._repr[0] = _swap(scratch._repr[0]); \ 945 \ 946 /* Mask and shift the value */ \ 947 scratch._repr[0] &= mask; \ 948 if (shift > 0) { \ 949 scratch. _repr[0] >>= shift; \ 950 } else if (shift < 0) { \ 951 scratch. _repr[0] <<= -shift; \ 952 } \ 953 \ 954 /* Widen to 32-bit representation and OR with current \ 955 * value */ \ 956 (*value) |= (_widen)scratch._repr[0]; \ 957 } while(0) 958 959 /* Apply mask/shift and widen to a common 32bit representation */ 960 switch (type) { 961 case BHND_NVRAM_TYPE_UINT8: 962 NV_PARSE_INT(uint32_t, u8, ); 963 break; 964 case BHND_NVRAM_TYPE_UINT16: 965 NV_PARSE_INT(uint32_t, u16, le16toh); 966 break; 967 case BHND_NVRAM_TYPE_UINT32: 968 NV_PARSE_INT(uint32_t, u32, le32toh); 969 break; 970 case BHND_NVRAM_TYPE_INT8: 971 NV_PARSE_INT(int32_t, i8, ); 972 break; 973 case BHND_NVRAM_TYPE_INT16: 974 NV_PARSE_INT(int32_t, i16, le16toh); 975 break; 976 case BHND_NVRAM_TYPE_INT32: 977 NV_PARSE_INT(int32_t, i32, le32toh); 978 break; 979 case BHND_NVRAM_TYPE_CHAR: 980 NV_PARSE_INT(uint32_t, u8, ); 981 break; 982 default: 983 BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type); 984 return (EFTYPE); 985 } 986 #undef NV_PARSE_INT 987 988 return (0); 989 } 990 991 /** 992 * Common variable decoding; fetches and decodes variable to @p val, 993 * using @p storage for actual data storage. 994 * 995 * The returned @p val instance will hold a borrowed reference to @p storage, 996 * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond 997 * the lifetime of @p storage. 998 * 999 * The caller is responsible for releasing any allocated value state 1000 * via bhnd_nvram_val_release(). 1001 */ 1002 static int 1003 bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep, 1004 union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val) 1005 { 1006 struct bhnd_nvram_sprom *sp; 1007 bhnd_sprom_opcode_idx_entry *entry; 1008 const struct bhnd_nvram_vardefn *var; 1009 union bhnd_nvram_sprom_storage *inp; 1010 bhnd_nvram_type var_btype; 1011 uint32_t intv; 1012 size_t ilen, ipos, iwidth; 1013 size_t nelem; 1014 bool all_bits_set; 1015 int error; 1016 1017 sp = (struct bhnd_nvram_sprom *)nv; 1018 entry = cookiep; 1019 1020 BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep")); 1021 1022 /* Fetch canonical variable definition */ 1023 var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep); 1024 BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep)); 1025 1026 /* 1027 * Fetch the array length from the SPROM variable definition. 1028 * 1029 * This generally be identical to the array length provided by the 1030 * canonical NVRAM variable definition, but some SPROM layouts may 1031 * define a smaller element count. 1032 */ 1033 if ((error = bhnd_sprom_opcode_parse_var(&sp->state, entry))) { 1034 BHND_NV_LOG("variable evaluation failed: %d\n", error); 1035 return (error); 1036 } 1037 1038 nelem = sp->state.var.nelem; 1039 if (nelem > var->nelem) { 1040 BHND_NV_LOG("SPROM array element count %zu cannot be " 1041 "represented by '%s' element count of %hhu\n", nelem, 1042 var->name, var->nelem); 1043 return (EFTYPE); 1044 } 1045 1046 /* Fetch the var's base element type */ 1047 var_btype = bhnd_nvram_base_type(var->type); 1048 1049 /* Calculate total byte length of the native encoding */ 1050 if ((iwidth = bhnd_nvram_value_size(NULL, 0, var_btype, 1)) == 0) { 1051 /* SPROM does not use (and we do not support) decoding of 1052 * variable-width data types */ 1053 BHND_NV_LOG("invalid SPROM data type: %d", var->type); 1054 return (EFTYPE); 1055 } 1056 ilen = nelem * iwidth; 1057 1058 /* Decode into our caller's local storage */ 1059 inp = storage; 1060 if (ilen > sizeof(*storage)) { 1061 BHND_NV_LOG("error decoding '%s', SPROM_ARRAY_MAXLEN " 1062 "incorrect\n", var->name); 1063 return (EFTYPE); 1064 } 1065 1066 /* Zero-initialize our decode buffer; any output elements skipped 1067 * during decode should default to zero. */ 1068 memset(inp, 0, ilen); 1069 1070 /* 1071 * Decode the SPROM data, iteratively decoding up to nelem values. 1072 */ 1073 if ((error = bhnd_sprom_opcode_seek(&sp->state, entry))) { 1074 BHND_NV_LOG("variable seek failed: %d\n", error); 1075 return (error); 1076 } 1077 1078 ipos = 0; 1079 intv = 0x0; 1080 if (var->flags & BHND_NVRAM_VF_IGNALL1) 1081 all_bits_set = true; 1082 else 1083 all_bits_set = false; 1084 while ((error = bhnd_sprom_opcode_next_binding(&sp->state)) == 0) { 1085 bhnd_sprom_opcode_bind *binding; 1086 bhnd_sprom_opcode_var *binding_var; 1087 bhnd_nvram_type intv_type; 1088 size_t offset; 1089 size_t nbyte; 1090 uint32_t skip_in_bytes; 1091 void *ptr; 1092 1093 BHND_NV_ASSERT( 1094 sp->state.var_state >= SPROM_OPCODE_VAR_STATE_OPEN, 1095 ("invalid var state")); 1096 BHND_NV_ASSERT(sp->state.var.have_bind, ("invalid bind state")); 1097 1098 binding_var = &sp->state.var; 1099 binding = &sp->state.var.bind; 1100 1101 if (ipos >= nelem) { 1102 BHND_NV_LOG("output skip %u positioned " 1103 "%zu beyond nelem %zu\n", 1104 binding->skip_out, ipos, nelem); 1105 return (EINVAL); 1106 } 1107 1108 /* Calculate input skip bytes for this binding */ 1109 skip_in_bytes = binding->skip_in; 1110 error = bhnd_sprom_opcode_apply_scale(&sp->state, 1111 &skip_in_bytes); 1112 if (error) 1113 return (error); 1114 1115 /* Bind */ 1116 offset = sp->state.offset; 1117 for (size_t i = 0; i < binding->count; i++) { 1118 /* Read the offset value, OR'ing with the current 1119 * value of intv */ 1120 error = bhnd_nvram_sprom_read_offset(var, sp->data, 1121 binding_var->base_type, 1122 offset, 1123 binding_var->mask, 1124 binding_var->shift, 1125 &intv); 1126 if (error) 1127 return (error); 1128 1129 /* If IGNALL1, record whether value does not have 1130 * all bits set. */ 1131 if (var->flags & BHND_NVRAM_VF_IGNALL1 && 1132 all_bits_set) 1133 { 1134 uint32_t all1; 1135 1136 all1 = binding_var->mask; 1137 if (binding_var->shift > 0) 1138 all1 >>= binding_var->shift; 1139 else if (binding_var->shift < 0) 1140 all1 <<= -binding_var->shift; 1141 1142 if ((intv & all1) != all1) 1143 all_bits_set = false; 1144 } 1145 1146 /* Adjust input position; this was already verified to 1147 * not overflow/underflow during SPROM opcode 1148 * evaluation */ 1149 if (binding->skip_in_negative) { 1150 offset -= skip_in_bytes; 1151 } else { 1152 offset += skip_in_bytes; 1153 } 1154 1155 /* Skip writing to inp if additional bindings are 1156 * required to fully populate intv */ 1157 if (binding->skip_out == 0) 1158 continue; 1159 1160 /* We use bhnd_nvram_value_coerce() to perform 1161 * overflow-checked coercion from the widened 1162 * uint32/int32 intv value to the requested output 1163 * type */ 1164 if (bhnd_nvram_is_signed_type(var_btype)) 1165 intv_type = BHND_NVRAM_TYPE_INT32; 1166 else 1167 intv_type = BHND_NVRAM_TYPE_UINT32; 1168 1169 /* Calculate address of the current element output 1170 * position */ 1171 ptr = (uint8_t *)inp + (iwidth * ipos); 1172 1173 /* Perform coercion of the array element */ 1174 nbyte = iwidth; 1175 error = bhnd_nvram_value_coerce(&intv, sizeof(intv), 1176 intv_type, ptr, &nbyte, var_btype); 1177 if (error) 1178 return (error); 1179 1180 /* Clear temporary state */ 1181 intv = 0x0; 1182 1183 /* Advance output position */ 1184 if (SIZE_MAX - binding->skip_out < ipos) { 1185 BHND_NV_LOG("output skip %u would overflow " 1186 "%zu\n", binding->skip_out, ipos); 1187 return (EINVAL); 1188 } 1189 1190 ipos += binding->skip_out; 1191 } 1192 } 1193 1194 /* Did we iterate all bindings until hitting end of the variable 1195 * definition? */ 1196 BHND_NV_ASSERT(error != 0, ("loop terminated early")); 1197 if (error != ENOENT) { 1198 return (error); 1199 } 1200 1201 /* If marked IGNALL1 and all bits are set, treat variable as 1202 * unavailable */ 1203 if ((var->flags & BHND_NVRAM_VF_IGNALL1) && all_bits_set) 1204 return (ENOENT); 1205 1206 /* Provide value wrapper */ 1207 return (bhnd_nvram_val_init(val, var->fmt, inp, ilen, var->type, 1208 BHND_NVRAM_VAL_BORROW_DATA)); 1209 return (error); 1210 } 1211 1212 static int 1213 bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1, 1214 void *cookiep2) 1215 { 1216 struct bhnd_sprom_opcode_idx_entry *e1, *e2; 1217 1218 e1 = cookiep1; 1219 e2 = cookiep2; 1220 1221 /* Use the index entry order; this matches the order of variables 1222 * returned via bhnd_nvram_sprom_next() */ 1223 if (e1 < e2) 1224 return (-1); 1225 else if (e1 > e2) 1226 return (1); 1227 1228 return (0); 1229 } 1230 1231 static int 1232 bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, 1233 size_t *len, bhnd_nvram_type otype) 1234 { 1235 bhnd_nvram_val val; 1236 union bhnd_nvram_sprom_storage storage; 1237 int error; 1238 1239 /* Decode variable to a new value instance */ 1240 error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val); 1241 if (error) 1242 return (error); 1243 1244 /* Perform value coercion */ 1245 error = bhnd_nvram_val_encode(&val, buf, len, otype); 1246 1247 /* Clean up */ 1248 bhnd_nvram_val_release(&val); 1249 return (error); 1250 } 1251 1252 static int 1253 bhnd_nvram_sprom_copy_val(struct bhnd_nvram_data *nv, void *cookiep, 1254 bhnd_nvram_val **value) 1255 { 1256 bhnd_nvram_val val; 1257 union bhnd_nvram_sprom_storage storage; 1258 int error; 1259 1260 /* Decode variable to a new value instance */ 1261 error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val); 1262 if (error) 1263 return (error); 1264 1265 /* Attempt to copy to heap */ 1266 *value = bhnd_nvram_val_copy(&val); 1267 bhnd_nvram_val_release(&val); 1268 1269 if (*value == NULL) 1270 return (ENOMEM); 1271 1272 return (0); 1273 } 1274 1275 static const void * 1276 bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, 1277 size_t *len, bhnd_nvram_type *type) 1278 { 1279 /* Unsupported */ 1280 return (NULL); 1281 } 1282 1283 static const char * 1284 bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) 1285 { 1286 const struct bhnd_nvram_vardefn *var; 1287 1288 BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep")); 1289 1290 var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep); 1291 BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep)); 1292 1293 return (var->name); 1294 } 1295 1296 static int 1297 bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data *nv, const char *name, 1298 bhnd_nvram_val *value, bhnd_nvram_val **result) 1299 { 1300 struct bhnd_nvram_sprom *sp; 1301 const struct bhnd_nvram_vardefn *var; 1302 bhnd_sprom_opcode_idx_entry *entry; 1303 bhnd_nvram_val *spval; 1304 int error; 1305 1306 sp = (struct bhnd_nvram_sprom *)nv; 1307 1308 /* Is this an externally immutable variable name? */ 1309 if (bhnd_sprom_is_external_immutable(name)) 1310 return (EINVAL); 1311 1312 /* Variable must be defined in our SPROM layout */ 1313 if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL) 1314 return (ENOENT); 1315 1316 var = bhnd_nvram_get_vardefn(entry->vid); 1317 BHND_NV_ASSERT(var != NULL, ("missing variable definition")); 1318 1319 /* Value must be convertible to the native variable type */ 1320 error = bhnd_nvram_val_convert_new(&spval, var->fmt, value, 1321 BHND_NVRAM_VAL_DYNAMIC); 1322 if (error) 1323 return (error); 1324 1325 /* Value must be encodeable by our SPROM layout */ 1326 error = bhnd_nvram_sprom_write_var(&sp->state, entry, spval, NULL); 1327 if (error) { 1328 bhnd_nvram_val_release(spval); 1329 return (error); 1330 } 1331 1332 /* Success. Transfer our ownership of the converted value to the 1333 * caller */ 1334 *result = spval; 1335 return (0); 1336 } 1337 1338 static int 1339 bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name) 1340 { 1341 struct bhnd_nvram_sprom *sp; 1342 const struct bhnd_nvram_vardefn *var; 1343 bhnd_sprom_opcode_idx_entry *entry; 1344 1345 sp = (struct bhnd_nvram_sprom *)nv; 1346 1347 /* Is this an externally immutable variable name? */ 1348 if (bhnd_sprom_is_external_immutable(name)) 1349 return (EINVAL); 1350 1351 /* Variable must be defined in our SPROM layout */ 1352 if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL) 1353 return (ENOENT); 1354 1355 var = bhnd_nvram_get_vardefn(entry->vid); 1356 1357 /* Variable must be capable of representing a NULL/deleted value. 1358 * 1359 * Since SPROM's layout is fixed, this requires IGNALL -- if 1360 * all bits are set, an IGNALL variable is treated as unset. */ 1361 if (!(var->flags & BHND_NVRAM_VF_IGNALL1)) 1362 return (EINVAL); 1363 1364 return (0); 1365 } 1366 1367 /** 1368 * Return true if @p name represents a special immutable variable name 1369 * (e.g. sromrev) that cannot be updated in an SPROM existing image. 1370 * 1371 * @param name The name to check. 1372 */ 1373 static bool 1374 bhnd_sprom_is_external_immutable(const char *name) 1375 { 1376 /* The layout revision is immutable and cannot be changed */ 1377 if (strcmp(name, BHND_NVAR_SROMREV) == 0) 1378 return (true); 1379 1380 return (false); 1381 } 1382