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