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