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