2 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
34 #include <sys/endian.h>
37 #include <sys/systm.h>
38 #include <machine/_inttypes.h>
46 #include "bhnd_nvram_private.h"
47 #include "bhnd_nvram_data_spromvar.h"
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,
53 static int bhnd_sprom_opcode_reset(bhnd_sprom_opcode_state *state);
55 static int bhnd_sprom_opcode_set_type(bhnd_sprom_opcode_state *state,
56 bhnd_nvram_type type);
58 static int bhnd_sprom_opcode_set_var(bhnd_sprom_opcode_state *state,
60 static int bhnd_sprom_opcode_clear_var(bhnd_sprom_opcode_state *state);
62 static int bhnd_sprom_opcode_flush_bind(bhnd_sprom_opcode_state *state);
64 static int bhnd_sprom_opcode_read_opval32(bhnd_sprom_opcode_state *state,
65 uint8_t type, uint32_t *opval);
67 static int bhnd_sprom_opcode_step(bhnd_sprom_opcode_state *state,
70 #define SPROM_OP_BAD(_state, _fmt, ...) \
71 BHND_NV_LOG("bad encoding at %td: " _fmt, \
72 (_state)->input - (_state)->layout->bindings, ##__VA_ARGS__)
75 * Initialize SPROM opcode evaluation state.
77 * @param state The opcode state to be initialized.
78 * @param layout The SPROM layout to be parsed by this instance.
82 * @retval non-zero If initialization fails, a regular unix error code will be
86 bhnd_sprom_opcode_init(bhnd_sprom_opcode_state *state,
87 const struct bhnd_sprom_layout *layout)
89 bhnd_sprom_opcode_idx_entry *idx;
90 size_t num_vars, num_idx;
95 state->layout = layout;
99 /* Initialize interpretation state */
100 if ((error = bhnd_sprom_opcode_reset(state)))
103 /* Allocate and populate our opcode index */
104 num_idx = state->layout->num_vars;
105 idx = bhnd_nv_calloc(num_idx, sizeof(*idx));
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);
118 /* Record entry state in our index */
119 error = bhnd_sprom_opcode_init_entry(state, &idx[num_vars]);
121 SPROM_OP_BAD(state, "error initializing index for "
122 "entry: %d\n", error);
128 /* Should have reached end of binding table; next read must return
130 if ((error = bhnd_sprom_opcode_next_var(state)) != ENOENT) {
131 BHND_NV_LOG("expected EOF parsing binding table: %d\n", error);
136 /* Reset interpretation state */
137 if ((error = bhnd_sprom_opcode_reset(state))) {
142 /* Make index available to opcode state evaluation */
143 qsort(idx, num_idx, sizeof(idx[0]), bhnd_sprom_opcode_sort_idx);
146 state->num_idx = num_idx;
152 * Reset SPROM opcode evaluation state; future evaluation will be performed
153 * starting at the first opcode.
155 * @param state The opcode state to be reset.
158 * @retval non-zero If reset fails, a regular unix error code will be returned.
161 bhnd_sprom_opcode_reset(bhnd_sprom_opcode_state *state)
163 memset(&state->var, 0, sizeof(state->var));
165 state->input = state->layout->bindings;
168 state->var_state = SPROM_OPCODE_VAR_STATE_NONE;
169 bit_set(state->revs, state->layout->rev);
175 * Free any resources associated with @p state.
177 * @param state An opcode state previously successfully initialized with
178 * bhnd_sprom_opcode_init().
181 bhnd_sprom_opcode_fini(bhnd_sprom_opcode_state *state)
183 bhnd_nv_free(state->idx);
188 * Sort function used to prepare our index for querying; sorts
189 * bhnd_sprom_opcode_idx_entry values by variable ID, ascending.
192 bhnd_sprom_opcode_sort_idx(const void *lhs, const void *rhs)
194 const bhnd_sprom_opcode_idx_entry *l, *r;
207 * Binary search comparison function used by bhnd_sprom_opcode_index_find();
208 * searches bhnd_sprom_opcode_idx_entry values by variable ID, ascending.
211 bhnd_nvram_opcode_idx_vid_compare(const void *key, const void *rhs)
213 const bhnd_sprom_opcode_idx_entry *entry;
216 vid = *(const size_t *)key;
219 if (vid < entry->vid)
221 if (vid > entry->vid)
228 * Locate an index entry for the variable with @p name, or NULL if not found.
230 * @param state The opcode state to be queried.
231 * @param name The name to search for.
233 * @retval non-NULL If @p name is found, its index entry value will be
235 * @retval NULL If @p name is not found.
237 bhnd_sprom_opcode_idx_entry *
238 bhnd_sprom_opcode_index_find(bhnd_sprom_opcode_state *state, const char *name)
240 const struct bhnd_nvram_vardefn *var;
243 /* Determine the variable ID for the given name */
244 if ((var = bhnd_nvram_find_vardefn(name)) == NULL)
247 vid = bhnd_nvram_get_vardefn_id(var);
249 /* Search our index for the variable ID */
250 return (bsearch(&vid, state->idx, state->num_idx, sizeof(state->idx[0]),
251 bhnd_nvram_opcode_idx_vid_compare));
256 * Iterate over all index entries in @p state.
258 * @param state The opcode state to be iterated.
259 * @param[in,out] prev An entry previously returned by
260 * bhnd_sprom_opcode_index_next(), or a NULL value
261 * to begin iteration.
263 * @return Returns the next index entry name, or NULL if all entries have
266 bhnd_sprom_opcode_idx_entry *
267 bhnd_sprom_opcode_index_next(bhnd_sprom_opcode_state *state,
268 bhnd_sprom_opcode_idx_entry *prev)
272 /* Get next index position */
276 /* Determine current position */
277 idxpos = (size_t)(prev - state->idx);
278 BHND_NV_ASSERT(idxpos < state->num_idx,
279 ("invalid index %zu", idxpos));
281 /* Advance to next entry */
286 if (idxpos == state->num_idx)
289 return (&state->idx[idxpos]);
294 * Initialize @p entry with the current variable's opcode state.
296 * @param state The opcode state to be saved.
297 * @param[out] entry The opcode index entry to be initialized from @p state.
300 * @retval ENXIO if @p state cannot be serialized as an index entry.
303 bhnd_sprom_opcode_init_entry(bhnd_sprom_opcode_state *state,
304 bhnd_sprom_opcode_idx_entry *entry)
308 /* We limit the SPROM index representations to the minimal type widths
309 * capable of covering all known layouts */
311 /* Save SPROM image offset */
312 if (state->offset > UINT16_MAX) {
313 SPROM_OP_BAD(state, "cannot index large offset %u\n",
318 entry->offset = state->offset;
320 /* Save current variable ID */
321 if (state->vid > UINT16_MAX) {
322 SPROM_OP_BAD(state, "cannot index large vid %zu\n",
326 entry->vid = state->vid;
328 /* Save opcode position */
329 opcodes = (state->input - state->layout->bindings);
330 if (opcodes > UINT16_MAX) {
331 SPROM_OP_BAD(state, "cannot index large opcode offset "
335 entry->opcodes = opcodes;
341 * Reset SPROM opcode evaluation state and seek to the @p entry's position.
343 * @param state The opcode state to be reset.
344 * @param entry The indexed entry to which we'll seek the opcode state.
347 bhnd_sprom_opcode_seek(bhnd_sprom_opcode_state *state,
348 bhnd_sprom_opcode_idx_entry *entry)
352 BHND_NV_ASSERT(entry->opcodes < state->layout->bindings_size,
353 ("index entry references invalid opcode position"));
356 if ((error = bhnd_sprom_opcode_reset(state)))
359 /* Seek to the indexed sprom opcode offset */
360 state->input = state->layout->bindings + entry->opcodes;
362 /* Restore the indexed sprom data offset and VID */
363 state->offset = entry->offset;
365 /* Restore the indexed sprom variable ID */
366 if ((error = bhnd_sprom_opcode_set_var(state, entry->vid)))
373 * Set the current revision range for @p state. This also resets
376 * @param state The opcode state to update
377 * @param start The first revision in the range.
378 * @param end The last revision in the range.
381 * @retval non-zero If updating @p state fails, a regular unix error code will
385 bhnd_sprom_opcode_set_revs(bhnd_sprom_opcode_state *state, uint8_t start,
390 /* Validate the revision range */
391 if (start > SPROM_OP_REV_MAX ||
392 end > SPROM_OP_REV_MAX ||
395 SPROM_OP_BAD(state, "invalid revision range: %hhu-%hhu\n",
400 /* Clear variable state */
401 if ((error = bhnd_sprom_opcode_clear_var(state)))
404 /* Reset revision mask */
405 memset(state->revs, 0x0, sizeof(state->revs));
406 bit_nset(state->revs, start, end);
412 * Set the current variable's value mask for @p state.
414 * @param state The opcode state to update
415 * @param mask The mask to be set
418 * @retval non-zero If updating @p state fails, a regular unix error code will
422 bhnd_sprom_opcode_set_mask(bhnd_sprom_opcode_state *state, uint32_t mask)
424 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
425 SPROM_OP_BAD(state, "no open variable definition\n");
429 state->var.mask = mask;
434 * Set the current variable's value shift for @p state.
436 * @param state The opcode state to update
437 * @param shift The shift to be set
440 * @retval non-zero If updating @p state fails, a regular unix error code will
444 bhnd_sprom_opcode_set_shift(bhnd_sprom_opcode_state *state, int8_t shift)
446 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
447 SPROM_OP_BAD(state, "no open variable definition\n");
451 state->var.shift = shift;
456 * Register a new BIND/BINDN operation with @p state.
458 * @param state The opcode state to update.
459 * @param count The number of elements to be bound.
460 * @param skip_in The number of input elements to skip after each bind.
461 * @param skip_in_negative If true, the input skip should be subtracted from
462 * the current offset after each bind. If false, the input skip should be
464 * @param skip_out The number of output elements to skip after each bind.
467 * @retval EINVAL if a variable definition is not open.
468 * @retval EINVAL if @p skip_in and @p count would trigger an overflow or
469 * underflow when applied to the current input offset.
470 * @retval ERANGE if @p skip_in would overflow uint32_t when multiplied by
471 * @p count and the scale value.
472 * @retval ERANGE if @p skip_out would overflow uint32_t when multiplied by
473 * @p count and the scale value.
474 * @retval non-zero If updating @p state otherwise fails, a regular unix error
475 * code will be returned.
478 bhnd_sprom_opcode_set_bind(bhnd_sprom_opcode_state *state, uint8_t count,
479 uint8_t skip_in, bool skip_in_negative, uint8_t skip_out)
481 uint32_t iskip_total;
482 uint32_t iskip_scaled;
485 /* Must have an open variable */
486 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
487 SPROM_OP_BAD(state, "no open variable definition\n");
488 SPROM_OP_BAD(state, "BIND outside of variable definition\n");
492 /* Cannot overwite an existing bind definition */
493 if (state->var.have_bind) {
494 SPROM_OP_BAD(state, "BIND overwrites existing definition\n");
498 /* Must have a count of at least 1 */
500 SPROM_OP_BAD(state, "BIND with zero count\n");
504 /* Scale skip_in by the current type width */
505 iskip_scaled = skip_in;
506 if ((error = bhnd_sprom_opcode_apply_scale(state, &iskip_scaled)))
509 /* Calculate total input bytes skipped: iskip_scaled * count) */
510 if (iskip_scaled > 0 && UINT32_MAX / iskip_scaled < count) {
511 SPROM_OP_BAD(state, "skip_in %hhu would overflow", skip_in);
515 iskip_total = iskip_scaled * count;
517 /* Verify that the skip_in value won't under/overflow the current
519 if (skip_in_negative) {
520 if (iskip_total > state->offset) {
521 SPROM_OP_BAD(state, "skip_in %hhu would underflow "
522 "offset %u\n", skip_in, state->offset);
526 if (UINT32_MAX - iskip_total < state->offset) {
527 SPROM_OP_BAD(state, "skip_in %hhu would overflow "
528 "offset %u\n", skip_in, state->offset);
533 /* Set the actual count and skip values */
534 state->var.have_bind = true;
535 state->var.bind.count = count;
536 state->var.bind.skip_in = skip_in;
537 state->var.bind.skip_out = skip_out;
539 state->var.bind.skip_in_negative = skip_in_negative;
541 /* Update total bind count for the current variable */
542 state->var.bind_total++;
549 * Apply and clear the current opcode bind state, if any.
551 * @param state The opcode state to update.
554 * @retval non-zero If updating @p state otherwise fails, a regular unix error
555 * code will be returned.
558 bhnd_sprom_opcode_flush_bind(bhnd_sprom_opcode_state *state)
564 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN ||
565 !state->var.have_bind)
568 /* Apply SPROM offset adjustment */
569 if (state->var.bind.count > 0) {
570 skip = state->var.bind.skip_in * state->var.bind.count;
571 if ((error = bhnd_sprom_opcode_apply_scale(state, &skip)))
574 if (state->var.bind.skip_in_negative) {
575 state->offset -= skip;
577 state->offset += skip;
581 /* Clear bind state */
582 memset(&state->var.bind, 0, sizeof(state->var.bind));
583 state->var.have_bind = false;
589 * Set the current type to @p type, and reset type-specific
592 * @param state The opcode state to update.
593 * @param type The new type.
596 * @retval EINVAL if @p vid is not a valid variable ID.
599 bhnd_sprom_opcode_set_type(bhnd_sprom_opcode_state *state, bhnd_nvram_type type)
601 bhnd_nvram_type base_type;
605 /* Must have an open variable definition */
606 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
607 SPROM_OP_BAD(state, "type set outside variable definition\n");
611 /* Fetch type width for use as our scale value */
612 width = bhnd_nvram_type_width(type);
614 SPROM_OP_BAD(state, "unsupported variable-width type: %d\n",
617 } else if (width > UINT32_MAX) {
618 SPROM_OP_BAD(state, "invalid type width %zu for type: %d\n",
623 /* Determine default mask value for the element type */
624 base_type = bhnd_nvram_base_type(type);
626 case BHND_NVRAM_TYPE_UINT8:
627 case BHND_NVRAM_TYPE_INT8:
628 case BHND_NVRAM_TYPE_CHAR:
631 case BHND_NVRAM_TYPE_UINT16:
632 case BHND_NVRAM_TYPE_INT16:
635 case BHND_NVRAM_TYPE_UINT32:
636 case BHND_NVRAM_TYPE_INT32:
639 case BHND_NVRAM_TYPE_STRING:
640 /* fallthrough (unused by SPROM) */
642 SPROM_OP_BAD(state, "unsupported type: %d\n", type);
647 state->var.base_type = base_type;
648 state->var.mask = mask;
649 state->var.scale = (uint32_t)width;
655 * Clear current variable state, if any.
657 * @param state The opcode state to update.
660 bhnd_sprom_opcode_clear_var(bhnd_sprom_opcode_state *state)
662 if (state->var_state == SPROM_OPCODE_VAR_STATE_NONE)
665 BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
666 ("incomplete variable definition"));
667 BHND_NV_ASSERT(!state->var.have_bind, ("stale bind state"));
669 memset(&state->var, 0, sizeof(state->var));
670 state->var_state = SPROM_OPCODE_VAR_STATE_NONE;
676 * Set the current variable's array element count to @p nelem.
678 * @param state The opcode state to update.
679 * @param nelem The new array length.
682 * @retval EINVAL if no open variable definition exists.
683 * @retval EINVAL if @p nelem is zero.
684 * @retval ENXIO if @p nelem is greater than one, and the current variable does
685 * not have an array type.
686 * @retval ENXIO if @p nelem exceeds the array length of the NVRAM variable
690 bhnd_sprom_opcode_set_nelem(bhnd_sprom_opcode_state *state, uint8_t nelem)
692 const struct bhnd_nvram_vardefn *var;
694 /* Must have a defined variable */
695 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
696 SPROM_OP_BAD(state, "array length set without open variable "
701 /* Locate the actual variable definition */
702 if ((var = bhnd_nvram_get_vardefn(state->vid)) == NULL) {
703 SPROM_OP_BAD(state, "unknown variable ID: %zu\n", state->vid);
707 /* Must be greater than zero */
709 SPROM_OP_BAD(state, "invalid nelem: %hhu\n", nelem);
713 /* If the variable is not an array-typed value, the array length
715 if (!bhnd_nvram_is_array_type(var->type) && nelem != 1) {
716 SPROM_OP_BAD(state, "nelem %hhu on non-array %zu\n", nelem,
721 /* Cannot exceed the variable's defined array length */
722 if (nelem > var->nelem) {
723 SPROM_OP_BAD(state, "nelem %hhu exceeds %zu length %hhu\n",
724 nelem, state->vid, var->nelem);
728 /* Valid length; update state */
729 state->var.nelem = nelem;
735 * Set the current variable ID to @p vid, and reset variable-specific
738 * @param state The opcode state to update.
739 * @param vid The new variable ID.
742 * @retval EINVAL if @p vid is not a valid variable ID.
745 bhnd_sprom_opcode_set_var(bhnd_sprom_opcode_state *state, size_t vid)
747 const struct bhnd_nvram_vardefn *var;
750 BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_NONE,
751 ("overwrite of open variable definition"));
753 /* Locate the variable definition */
754 if ((var = bhnd_nvram_get_vardefn(vid)) == NULL) {
755 SPROM_OP_BAD(state, "unknown variable ID: %zu\n", vid);
759 /* Update vid and var state */
761 state->var_state = SPROM_OPCODE_VAR_STATE_OPEN;
763 /* Initialize default variable record values */
764 memset(&state->var, 0x0, sizeof(state->var));
766 /* Set initial base type */
767 if ((error = bhnd_sprom_opcode_set_type(state, var->type)))
770 /* Set default array length */
771 if ((error = bhnd_sprom_opcode_set_nelem(state, var->nelem)))
778 * Mark the currently open variable definition as complete.
780 * @param state The opcode state to update.
783 * @retval EINVAL if no incomplete open variable definition exists.
786 bhnd_sprom_opcode_end_var(bhnd_sprom_opcode_state *state)
788 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
789 SPROM_OP_BAD(state, "no open variable definition\n");
793 state->var_state = SPROM_OPCODE_VAR_STATE_DONE;
798 * Apply the current scale to @p value.
800 * @param state The SPROM opcode state.
801 * @param[in,out] value The value to scale
804 * @retval EINVAL if no open variable definition exists.
805 * @retval EINVAL if applying the current scale would overflow.
808 bhnd_sprom_opcode_apply_scale(bhnd_sprom_opcode_state *state, uint32_t *value)
810 /* Must have a defined variable (and thus, scale) */
811 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
812 SPROM_OP_BAD(state, "scaled value encoded without open "
817 /* Applying the scale value must not overflow */
818 if (UINT32_MAX / state->var.scale < *value) {
819 SPROM_OP_BAD(state, "cannot represent %" PRIu32 " * %" PRIu32
820 "\n", *value, state->var.scale);
824 *value = (*value) * state->var.scale;
829 * Read a SPROM_OP_DATA_* value from @p opcodes.
831 * @param state The SPROM opcode state.
832 * @param type The SROM_OP_DATA_* type to be read.
833 * @param opval On success, the 32bit data representation. If @p type is signed,
834 * the value will be appropriately sign extended and may be directly cast to
838 * @retval non-zero If reading the value otherwise fails, a regular unix error
839 * code will be returned.
842 bhnd_sprom_opcode_read_opval32(bhnd_sprom_opcode_state *state, uint8_t type,
850 case SPROM_OP_DATA_I8:
851 /* Convert to signed value first, then sign extend */
852 *opval = (int32_t)(int8_t)(*p);
855 case SPROM_OP_DATA_U8:
859 case SPROM_OP_DATA_U8_SCALED:
862 if ((error = bhnd_sprom_opcode_apply_scale(state, opval)))
867 case SPROM_OP_DATA_U16:
871 case SPROM_OP_DATA_U32:
876 SPROM_OP_BAD(state, "unsupported data type: %hhu\n", type);
880 /* Update read address */
887 * Return true if our layout revision is currently defined by the SPROM
890 * This may be used to test whether the current opcode stream state applies
891 * to the layout that we are actually parsing.
893 * A given opcode stream may cover multiple layout revisions, switching
894 * between them prior to defining a set of variables.
897 bhnd_sprom_opcode_matches_layout_rev(bhnd_sprom_opcode_state *state)
899 return (bit_test(state->revs, state->layout->rev));
903 * When evaluating @p state and @p opcode, rewrite @p opcode based on the
904 * current evaluation state.
906 * This allows the insertion of implicit opcodes into interpretation of the
909 * If @p opcode is rewritten, it should be returned from
910 * bhnd_sprom_opcode_step() instead of the opcode parsed from @p state's opcode
913 * If @p opcode remains unmodified, then bhnd_sprom_opcode_step() should
914 * proceed to standard evaluation.
917 bhnd_sprom_opcode_rewrite_opcode(bhnd_sprom_opcode_state *state,
923 op = SPROM_OPCODE_OP(*opcode);
924 switch (state->var_state) {
925 case SPROM_OPCODE_VAR_STATE_NONE:
926 /* No open variable definition */
929 case SPROM_OPCODE_VAR_STATE_OPEN:
930 /* Open variable definition; check for implicit closure. */
933 * If a variable definition contains no explicit bind
934 * instructions prior to closure, we must generate a DO_BIND
935 * instruction with count and skip values of 1.
937 if (SPROM_OP_IS_VAR_END(op) &&
938 state->var.bind_total == 0)
940 uint8_t count, skip_in, skip_out;
941 bool skip_in_negative;
943 /* Create bind with skip_in/skip_out of 1, count of 1 */
947 skip_in_negative = false;
949 error = bhnd_sprom_opcode_set_bind(state, count,
950 skip_in, skip_in_negative, skip_out);
955 *opcode = SPROM_OPCODE_DO_BIND |
956 (0 << SPROM_OP_BIND_SKIP_IN_SIGN) |
957 (1 << SPROM_OP_BIND_SKIP_IN_SHIFT) |
958 (1 << SPROM_OP_BIND_SKIP_OUT_SHIFT);
964 * If a variable is implicitly closed (e.g. by a new variable
965 * definition), we must generate a VAR_END instruction.
967 if (SPROM_OP_IS_IMPLICIT_VAR_END(op)) {
968 /* Mark as complete */
969 if ((error = bhnd_sprom_opcode_end_var(state)))
973 *opcode = SPROM_OPCODE_VAR_END;
979 case SPROM_OPCODE_VAR_STATE_DONE:
980 /* Previously completed variable definition. Discard variable
982 return (bhnd_sprom_opcode_clear_var(state));
990 * Evaluate one opcode from @p state.
992 * @param state The opcode state to be evaluated.
993 * @param[out] opcode On success, the evaluated opcode
996 * @retval ENOENT if EOF is reached
997 * @retval non-zero if evaluation otherwise fails, a regular unix error
998 * code will be returned.
1001 bhnd_sprom_opcode_step(bhnd_sprom_opcode_state *state, uint8_t *opcode)
1005 while (*state->input != SPROM_OPCODE_EOF) {
1007 uint8_t op, rewrite, immd;
1010 *opcode = *state->input;
1011 op = SPROM_OPCODE_OP(*opcode);
1012 immd = SPROM_OPCODE_IMM(*opcode);
1014 /* Clear any existing bind state */
1015 if ((error = bhnd_sprom_opcode_flush_bind(state)))
1018 /* Insert local opcode based on current state? */
1020 if ((error = bhnd_sprom_opcode_rewrite_opcode(state, &rewrite)))
1023 if (rewrite != *opcode) {
1024 /* Provide rewritten opcode */
1027 /* We must keep evaluating until we hit a state
1028 * applicable to the SPROM revision we're parsing */
1029 if (!bhnd_sprom_opcode_matches_layout_rev(state))
1039 case SPROM_OPCODE_VAR_IMM:
1040 if ((error = bhnd_sprom_opcode_set_var(state, immd)))
1044 case SPROM_OPCODE_VAR_REL_IMM:
1045 error = bhnd_sprom_opcode_set_var(state,
1051 case SPROM_OPCODE_VAR:
1052 error = bhnd_sprom_opcode_read_opval32(state, immd,
1057 if ((error = bhnd_sprom_opcode_set_var(state, val)))
1062 case SPROM_OPCODE_VAR_END:
1063 if ((error = bhnd_sprom_opcode_end_var(state)))
1067 case SPROM_OPCODE_NELEM:
1068 immd = *state->input;
1069 if ((error = bhnd_sprom_opcode_set_nelem(state, immd)))
1075 case SPROM_OPCODE_DO_BIND:
1076 case SPROM_OPCODE_DO_BINDN: {
1077 uint8_t count, skip_in, skip_out;
1078 bool skip_in_negative;
1080 /* Fetch skip arguments */
1081 skip_in = (immd & SPROM_OP_BIND_SKIP_IN_MASK) >>
1082 SPROM_OP_BIND_SKIP_IN_SHIFT;
1085 ((immd & SPROM_OP_BIND_SKIP_IN_SIGN) != 0);
1087 skip_out = (immd & SPROM_OP_BIND_SKIP_OUT_MASK) >>
1088 SPROM_OP_BIND_SKIP_OUT_SHIFT;
1090 /* Fetch count argument (if any) */
1091 if (op == SPROM_OPCODE_DO_BINDN) {
1092 /* Count is provided as trailing U8 */
1093 count = *state->input;
1099 /* Set BIND state */
1100 error = bhnd_sprom_opcode_set_bind(state, count,
1101 skip_in, skip_in_negative, skip_out);
1107 case SPROM_OPCODE_DO_BINDN_IMM: {
1108 uint8_t count, skip_in, skip_out;
1109 bool skip_in_negative;
1111 /* Implicit skip_in/skip_out of 1, count encoded as immd
1116 skip_in_negative = false;
1118 error = bhnd_sprom_opcode_set_bind(state, count,
1119 skip_in, skip_in_negative, skip_out);
1125 case SPROM_OPCODE_REV_IMM:
1126 error = bhnd_sprom_opcode_set_revs(state, immd, immd);
1131 case SPROM_OPCODE_REV_RANGE: {
1133 uint8_t rstart, rend;
1135 /* Revision range is encoded in next byte, as
1136 * { uint8_t start:4, uint8_t end:4 } */
1137 range = *state->input;
1138 rstart = (range & SPROM_OP_REV_START_MASK) >>
1139 SPROM_OP_REV_START_SHIFT;
1140 rend = (range & SPROM_OP_REV_END_MASK) >>
1141 SPROM_OP_REV_END_SHIFT;
1143 /* Update revision bitmask */
1144 error = bhnd_sprom_opcode_set_revs(state, rstart, rend);
1152 case SPROM_OPCODE_MASK_IMM:
1153 if ((error = bhnd_sprom_opcode_set_mask(state, immd)))
1157 case SPROM_OPCODE_MASK:
1158 error = bhnd_sprom_opcode_read_opval32(state, immd,
1163 if ((error = bhnd_sprom_opcode_set_mask(state, val)))
1167 case SPROM_OPCODE_SHIFT_IMM:
1168 error = bhnd_sprom_opcode_set_shift(state, immd * 2);
1173 case SPROM_OPCODE_SHIFT: {
1176 if (immd == SPROM_OP_DATA_I8) {
1177 shift = (int8_t)(*state->input);
1178 } else if (immd == SPROM_OP_DATA_U8) {
1179 val = *state->input;
1180 if (val > INT8_MAX) {
1181 SPROM_OP_BAD(state, "invalid shift "
1182 "value: %#x\n", val);
1187 SPROM_OP_BAD(state, "unsupported shift data "
1188 "type: %#hhx\n", immd);
1192 if ((error = bhnd_sprom_opcode_set_shift(state, shift)))
1198 case SPROM_OPCODE_OFFSET_REL_IMM:
1199 /* Fetch unscaled relative offset */
1203 error = bhnd_sprom_opcode_apply_scale(state, &val);
1207 /* Adding val must not overflow our offset */
1208 if (UINT32_MAX - state->offset < val) {
1209 BHND_NV_LOG("offset out of range\n");
1214 state->offset += val;
1216 case SPROM_OPCODE_OFFSET:
1217 error = bhnd_sprom_opcode_read_opval32(state, immd,
1222 state->offset = val;
1225 case SPROM_OPCODE_TYPE:
1226 /* Type follows as U8 */
1227 immd = *state->input;
1231 case SPROM_OPCODE_TYPE_IMM:
1233 case BHND_NVRAM_TYPE_UINT8:
1234 case BHND_NVRAM_TYPE_UINT16:
1235 case BHND_NVRAM_TYPE_UINT32:
1236 case BHND_NVRAM_TYPE_UINT64:
1237 case BHND_NVRAM_TYPE_INT8:
1238 case BHND_NVRAM_TYPE_INT16:
1239 case BHND_NVRAM_TYPE_INT32:
1240 case BHND_NVRAM_TYPE_INT64:
1241 case BHND_NVRAM_TYPE_CHAR:
1242 case BHND_NVRAM_TYPE_STRING:
1243 error = bhnd_sprom_opcode_set_type(state,
1244 (bhnd_nvram_type)immd);
1249 BHND_NV_LOG("unrecognized type %#hhx\n", immd);
1255 BHND_NV_LOG("unrecognized opcode %#hhx\n", *opcode);
1259 /* We must keep evaluating until we hit a state applicable to
1260 * the SPROM revision we're parsing */
1261 if (bhnd_sprom_opcode_matches_layout_rev(state))
1265 /* End of opcode stream */
1270 * Reset SPROM opcode evaluation state, seek to the @p entry's position,
1271 * and perform complete evaluation of the variable's opcodes.
1273 * @param state The opcode state to be to be evaluated.
1274 * @param entry The indexed variable location.
1277 * @retval non-zero If evaluation fails, a regular unix error code will be
1281 bhnd_sprom_opcode_eval_var(bhnd_sprom_opcode_state *state,
1282 bhnd_sprom_opcode_idx_entry *entry)
1288 if ((error = bhnd_sprom_opcode_seek(state, entry)))
1291 /* Parse full variable definition */
1292 while ((error = bhnd_sprom_opcode_step(state, &opcode)) == 0) {
1293 /* Iterate until VAR_END */
1294 if (SPROM_OPCODE_OP(opcode) != SPROM_OPCODE_VAR_END)
1297 BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
1298 ("incomplete variable definition"));
1303 /* Error parsing definition */
1308 * Evaluate @p state until the next variable definition is found.
1310 * @param state The opcode state to be evaluated.
1313 * @retval ENOENT if no additional variable definitions are available.
1314 * @retval non-zero if evaluation otherwise fails, a regular unix error
1315 * code will be returned.
1318 bhnd_sprom_opcode_next_var(bhnd_sprom_opcode_state *state)
1323 /* Step until we hit a variable opcode */
1324 while ((error = bhnd_sprom_opcode_step(state, &opcode)) == 0) {
1325 switch (SPROM_OPCODE_OP(opcode)) {
1326 case SPROM_OPCODE_VAR:
1327 case SPROM_OPCODE_VAR_IMM:
1328 case SPROM_OPCODE_VAR_REL_IMM:
1330 state->var_state == SPROM_OPCODE_VAR_STATE_OPEN,
1331 ("missing variable definition"));
1339 /* Reached EOF, or evaluation failed */
1344 * Evaluate @p state until the next binding for the current variable definition
1347 * @param state The opcode state to be evaluated.
1350 * @retval ENOENT if no additional binding opcodes are found prior to reaching
1351 * a new variable definition, or the end of @p state's binding opcodes.
1352 * @retval non-zero if evaluation otherwise fails, a regular unix error
1353 * code will be returned.
1356 bhnd_sprom_opcode_next_binding(bhnd_sprom_opcode_state *state)
1361 if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN)
1364 /* Step until we hit a bind opcode, or a new variable */
1365 while ((error = bhnd_sprom_opcode_step(state, &opcode)) == 0) {
1366 switch (SPROM_OPCODE_OP(opcode)) {
1367 case SPROM_OPCODE_DO_BIND:
1368 case SPROM_OPCODE_DO_BINDN:
1369 case SPROM_OPCODE_DO_BINDN_IMM:
1370 /* Found next bind */
1372 state->var_state == SPROM_OPCODE_VAR_STATE_OPEN,
1373 ("missing variable definition"));
1374 BHND_NV_ASSERT(state->var.have_bind, ("missing bind"));
1378 case SPROM_OPCODE_VAR_END:
1379 /* No further binding opcodes */
1381 state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
1382 ("variable definition still available"));
1387 /* Not found, or evaluation failed */