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