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