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