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