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