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/param.h> 34 #include <sys/endian.h> 35 36 #ifdef _KERNEL 37 #include <sys/systm.h> 38 #include <machine/_inttypes.h> 39 #else /* !_KERNEL */ 40 #include <errno.h> 41 #include <inttypes.h> 42 #include <stdint.h> 43 #include <string.h> 44 #endif /* _KERNEL */ 45 46 #include "bhnd_nvram_private.h" 47 #include "bhnd_nvram_data_spromvar.h" 48 49 static int bhnd_sprom_opcode_sort_idx(const void *lhs, const void *rhs); 50 static int bhnd_nvram_opcode_idx_vid_compare(const void *key, 51 const void *rhs); 52 53 static int bhnd_sprom_opcode_reset(bhnd_sprom_opcode_state *state); 54 55 static int bhnd_sprom_opcode_set_type(bhnd_sprom_opcode_state *state, 56 bhnd_nvram_type type); 57 58 static int bhnd_sprom_opcode_set_var(bhnd_sprom_opcode_state *state, 59 size_t vid); 60 static int bhnd_sprom_opcode_clear_var(bhnd_sprom_opcode_state *state); 61 62 static int bhnd_sprom_opcode_flush_bind(bhnd_sprom_opcode_state *state); 63 64 static int bhnd_sprom_opcode_read_opval32(bhnd_sprom_opcode_state *state, 65 uint8_t type, uint32_t *opval); 66 67 static int bhnd_sprom_opcode_step(bhnd_sprom_opcode_state *state, 68 uint8_t *opcode); 69 70 #define SPROM_OP_BAD(_state, _fmt, ...) \ 71 BHND_NV_LOG("bad encoding at %td: " _fmt, \ 72 (_state)->input - (_state)->layout->bindings, ##__VA_ARGS__) 73 74 /** 75 * Initialize SPROM opcode evaluation state. 76 * 77 * @param state The opcode state to be initialized. 78 * @param layout The SPROM layout to be parsed by this instance. 79 * 80 * 81 * @retval 0 success 82 * @retval non-zero If initialization fails, a regular unix error code will be 83 * returned. 84 */ 85 int 86 bhnd_sprom_opcode_init(bhnd_sprom_opcode_state *state, 87 const struct bhnd_sprom_layout *layout) 88 { 89 bhnd_sprom_opcode_idx_entry *idx; 90 size_t num_vars, num_idx; 91 int error; 92 93 idx = NULL; 94 95 state->layout = layout; 96 state->idx = NULL; 97 state->num_idx = 0; 98 99 /* Initialize interpretation state */ 100 if ((error = bhnd_sprom_opcode_reset(state))) 101 return (error); 102 103 /* Allocate and populate our opcode index */ 104 num_idx = state->layout->num_vars; 105 idx = bhnd_nv_calloc(num_idx, sizeof(*idx)); 106 if (idx == NULL) 107 return (ENOMEM); 108 109 for (num_vars = 0; num_vars < num_idx; num_vars++) { 110 /* Seek to next entry */ 111 if ((error = bhnd_sprom_opcode_next_var(state))) { 112 SPROM_OP_BAD(state, "error reading expected variable " 113 "entry: %d\n", error); 114 bhnd_nv_free(idx); 115 return (error); 116 } 117 118 /* Record entry state in our index */ 119 error = bhnd_sprom_opcode_init_entry(state, &idx[num_vars]); 120 if (error) { 121 SPROM_OP_BAD(state, "error initializing index for " 122 "entry: %d\n", error); 123 bhnd_nv_free(idx); 124 return (error); 125 } 126 } 127 128 /* Should have reached end of binding table; next read must return 129 * ENOENT */ 130 if ((error = bhnd_sprom_opcode_next_var(state)) != ENOENT) { 131 BHND_NV_LOG("expected EOF parsing binding table: %d\n", error); 132 bhnd_nv_free(idx); 133 return (ENXIO); 134 } 135 136 /* Reset interpretation state */ 137 if ((error = bhnd_sprom_opcode_reset(state))) { 138 bhnd_nv_free(idx); 139 return (error); 140 } 141 142 /* Make index available to opcode state evaluation */ 143 qsort(idx, num_idx, sizeof(idx[0]), bhnd_sprom_opcode_sort_idx); 144 145 state->idx = idx; 146 state->num_idx = num_idx; 147 148 return (0); 149 } 150 151 /** 152 * Reset SPROM opcode evaluation state; future evaluation will be performed 153 * starting at the first opcode. 154 * 155 * @param state The opcode state to be reset. 156 * 157 * @retval 0 success 158 * @retval non-zero If reset fails, a regular unix error code will be returned. 159 */ 160 static int 161 bhnd_sprom_opcode_reset(bhnd_sprom_opcode_state *state) 162 { 163 memset(&state->var, 0, sizeof(state->var)); 164 165 state->input = state->layout->bindings; 166 state->offset = 0; 167 state->vid = 0; 168 state->var_state = SPROM_OPCODE_VAR_STATE_NONE; 169 bit_set(state->revs, state->layout->rev); 170 171 return (0); 172 } 173 174 /** 175 * Free any resources associated with @p state. 176 * 177 * @param state An opcode state previously successfully initialized with 178 * bhnd_sprom_opcode_init(). 179 */ 180 void 181 bhnd_sprom_opcode_fini(bhnd_sprom_opcode_state *state) 182 { 183 bhnd_nv_free(state->idx); 184 } 185 186 187 /** 188 * Sort function used to prepare our index for querying; sorts 189 * bhnd_sprom_opcode_idx_entry values by variable ID, ascending. 190 */ 191 static int 192 bhnd_sprom_opcode_sort_idx(const void *lhs, const void *rhs) 193 { 194 const bhnd_sprom_opcode_idx_entry *l, *r; 195 196 l = lhs; 197 r = rhs; 198 199 if (l->vid < r->vid) 200 return (-1); 201 if (l->vid > r->vid) 202 return (1); 203 return (0); 204 } 205 206 /** 207 * Binary search comparison function used by bhnd_sprom_opcode_index_find(); 208 * searches bhnd_sprom_opcode_idx_entry values by variable ID, ascending. 209 */ 210 static int 211 bhnd_nvram_opcode_idx_vid_compare(const void *key, const void *rhs) 212 { 213 const bhnd_sprom_opcode_idx_entry *entry; 214 size_t vid; 215 216 vid = *(const size_t *)key; 217 entry = rhs; 218 219 if (vid < entry->vid) 220 return (-1); 221 if (vid > entry->vid) 222 return (1); 223 224 return (0); 225 } 226 227 /** 228 * Locate an index entry for the variable with @p name, or NULL if not found. 229 * 230 * @param state The opcode state to be queried. 231 * @param name The name to search for. 232 * 233 * @retval non-NULL If @p name is found, its index entry value will be 234 * returned. 235 * @retval NULL If @p name is not found. 236 */ 237 bhnd_sprom_opcode_idx_entry * 238 bhnd_sprom_opcode_index_find(bhnd_sprom_opcode_state *state, const char *name) 239 { 240 const struct bhnd_nvram_vardefn *var; 241 size_t vid; 242 243 /* Determine the variable ID for the given name */ 244 if ((var = bhnd_nvram_find_vardefn(name)) == NULL) 245 return (NULL); 246 247 vid = bhnd_nvram_get_vardefn_id(var); 248 249 /* Search our index for the variable ID */ 250 return (bsearch(&vid, state->idx, state->num_idx, sizeof(state->idx[0]), 251 bhnd_nvram_opcode_idx_vid_compare)); 252 } 253 254 255 /** 256 * Iterate over all index entries in @p state. 257 * 258 * @param state The opcode state to be iterated. 259 * @param[in,out] prev An entry previously returned by 260 * bhnd_sprom_opcode_index_next(), or a NULL value 261 * to begin iteration. 262 * 263 * @return Returns the next index entry name, or NULL if all entries have 264 * been iterated. 265 */ 266 bhnd_sprom_opcode_idx_entry * 267 bhnd_sprom_opcode_index_next(bhnd_sprom_opcode_state *state, 268 bhnd_sprom_opcode_idx_entry *prev) 269 { 270 size_t idxpos; 271 272 /* Get next index position */ 273 if (prev == NULL) { 274 idxpos = 0; 275 } else { 276 /* Determine current position */ 277 idxpos = (size_t)(prev - state->idx); 278 BHND_NV_ASSERT(idxpos < state->num_idx, 279 ("invalid index %zu", idxpos)); 280 281 /* Advance to next entry */ 282 idxpos++; 283 } 284 285 /* Check for EOF */ 286 if (idxpos == state->num_idx) 287 return (NULL); 288 289 return (&state->idx[idxpos]); 290 } 291 292 293 /** 294 * Initialize @p entry with the current variable's opcode state. 295 * 296 * @param state The opcode state to be saved. 297 * @param[out] entry The opcode index entry to be initialized from @p state. 298 * 299 * @retval 0 success 300 * @retval ENXIO if @p state cannot be serialized as an index entry. 301 */ 302 int 303 bhnd_sprom_opcode_init_entry(bhnd_sprom_opcode_state *state, 304 bhnd_sprom_opcode_idx_entry *entry) 305 { 306 size_t opcodes; 307 308 /* We limit the SPROM index representations to the minimal type widths 309 * capable of covering all known layouts */ 310 311 /* Save SPROM image offset */ 312 if (state->offset > UINT16_MAX) { 313 SPROM_OP_BAD(state, "cannot index large offset %u\n", 314 state->offset); 315 return (ENXIO); 316 } 317 318 entry->offset = state->offset; 319 320 /* Save current variable ID */ 321 if (state->vid > UINT16_MAX) { 322 SPROM_OP_BAD(state, "cannot index large vid %zu\n", 323 state->vid); 324 return (ENXIO); 325 } 326 entry->vid = state->vid; 327 328 /* Save opcode position */ 329 opcodes = (state->input - state->layout->bindings); 330 if (opcodes > UINT16_MAX) { 331 SPROM_OP_BAD(state, "cannot index large opcode offset " 332 "%zu\n", opcodes); 333 return (ENXIO); 334 } 335 entry->opcodes = opcodes; 336 337 return (0); 338 } 339 340 /** 341 * Reset SPROM opcode evaluation state and seek to the @p entry's position. 342 * 343 * @param state The opcode state to be reset. 344 * @param entry The indexed entry to which we'll seek the opcode state. 345 */ 346 int 347 bhnd_sprom_opcode_seek(bhnd_sprom_opcode_state *state, 348 bhnd_sprom_opcode_idx_entry *entry) 349 { 350 int error; 351 352 BHND_NV_ASSERT(entry->opcodes < state->layout->bindings_size, 353 ("index entry references invalid opcode position")); 354 355 /* Reset state */ 356 if ((error = bhnd_sprom_opcode_reset(state))) 357 return (error); 358 359 /* Seek to the indexed sprom opcode offset */ 360 state->input = state->layout->bindings + entry->opcodes; 361 362 /* Restore the indexed sprom data offset and VID */ 363 state->offset = entry->offset; 364 365 /* Restore the indexed sprom variable ID */ 366 if ((error = bhnd_sprom_opcode_set_var(state, entry->vid))) 367 return (error); 368 369 return (0); 370 } 371 372 /** 373 * Set the current revision range for @p state. This also resets 374 * variable state. 375 * 376 * @param state The opcode state to update 377 * @param start The first revision in the range. 378 * @param end The last revision in the range. 379 * 380 * @retval 0 success 381 * @retval non-zero If updating @p state fails, a regular unix error code will 382 * be returned. 383 */ 384 static inline int 385 bhnd_sprom_opcode_set_revs(bhnd_sprom_opcode_state *state, uint8_t start, 386 uint8_t end) 387 { 388 int error; 389 390 /* Validate the revision range */ 391 if (start > SPROM_OP_REV_MAX || 392 end > SPROM_OP_REV_MAX || 393 end < start) 394 { 395 SPROM_OP_BAD(state, "invalid revision range: %hhu-%hhu\n", 396 start, end); 397 return (EINVAL); 398 } 399 400 /* Clear variable state */ 401 if ((error = bhnd_sprom_opcode_clear_var(state))) 402 return (error); 403 404 /* Reset revision mask */ 405 memset(state->revs, 0x0, sizeof(state->revs)); 406 bit_nset(state->revs, start, end); 407 408 return (0); 409 } 410 411 /** 412 * Set the current variable's value mask for @p state. 413 * 414 * @param state The opcode state to update 415 * @param mask The mask to be set 416 * 417 * @retval 0 success 418 * @retval non-zero If updating @p state fails, a regular unix error code will 419 * be returned. 420 */ 421 static inline int 422 bhnd_sprom_opcode_set_mask(bhnd_sprom_opcode_state *state, uint32_t mask) 423 { 424 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { 425 SPROM_OP_BAD(state, "no open variable definition\n"); 426 return (EINVAL); 427 } 428 429 state->var.mask = mask; 430 return (0); 431 } 432 433 /** 434 * Set the current variable's value shift for @p state. 435 * 436 * @param state The opcode state to update 437 * @param shift The shift to be set 438 * 439 * @retval 0 success 440 * @retval non-zero If updating @p state fails, a regular unix error code will 441 * be returned. 442 */ 443 static inline int 444 bhnd_sprom_opcode_set_shift(bhnd_sprom_opcode_state *state, int8_t shift) 445 { 446 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { 447 SPROM_OP_BAD(state, "no open variable definition\n"); 448 return (EINVAL); 449 } 450 451 state->var.shift = shift; 452 return (0); 453 } 454 455 /** 456 * Register a new BIND/BINDN operation with @p state. 457 * 458 * @param state The opcode state to update. 459 * @param count The number of elements to be bound. 460 * @param skip_in The number of input elements to skip after each bind. 461 * @param skip_in_negative If true, the input skip should be subtracted from 462 * the current offset after each bind. If false, the input skip should be 463 * added. 464 * @param skip_out The number of output elements to skip after each bind. 465 * 466 * @retval 0 success 467 * @retval EINVAL if a variable definition is not open. 468 * @retval EINVAL if @p skip_in and @p count would trigger an overflow or 469 * underflow when applied to the current input offset. 470 * @retval ERANGE if @p skip_in would overflow uint32_t when multiplied by 471 * @p count and the scale value. 472 * @retval ERANGE if @p skip_out would overflow uint32_t when multiplied by 473 * @p count and the scale value. 474 * @retval non-zero If updating @p state otherwise fails, a regular unix error 475 * code will be returned. 476 */ 477 static inline int 478 bhnd_sprom_opcode_set_bind(bhnd_sprom_opcode_state *state, uint8_t count, 479 uint8_t skip_in, bool skip_in_negative, uint8_t skip_out) 480 { 481 uint32_t iskip_total; 482 uint32_t iskip_scaled; 483 int error; 484 485 /* Must have an open variable */ 486 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { 487 SPROM_OP_BAD(state, "no open variable definition\n"); 488 SPROM_OP_BAD(state, "BIND outside of variable definition\n"); 489 return (EINVAL); 490 } 491 492 /* Cannot overwite an existing bind definition */ 493 if (state->var.have_bind) { 494 SPROM_OP_BAD(state, "BIND overwrites existing definition\n"); 495 return (EINVAL); 496 } 497 498 /* Must have a count of at least 1 */ 499 if (count == 0) { 500 SPROM_OP_BAD(state, "BIND with zero count\n"); 501 return (EINVAL); 502 } 503 504 /* Scale skip_in by the current type width */ 505 iskip_scaled = skip_in; 506 if ((error = bhnd_sprom_opcode_apply_scale(state, &iskip_scaled))) 507 return (error); 508 509 /* Calculate total input bytes skipped: iskip_scaled * count) */ 510 if (iskip_scaled > 0 && UINT32_MAX / iskip_scaled < count) { 511 SPROM_OP_BAD(state, "skip_in %hhu would overflow", skip_in); 512 return (EINVAL); 513 } 514 515 iskip_total = iskip_scaled * count; 516 517 /* Verify that the skip_in value won't under/overflow the current 518 * input offset. */ 519 if (skip_in_negative) { 520 if (iskip_total > state->offset) { 521 SPROM_OP_BAD(state, "skip_in %hhu would underflow " 522 "offset %u\n", skip_in, state->offset); 523 return (EINVAL); 524 } 525 } else { 526 if (UINT32_MAX - iskip_total < state->offset) { 527 SPROM_OP_BAD(state, "skip_in %hhu would overflow " 528 "offset %u\n", skip_in, state->offset); 529 return (EINVAL); 530 } 531 } 532 533 /* Set the actual count and skip values */ 534 state->var.have_bind = true; 535 state->var.bind.count = count; 536 state->var.bind.skip_in = skip_in; 537 state->var.bind.skip_out = skip_out; 538 539 state->var.bind.skip_in_negative = skip_in_negative; 540 541 /* Update total bind count for the current variable */ 542 state->var.bind_total++; 543 544 return (0); 545 } 546 547 548 /** 549 * Apply and clear the current opcode bind state, if any. 550 * 551 * @param state The opcode state to update. 552 * 553 * @retval 0 success 554 * @retval non-zero If updating @p state otherwise fails, a regular unix error 555 * code will be returned. 556 */ 557 static int 558 bhnd_sprom_opcode_flush_bind(bhnd_sprom_opcode_state *state) 559 { 560 int error; 561 uint32_t skip; 562 563 /* Nothing to do? */ 564 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN || 565 !state->var.have_bind) 566 return (0); 567 568 /* Apply SPROM offset adjustment */ 569 if (state->var.bind.count > 0) { 570 skip = state->var.bind.skip_in * state->var.bind.count; 571 if ((error = bhnd_sprom_opcode_apply_scale(state, &skip))) 572 return (error); 573 574 if (state->var.bind.skip_in_negative) { 575 state->offset -= skip; 576 } else { 577 state->offset += skip; 578 } 579 } 580 581 /* Clear bind state */ 582 memset(&state->var.bind, 0, sizeof(state->var.bind)); 583 state->var.have_bind = false; 584 585 return (0); 586 } 587 588 /** 589 * Set the current type to @p type, and reset type-specific 590 * stream state. 591 * 592 * @param state The opcode state to update. 593 * @param type The new type. 594 * 595 * @retval 0 success 596 * @retval EINVAL if @p vid is not a valid variable ID. 597 */ 598 static int 599 bhnd_sprom_opcode_set_type(bhnd_sprom_opcode_state *state, bhnd_nvram_type type) 600 { 601 bhnd_nvram_type base_type; 602 size_t width; 603 uint32_t mask; 604 605 /* Must have an open variable definition */ 606 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { 607 SPROM_OP_BAD(state, "type set outside variable definition\n"); 608 return (EINVAL); 609 } 610 611 /* Fetch type width for use as our scale value */ 612 width = bhnd_nvram_type_width(type); 613 if (width == 0) { 614 SPROM_OP_BAD(state, "unsupported variable-width type: %d\n", 615 type); 616 return (EINVAL); 617 } else if (width > UINT32_MAX) { 618 SPROM_OP_BAD(state, "invalid type width %zu for type: %d\n", 619 width, type); 620 return (EINVAL); 621 } 622 623 /* Determine default mask value for the element type */ 624 base_type = bhnd_nvram_base_type(type); 625 switch (base_type) { 626 case BHND_NVRAM_TYPE_UINT8: 627 case BHND_NVRAM_TYPE_INT8: 628 case BHND_NVRAM_TYPE_CHAR: 629 mask = UINT8_MAX; 630 break; 631 case BHND_NVRAM_TYPE_UINT16: 632 case BHND_NVRAM_TYPE_INT16: 633 mask = UINT16_MAX; 634 break; 635 case BHND_NVRAM_TYPE_UINT32: 636 case BHND_NVRAM_TYPE_INT32: 637 mask = UINT32_MAX; 638 break; 639 case BHND_NVRAM_TYPE_STRING: 640 /* fallthrough (unused by SPROM) */ 641 default: 642 SPROM_OP_BAD(state, "unsupported type: %d\n", type); 643 return (EINVAL); 644 } 645 646 /* Update state */ 647 state->var.base_type = base_type; 648 state->var.mask = mask; 649 state->var.scale = (uint32_t)width; 650 651 return (0); 652 } 653 654 /** 655 * Clear current variable state, if any. 656 * 657 * @param state The opcode state to update. 658 */ 659 static int 660 bhnd_sprom_opcode_clear_var(bhnd_sprom_opcode_state *state) 661 { 662 if (state->var_state == SPROM_OPCODE_VAR_STATE_NONE) 663 return (0); 664 665 BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE, 666 ("incomplete variable definition")); 667 BHND_NV_ASSERT(!state->var.have_bind, ("stale bind state")); 668 669 memset(&state->var, 0, sizeof(state->var)); 670 state->var_state = SPROM_OPCODE_VAR_STATE_NONE; 671 672 return (0); 673 } 674 675 /** 676 * Set the current variable's array element count to @p nelem. 677 * 678 * @param state The opcode state to update. 679 * @param nelem The new array length. 680 * 681 * @retval 0 success 682 * @retval EINVAL if no open variable definition exists. 683 * @retval EINVAL if @p nelem is zero. 684 * @retval ENXIO if @p nelem is greater than one, and the current variable does 685 * not have an array type. 686 * @retval ENXIO if @p nelem exceeds the array length of the NVRAM variable 687 * definition. 688 */ 689 static int 690 bhnd_sprom_opcode_set_nelem(bhnd_sprom_opcode_state *state, uint8_t nelem) 691 { 692 const struct bhnd_nvram_vardefn *var; 693 694 /* Must have a defined variable */ 695 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { 696 SPROM_OP_BAD(state, "array length set without open variable " 697 "state"); 698 return (EINVAL); 699 } 700 701 /* Locate the actual variable definition */ 702 if ((var = bhnd_nvram_get_vardefn(state->vid)) == NULL) { 703 SPROM_OP_BAD(state, "unknown variable ID: %zu\n", state->vid); 704 return (EINVAL); 705 } 706 707 /* Must be greater than zero */ 708 if (nelem == 0) { 709 SPROM_OP_BAD(state, "invalid nelem: %hhu\n", nelem); 710 return (EINVAL); 711 } 712 713 /* If the variable is not an array-typed value, the array length 714 * must be 1 */ 715 if (!bhnd_nvram_is_array_type(var->type) && nelem != 1) { 716 SPROM_OP_BAD(state, "nelem %hhu on non-array %zu\n", nelem, 717 state->vid); 718 return (ENXIO); 719 } 720 721 /* Cannot exceed the variable's defined array length */ 722 if (nelem > var->nelem) { 723 SPROM_OP_BAD(state, "nelem %hhu exceeds %zu length %hhu\n", 724 nelem, state->vid, var->nelem); 725 return (ENXIO); 726 } 727 728 /* Valid length; update state */ 729 state->var.nelem = nelem; 730 731 return (0); 732 } 733 734 /** 735 * Set the current variable ID to @p vid, and reset variable-specific 736 * stream state. 737 * 738 * @param state The opcode state to update. 739 * @param vid The new variable ID. 740 * 741 * @retval 0 success 742 * @retval EINVAL if @p vid is not a valid variable ID. 743 */ 744 static int 745 bhnd_sprom_opcode_set_var(bhnd_sprom_opcode_state *state, size_t vid) 746 { 747 const struct bhnd_nvram_vardefn *var; 748 int error; 749 750 BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_NONE, 751 ("overwrite of open variable definition")); 752 753 /* Locate the variable definition */ 754 if ((var = bhnd_nvram_get_vardefn(vid)) == NULL) { 755 SPROM_OP_BAD(state, "unknown variable ID: %zu\n", vid); 756 return (EINVAL); 757 } 758 759 /* Update vid and var state */ 760 state->vid = vid; 761 state->var_state = SPROM_OPCODE_VAR_STATE_OPEN; 762 763 /* Initialize default variable record values */ 764 memset(&state->var, 0x0, sizeof(state->var)); 765 766 /* Set initial base type */ 767 if ((error = bhnd_sprom_opcode_set_type(state, var->type))) 768 return (error); 769 770 /* Set default array length */ 771 if ((error = bhnd_sprom_opcode_set_nelem(state, var->nelem))) 772 return (error); 773 774 return (0); 775 } 776 777 /** 778 * Mark the currently open variable definition as complete. 779 * 780 * @param state The opcode state to update. 781 * 782 * @retval 0 success 783 * @retval EINVAL if no incomplete open variable definition exists. 784 */ 785 static int 786 bhnd_sprom_opcode_end_var(bhnd_sprom_opcode_state *state) 787 { 788 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { 789 SPROM_OP_BAD(state, "no open variable definition\n"); 790 return (EINVAL); 791 } 792 793 state->var_state = SPROM_OPCODE_VAR_STATE_DONE; 794 return (0); 795 } 796 797 /** 798 * Apply the current scale to @p value. 799 * 800 * @param state The SPROM opcode state. 801 * @param[in,out] value The value to scale 802 * 803 * @retval 0 success 804 * @retval EINVAL if no open variable definition exists. 805 * @retval EINVAL if applying the current scale would overflow. 806 */ 807 int 808 bhnd_sprom_opcode_apply_scale(bhnd_sprom_opcode_state *state, uint32_t *value) 809 { 810 /* Must have a defined variable (and thus, scale) */ 811 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { 812 SPROM_OP_BAD(state, "scaled value encoded without open " 813 "variable state"); 814 return (EINVAL); 815 } 816 817 /* Applying the scale value must not overflow */ 818 if (UINT32_MAX / state->var.scale < *value) { 819 SPROM_OP_BAD(state, "cannot represent %" PRIu32 " * %" PRIu32 820 "\n", *value, state->var.scale); 821 return (EINVAL); 822 } 823 824 *value = (*value) * state->var.scale; 825 return (0); 826 } 827 828 /** 829 * Read a SPROM_OP_DATA_* value from @p opcodes. 830 * 831 * @param state The SPROM opcode state. 832 * @param type The SROM_OP_DATA_* type to be read. 833 * @param opval On success, the 32bit data representation. If @p type is signed, 834 * the value will be appropriately sign extended and may be directly cast to 835 * int32_t. 836 * 837 * @retval 0 success 838 * @retval non-zero If reading the value otherwise fails, a regular unix error 839 * code will be returned. 840 */ 841 static int 842 bhnd_sprom_opcode_read_opval32(bhnd_sprom_opcode_state *state, uint8_t type, 843 uint32_t *opval) 844 { 845 const uint8_t *p; 846 int error; 847 848 p = state->input; 849 switch (type) { 850 case SPROM_OP_DATA_I8: 851 /* Convert to signed value first, then sign extend */ 852 *opval = (int32_t)(int8_t)(*p); 853 p += 1; 854 break; 855 case SPROM_OP_DATA_U8: 856 *opval = *p; 857 p += 1; 858 break; 859 case SPROM_OP_DATA_U8_SCALED: 860 *opval = *p; 861 862 if ((error = bhnd_sprom_opcode_apply_scale(state, opval))) 863 return (error); 864 865 p += 1; 866 break; 867 case SPROM_OP_DATA_U16: 868 *opval = le16dec(p); 869 p += 2; 870 break; 871 case SPROM_OP_DATA_U32: 872 *opval = le32dec(p); 873 p += 4; 874 break; 875 default: 876 SPROM_OP_BAD(state, "unsupported data type: %hhu\n", type); 877 return (EINVAL); 878 } 879 880 /* Update read address */ 881 state->input = p; 882 883 return (0); 884 } 885 886 /** 887 * Return true if our layout revision is currently defined by the SPROM 888 * opcode state. 889 * 890 * This may be used to test whether the current opcode stream state applies 891 * to the layout that we are actually parsing. 892 * 893 * A given opcode stream may cover multiple layout revisions, switching 894 * between them prior to defining a set of variables. 895 */ 896 static inline bool 897 bhnd_sprom_opcode_matches_layout_rev(bhnd_sprom_opcode_state *state) 898 { 899 return (bit_test(state->revs, state->layout->rev)); 900 } 901 902 /** 903 * When evaluating @p state and @p opcode, rewrite @p opcode based on the 904 * current evaluation state. 905 * 906 * This allows the insertion of implicit opcodes into interpretation of the 907 * opcode stream. 908 * 909 * If @p opcode is rewritten, it should be returned from 910 * bhnd_sprom_opcode_step() instead of the opcode parsed from @p state's opcode 911 * stream. 912 * 913 * If @p opcode remains unmodified, then bhnd_sprom_opcode_step() should 914 * proceed to standard evaluation. 915 */ 916 static int 917 bhnd_sprom_opcode_rewrite_opcode(bhnd_sprom_opcode_state *state, 918 uint8_t *opcode) 919 { 920 uint8_t op; 921 int error; 922 923 op = SPROM_OPCODE_OP(*opcode); 924 switch (state->var_state) { 925 case SPROM_OPCODE_VAR_STATE_NONE: 926 /* No open variable definition */ 927 return (0); 928 929 case SPROM_OPCODE_VAR_STATE_OPEN: 930 /* Open variable definition; check for implicit closure. */ 931 932 /* 933 * If a variable definition contains no explicit bind 934 * instructions prior to closure, we must generate a DO_BIND 935 * instruction with count and skip values of 1. 936 */ 937 if (SPROM_OP_IS_VAR_END(op) && 938 state->var.bind_total == 0) 939 { 940 uint8_t count, skip_in, skip_out; 941 bool skip_in_negative; 942 943 /* Create bind with skip_in/skip_out of 1, count of 1 */ 944 count = 1; 945 skip_in = 1; 946 skip_out = 1; 947 skip_in_negative = false; 948 949 error = bhnd_sprom_opcode_set_bind(state, count, 950 skip_in, skip_in_negative, skip_out); 951 if (error) 952 return (error); 953 954 /* Return DO_BIND */ 955 *opcode = SPROM_OPCODE_DO_BIND | 956 (0 << SPROM_OP_BIND_SKIP_IN_SIGN) | 957 (1 << SPROM_OP_BIND_SKIP_IN_SHIFT) | 958 (1 << SPROM_OP_BIND_SKIP_OUT_SHIFT); 959 960 return (0); 961 } 962 963 /* 964 * If a variable is implicitly closed (e.g. by a new variable 965 * definition), we must generate a VAR_END instruction. 966 */ 967 if (SPROM_OP_IS_IMPLICIT_VAR_END(op)) { 968 /* Mark as complete */ 969 if ((error = bhnd_sprom_opcode_end_var(state))) 970 return (error); 971 972 /* Return VAR_END */ 973 *opcode = SPROM_OPCODE_VAR_END; 974 return (0); 975 } 976 break; 977 978 979 case SPROM_OPCODE_VAR_STATE_DONE: 980 /* Previously completed variable definition. Discard variable 981 * state */ 982 return (bhnd_sprom_opcode_clear_var(state)); 983 } 984 985 /* Nothing to do */ 986 return (0); 987 } 988 989 /** 990 * Evaluate one opcode from @p state. 991 * 992 * @param state The opcode state to be evaluated. 993 * @param[out] opcode On success, the evaluated opcode 994 * 995 * @retval 0 success 996 * @retval ENOENT if EOF is reached 997 * @retval non-zero if evaluation otherwise fails, a regular unix error 998 * code will be returned. 999 */ 1000 static int 1001 bhnd_sprom_opcode_step(bhnd_sprom_opcode_state *state, uint8_t *opcode) 1002 { 1003 int error; 1004 1005 while (*state->input != SPROM_OPCODE_EOF) { 1006 uint32_t val; 1007 uint8_t op, rewrite, immd; 1008 1009 /* Fetch opcode */ 1010 *opcode = *state->input; 1011 op = SPROM_OPCODE_OP(*opcode); 1012 immd = SPROM_OPCODE_IMM(*opcode); 1013 1014 /* Clear any existing bind state */ 1015 if ((error = bhnd_sprom_opcode_flush_bind(state))) 1016 return (error); 1017 1018 /* Insert local opcode based on current state? */ 1019 rewrite = *opcode; 1020 if ((error = bhnd_sprom_opcode_rewrite_opcode(state, &rewrite))) 1021 return (error); 1022 1023 if (rewrite != *opcode) { 1024 /* Provide rewritten opcode */ 1025 *opcode = rewrite; 1026 1027 /* We must keep evaluating until we hit a state 1028 * applicable to the SPROM revision we're parsing */ 1029 if (!bhnd_sprom_opcode_matches_layout_rev(state)) 1030 continue; 1031 1032 return (0); 1033 } 1034 1035 /* Advance input */ 1036 state->input++; 1037 1038 switch (op) { 1039 case SPROM_OPCODE_VAR_IMM: 1040 if ((error = bhnd_sprom_opcode_set_var(state, immd))) 1041 return (error); 1042 break; 1043 1044 case SPROM_OPCODE_VAR_REL_IMM: 1045 error = bhnd_sprom_opcode_set_var(state, 1046 state->vid + immd); 1047 if (error) 1048 return (error); 1049 break; 1050 1051 case SPROM_OPCODE_VAR: 1052 error = bhnd_sprom_opcode_read_opval32(state, immd, 1053 &val); 1054 if (error) 1055 return (error); 1056 1057 if ((error = bhnd_sprom_opcode_set_var(state, val))) 1058 return (error); 1059 1060 break; 1061 1062 case SPROM_OPCODE_VAR_END: 1063 if ((error = bhnd_sprom_opcode_end_var(state))) 1064 return (error); 1065 break; 1066 1067 case SPROM_OPCODE_NELEM: 1068 immd = *state->input; 1069 if ((error = bhnd_sprom_opcode_set_nelem(state, immd))) 1070 return (error); 1071 1072 state->input++; 1073 break; 1074 1075 case SPROM_OPCODE_DO_BIND: 1076 case SPROM_OPCODE_DO_BINDN: { 1077 uint8_t count, skip_in, skip_out; 1078 bool skip_in_negative; 1079 1080 /* Fetch skip arguments */ 1081 skip_in = (immd & SPROM_OP_BIND_SKIP_IN_MASK) >> 1082 SPROM_OP_BIND_SKIP_IN_SHIFT; 1083 1084 skip_in_negative = 1085 ((immd & SPROM_OP_BIND_SKIP_IN_SIGN) != 0); 1086 1087 skip_out = (immd & SPROM_OP_BIND_SKIP_OUT_MASK) >> 1088 SPROM_OP_BIND_SKIP_OUT_SHIFT; 1089 1090 /* Fetch count argument (if any) */ 1091 if (op == SPROM_OPCODE_DO_BINDN) { 1092 /* Count is provided as trailing U8 */ 1093 count = *state->input; 1094 state->input++; 1095 } else { 1096 count = 1; 1097 } 1098 1099 /* Set BIND state */ 1100 error = bhnd_sprom_opcode_set_bind(state, count, 1101 skip_in, skip_in_negative, skip_out); 1102 if (error) 1103 return (error); 1104 1105 break; 1106 } 1107 case SPROM_OPCODE_DO_BINDN_IMM: { 1108 uint8_t count, skip_in, skip_out; 1109 bool skip_in_negative; 1110 1111 /* Implicit skip_in/skip_out of 1, count encoded as immd 1112 * value */ 1113 count = immd; 1114 skip_in = 1; 1115 skip_out = 1; 1116 skip_in_negative = false; 1117 1118 error = bhnd_sprom_opcode_set_bind(state, count, 1119 skip_in, skip_in_negative, skip_out); 1120 if (error) 1121 return (error); 1122 break; 1123 } 1124 1125 case SPROM_OPCODE_REV_IMM: 1126 error = bhnd_sprom_opcode_set_revs(state, immd, immd); 1127 if (error) 1128 return (error); 1129 break; 1130 1131 case SPROM_OPCODE_REV_RANGE: { 1132 uint8_t range; 1133 uint8_t rstart, rend; 1134 1135 /* Revision range is encoded in next byte, as 1136 * { uint8_t start:4, uint8_t end:4 } */ 1137 range = *state->input; 1138 rstart = (range & SPROM_OP_REV_START_MASK) >> 1139 SPROM_OP_REV_START_SHIFT; 1140 rend = (range & SPROM_OP_REV_END_MASK) >> 1141 SPROM_OP_REV_END_SHIFT; 1142 1143 /* Update revision bitmask */ 1144 error = bhnd_sprom_opcode_set_revs(state, rstart, rend); 1145 if (error) 1146 return (error); 1147 1148 /* Advance input */ 1149 state->input++; 1150 break; 1151 } 1152 case SPROM_OPCODE_MASK_IMM: 1153 if ((error = bhnd_sprom_opcode_set_mask(state, immd))) 1154 return (error); 1155 break; 1156 1157 case SPROM_OPCODE_MASK: 1158 error = bhnd_sprom_opcode_read_opval32(state, immd, 1159 &val); 1160 if (error) 1161 return (error); 1162 1163 if ((error = bhnd_sprom_opcode_set_mask(state, val))) 1164 return (error); 1165 break; 1166 1167 case SPROM_OPCODE_SHIFT_IMM: 1168 error = bhnd_sprom_opcode_set_shift(state, immd * 2); 1169 if (error) 1170 return (error); 1171 break; 1172 1173 case SPROM_OPCODE_SHIFT: { 1174 int8_t shift; 1175 1176 if (immd == SPROM_OP_DATA_I8) { 1177 shift = (int8_t)(*state->input); 1178 } else if (immd == SPROM_OP_DATA_U8) { 1179 val = *state->input; 1180 if (val > INT8_MAX) { 1181 SPROM_OP_BAD(state, "invalid shift " 1182 "value: %#x\n", val); 1183 } 1184 1185 shift = val; 1186 } else { 1187 SPROM_OP_BAD(state, "unsupported shift data " 1188 "type: %#hhx\n", immd); 1189 return (EINVAL); 1190 } 1191 1192 if ((error = bhnd_sprom_opcode_set_shift(state, shift))) 1193 return (error); 1194 1195 state->input++; 1196 break; 1197 } 1198 case SPROM_OPCODE_OFFSET_REL_IMM: 1199 /* Fetch unscaled relative offset */ 1200 val = immd; 1201 1202 /* Apply scale */ 1203 error = bhnd_sprom_opcode_apply_scale(state, &val); 1204 if (error) 1205 return (error); 1206 1207 /* Adding val must not overflow our offset */ 1208 if (UINT32_MAX - state->offset < val) { 1209 BHND_NV_LOG("offset out of range\n"); 1210 return (EINVAL); 1211 } 1212 1213 /* Adjust offset */ 1214 state->offset += val; 1215 break; 1216 case SPROM_OPCODE_OFFSET: 1217 error = bhnd_sprom_opcode_read_opval32(state, immd, 1218 &val); 1219 if (error) 1220 return (error); 1221 1222 state->offset = val; 1223 break; 1224 1225 case SPROM_OPCODE_TYPE: 1226 /* Type follows as U8 */ 1227 immd = *state->input; 1228 state->input++; 1229 1230 /* fall through */ 1231 case SPROM_OPCODE_TYPE_IMM: 1232 switch (immd) { 1233 case BHND_NVRAM_TYPE_UINT8: 1234 case BHND_NVRAM_TYPE_UINT16: 1235 case BHND_NVRAM_TYPE_UINT32: 1236 case BHND_NVRAM_TYPE_UINT64: 1237 case BHND_NVRAM_TYPE_INT8: 1238 case BHND_NVRAM_TYPE_INT16: 1239 case BHND_NVRAM_TYPE_INT32: 1240 case BHND_NVRAM_TYPE_INT64: 1241 case BHND_NVRAM_TYPE_CHAR: 1242 case BHND_NVRAM_TYPE_STRING: 1243 error = bhnd_sprom_opcode_set_type(state, 1244 (bhnd_nvram_type)immd); 1245 if (error) 1246 return (error); 1247 break; 1248 default: 1249 BHND_NV_LOG("unrecognized type %#hhx\n", immd); 1250 return (EINVAL); 1251 } 1252 break; 1253 1254 default: 1255 BHND_NV_LOG("unrecognized opcode %#hhx\n", *opcode); 1256 return (EINVAL); 1257 } 1258 1259 /* We must keep evaluating until we hit a state applicable to 1260 * the SPROM revision we're parsing */ 1261 if (bhnd_sprom_opcode_matches_layout_rev(state)) 1262 return (0); 1263 } 1264 1265 /* End of opcode stream */ 1266 return (ENOENT); 1267 } 1268 1269 /** 1270 * Reset SPROM opcode evaluation state, seek to the @p entry's position, 1271 * and perform complete evaluation of the variable's opcodes. 1272 * 1273 * @param state The opcode state to be to be evaluated. 1274 * @param entry The indexed variable location. 1275 * 1276 * @retval 0 success 1277 * @retval non-zero If evaluation fails, a regular unix error code will be 1278 * returned. 1279 */ 1280 int 1281 bhnd_sprom_opcode_eval_var(bhnd_sprom_opcode_state *state, 1282 bhnd_sprom_opcode_idx_entry *entry) 1283 { 1284 uint8_t opcode; 1285 int error; 1286 1287 /* Seek to entry */ 1288 if ((error = bhnd_sprom_opcode_seek(state, entry))) 1289 return (error); 1290 1291 /* Parse full variable definition */ 1292 while ((error = bhnd_sprom_opcode_step(state, &opcode)) == 0) { 1293 /* Iterate until VAR_END */ 1294 if (SPROM_OPCODE_OP(opcode) != SPROM_OPCODE_VAR_END) 1295 continue; 1296 1297 BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE, 1298 ("incomplete variable definition")); 1299 1300 return (0); 1301 } 1302 1303 /* Error parsing definition */ 1304 return (error); 1305 } 1306 1307 /** 1308 * Evaluate @p state until the next variable definition is found. 1309 * 1310 * @param state The opcode state to be evaluated. 1311 * 1312 * @retval 0 success 1313 * @retval ENOENT if no additional variable definitions are available. 1314 * @retval non-zero if evaluation otherwise fails, a regular unix error 1315 * code will be returned. 1316 */ 1317 int 1318 bhnd_sprom_opcode_next_var(bhnd_sprom_opcode_state *state) 1319 { 1320 uint8_t opcode; 1321 int error; 1322 1323 /* Step until we hit a variable opcode */ 1324 while ((error = bhnd_sprom_opcode_step(state, &opcode)) == 0) { 1325 switch (SPROM_OPCODE_OP(opcode)) { 1326 case SPROM_OPCODE_VAR: 1327 case SPROM_OPCODE_VAR_IMM: 1328 case SPROM_OPCODE_VAR_REL_IMM: 1329 BHND_NV_ASSERT( 1330 state->var_state == SPROM_OPCODE_VAR_STATE_OPEN, 1331 ("missing variable definition")); 1332 1333 return (0); 1334 default: 1335 continue; 1336 } 1337 } 1338 1339 /* Reached EOF, or evaluation failed */ 1340 return (error); 1341 } 1342 1343 /** 1344 * Evaluate @p state until the next binding for the current variable definition 1345 * is found. 1346 * 1347 * @param state The opcode state to be evaluated. 1348 * 1349 * @retval 0 success 1350 * @retval ENOENT if no additional binding opcodes are found prior to reaching 1351 * a new variable definition, or the end of @p state's binding opcodes. 1352 * @retval non-zero if evaluation otherwise fails, a regular unix error 1353 * code will be returned. 1354 */ 1355 int 1356 bhnd_sprom_opcode_next_binding(bhnd_sprom_opcode_state *state) 1357 { 1358 uint8_t opcode; 1359 int error; 1360 1361 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) 1362 return (EINVAL); 1363 1364 /* Step until we hit a bind opcode, or a new variable */ 1365 while ((error = bhnd_sprom_opcode_step(state, &opcode)) == 0) { 1366 switch (SPROM_OPCODE_OP(opcode)) { 1367 case SPROM_OPCODE_DO_BIND: 1368 case SPROM_OPCODE_DO_BINDN: 1369 case SPROM_OPCODE_DO_BINDN_IMM: 1370 /* Found next bind */ 1371 BHND_NV_ASSERT( 1372 state->var_state == SPROM_OPCODE_VAR_STATE_OPEN, 1373 ("missing variable definition")); 1374 BHND_NV_ASSERT(state->var.have_bind, ("missing bind")); 1375 1376 return (0); 1377 1378 case SPROM_OPCODE_VAR_END: 1379 /* No further binding opcodes */ 1380 BHND_NV_ASSERT( 1381 state->var_state == SPROM_OPCODE_VAR_STATE_DONE, 1382 ("variable definition still available")); 1383 return (ENOENT); 1384 } 1385 } 1386 1387 /* Not found, or evaluation failed */ 1388 return (error); 1389 } 1390