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