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.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/cdefs.h>
31
#include <sys/endian.h>
32
33
#ifdef _KERNEL
34
#include <sys/param.h>
35
#include <sys/ctype.h>
36
#include <sys/malloc.h>
37
#include <sys/systm.h>
38
39
#include <machine/_inttypes.h>
40
#else /* !_KERNEL */
41
#include <ctype.h>
42
#include <errno.h>
43
#include <inttypes.h>
44
#include <stdint.h>
45
#include <stdio.h>
46
#include <stdlib.h>
47
#include <string.h>
48
#endif /* _KERNEL */
49
50
#include "bhnd_nvram_map.h"
51
52
#include "bhnd_nvram_private.h"
53
#include "bhnd_nvram_datavar.h"
54
55
#include "bhnd_nvram_data_spromvar.h"
56
57
/*
58
* BHND SPROM NVRAM data class
59
*
60
* The SPROM data format is a fixed-layout, non-self-descriptive binary format,
61
* used on Broadcom wireless and wired adapters, that provides a subset of the
62
* variables defined by Broadcom SoC NVRAM formats.
63
*/
64
65
static const bhnd_sprom_layout *bhnd_nvram_sprom_get_layout(uint8_t sromrev);
66
67
static int bhnd_nvram_sprom_ident(
68
struct bhnd_nvram_io *io,
69
const bhnd_sprom_layout **ident);
70
71
static int bhnd_nvram_sprom_write_var(
72
bhnd_sprom_opcode_state *state,
73
bhnd_sprom_opcode_idx_entry *entry,
74
bhnd_nvram_val *value,
75
struct bhnd_nvram_io *io);
76
77
static int bhnd_nvram_sprom_read_var(
78
struct bhnd_sprom_opcode_state *state,
79
struct bhnd_sprom_opcode_idx_entry *entry,
80
struct bhnd_nvram_io *io,
81
union bhnd_nvram_sprom_storage *storage,
82
bhnd_nvram_val *val);
83
84
static int bhnd_nvram_sprom_write_offset(
85
const struct bhnd_nvram_vardefn *var,
86
struct bhnd_nvram_io *data,
87
bhnd_nvram_type type, size_t offset,
88
uint32_t mask, int8_t shift,
89
uint32_t value);
90
91
static int bhnd_nvram_sprom_read_offset(
92
const struct bhnd_nvram_vardefn *var,
93
struct bhnd_nvram_io *data,
94
bhnd_nvram_type type, size_t offset,
95
uint32_t mask, int8_t shift,
96
uint32_t *value);
97
98
static bool bhnd_sprom_is_external_immutable(
99
const char *name);
100
101
BHND_NVRAM_DATA_CLASS_DEFN(sprom, "Broadcom SPROM",
102
BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_sprom))
103
104
#define SPROM_COOKIE_TO_VID(_cookie) \
105
(((struct bhnd_sprom_opcode_idx_entry *)(_cookie))->vid)
106
107
#define SPROM_COOKIE_TO_NVRAM_VAR(_cookie) \
108
bhnd_nvram_get_vardefn(SPROM_COOKIE_TO_VID(_cookie))
109
110
/**
111
* Read the magic value from @p io, and verify that it matches
112
* the @p layout's expected magic value.
113
*
114
* If @p layout does not defined a magic value, @p magic is set to 0x0
115
* and success is returned.
116
*
117
* @param io An I/O context mapping the SPROM data to be identified.
118
* @param layout The SPROM layout against which @p io should be verified.
119
* @param[out] magic On success, the SPROM magic value.
120
*
121
* @retval 0 success
122
* @retval non-zero If checking @p io otherwise fails, a regular unix
123
* error code will be returned.
124
*/
125
static int
126
bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io *io,
127
const bhnd_sprom_layout *layout, uint16_t *magic)
128
{
129
int error;
130
131
/* Skip if layout does not define a magic value */
132
if (layout->flags & SPROM_LAYOUT_MAGIC_NONE)
133
return (0);
134
135
/* Read the magic value */
136
error = bhnd_nvram_io_read(io, layout->magic_offset, magic,
137
sizeof(*magic));
138
if (error)
139
return (error);
140
141
*magic = le16toh(*magic);
142
143
/* If the signature does not match, skip to next layout */
144
if (*magic != layout->magic_value)
145
return (ENXIO);
146
147
return (0);
148
}
149
150
/**
151
* Attempt to identify the format of the SPROM data mapped by @p io.
152
*
153
* The SPROM data format does not provide any identifying information at a
154
* known offset, instead requiring that we iterate over the known SPROM image
155
* sizes until we are able to compute a valid checksum (and, for later
156
* revisions, validate a signature at a revision-specific offset).
157
*
158
* @param io An I/O context mapping the SPROM data to be identified.
159
* @param[out] ident On success, the identified SPROM layout.
160
*
161
* @retval 0 success
162
* @retval non-zero If identifying @p io otherwise fails, a regular unix
163
* error code will be returned.
164
*/
165
static int
166
bhnd_nvram_sprom_ident(struct bhnd_nvram_io *io,
167
const bhnd_sprom_layout **ident)
168
{
169
uint8_t crc;
170
size_t crc_errors;
171
size_t nbytes;
172
int error;
173
174
crc = BHND_NVRAM_CRC8_INITIAL;
175
crc_errors = 0;
176
nbytes = 0;
177
178
/* We iterate the SPROM layouts smallest to largest, allowing us to
179
* perform incremental checksum calculation */
180
for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
181
const bhnd_sprom_layout *layout;
182
u_char buf[512];
183
size_t nread;
184
uint16_t magic;
185
uint8_t srevcrc[2];
186
uint8_t srev;
187
bool crc_valid;
188
bool have_magic;
189
190
layout = &bhnd_sprom_layouts[i];
191
crc_valid = true;
192
193
have_magic = true;
194
if ((layout->flags & SPROM_LAYOUT_MAGIC_NONE))
195
have_magic = false;
196
197
/*
198
* Read image data and update CRC (errors are reported
199
* after the signature check)
200
*
201
* Layout instances must be ordered from smallest to largest by
202
* the nvram_map compiler, allowing us to incrementally update
203
* our CRC.
204
*/
205
if (nbytes > layout->size)
206
BHND_NV_PANIC("SPROM layout defined out-of-order");
207
208
nread = layout->size - nbytes;
209
210
while (nread > 0) {
211
size_t nr;
212
213
nr = bhnd_nv_ummin(nread, sizeof(buf));
214
215
if ((error = bhnd_nvram_io_read(io, nbytes, buf, nr)))
216
return (error);
217
218
crc = bhnd_nvram_crc8(buf, nr, crc);
219
crc_valid = (crc == BHND_NVRAM_CRC8_VALID);
220
if (!crc_valid)
221
crc_errors++;
222
223
nread -= nr;
224
nbytes += nr;
225
}
226
227
/* Read 8-bit SPROM revision, maintaining 16-bit size alignment
228
* required by some OTP/SPROM chipsets. */
229
error = bhnd_nvram_io_read(io, layout->srev_offset, &srevcrc,
230
sizeof(srevcrc));
231
if (error)
232
return (error);
233
234
srev = srevcrc[0];
235
236
/* Early sromrev 1 devices (specifically some BCM440x enet
237
* cards) are reported to have been incorrectly programmed
238
* with a revision of 0x10. */
239
if (layout->rev == 1 && srev == 0x10)
240
srev = 0x1;
241
242
/* Check revision against the layout definition */
243
if (srev != layout->rev)
244
continue;
245
246
/* Check the magic value, skipping to the next layout on
247
* failure. */
248
error = bhnd_nvram_sprom_check_magic(io, layout, &magic);
249
if (error) {
250
/* If the CRC is was valid, log the mismatch */
251
if (crc_valid || BHND_NV_VERBOSE) {
252
BHND_NV_LOG("invalid sprom %hhu signature: "
253
"0x%hx (expected 0x%hx)\n", srev,
254
magic, layout->magic_value);
255
256
return (ENXIO);
257
}
258
259
continue;
260
}
261
262
/* Check for an earlier CRC error */
263
if (!crc_valid) {
264
/* If the magic check succeeded, then we may just have
265
* data corruption -- log the CRC error */
266
if (have_magic || BHND_NV_VERBOSE) {
267
BHND_NV_LOG("sprom %hhu CRC error (crc=%#hhx, "
268
"expected=%#x)\n", srev, crc,
269
BHND_NVRAM_CRC8_VALID);
270
}
271
272
continue;
273
}
274
275
/* Identified */
276
*ident = layout;
277
return (0);
278
}
279
280
/* No match */
281
if (crc_errors > 0 && BHND_NV_VERBOSE) {
282
BHND_NV_LOG("sprom parsing failed with %zu CRC errors\n",
283
crc_errors);
284
}
285
286
return (ENXIO);
287
}
288
289
static int
290
bhnd_nvram_sprom_probe(struct bhnd_nvram_io *io)
291
{
292
const bhnd_sprom_layout *layout;
293
int error;
294
295
/* Try to parse the input */
296
if ((error = bhnd_nvram_sprom_ident(io, &layout)))
297
return (error);
298
299
return (BHND_NVRAM_DATA_PROBE_DEFAULT);
300
}
301
302
static int
303
bhnd_nvram_sprom_getvar_direct(struct bhnd_nvram_io *io, const char *name,
304
void *buf, size_t *len, bhnd_nvram_type type)
305
{
306
const bhnd_sprom_layout *layout;
307
bhnd_sprom_opcode_state state;
308
const struct bhnd_nvram_vardefn *var;
309
size_t vid;
310
int error;
311
312
/* Look up the variable definition and ID */
313
if ((var = bhnd_nvram_find_vardefn(name)) == NULL)
314
return (ENOENT);
315
316
vid = bhnd_nvram_get_vardefn_id(var);
317
318
/* Identify the SPROM image layout */
319
if ((error = bhnd_nvram_sprom_ident(io, &layout)))
320
return (error);
321
322
/* Initialize SPROM layout interpreter */
323
if ((error = bhnd_sprom_opcode_init(&state, layout))) {
324
BHND_NV_LOG("error initializing opcode state: %d\n", error);
325
return (ENXIO);
326
}
327
328
/* Find SPROM layout entry for the requested variable */
329
while ((error = bhnd_sprom_opcode_next_var(&state)) == 0) {
330
bhnd_sprom_opcode_idx_entry entry;
331
union bhnd_nvram_sprom_storage storage;
332
bhnd_nvram_val val;
333
334
/* Fetch the variable's entry state */
335
if ((error = bhnd_sprom_opcode_init_entry(&state, &entry)))
336
return (error);
337
338
/* Match against expected VID */
339
if (entry.vid != vid)
340
continue;
341
342
/* Decode variable to a new value instance */
343
error = bhnd_nvram_sprom_read_var(&state, &entry, io, &storage,
344
&val);
345
if (error)
346
return (error);
347
348
/* Perform value coercion */
349
error = bhnd_nvram_val_encode(&val, buf, len, type);
350
351
/* Clean up */
352
bhnd_nvram_val_release(&val);
353
return (error);
354
}
355
356
/* Hit EOF without matching the requested variable? */
357
if (error == ENOENT)
358
return (ENOENT);
359
360
/* Some other parse error occurred */
361
return (error);
362
}
363
364
/**
365
* Return the SPROM layout definition for the given @p sromrev, or NULL if
366
* not found.
367
*/
368
static const bhnd_sprom_layout *
369
bhnd_nvram_sprom_get_layout(uint8_t sromrev)
370
{
371
/* Find matching SPROM layout definition */
372
for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
373
if (bhnd_sprom_layouts[i].rev == sromrev)
374
return (&bhnd_sprom_layouts[i]);
375
}
376
377
/* Not found */
378
return (NULL);
379
}
380
381
/**
382
* Serialize a SPROM variable.
383
*
384
* @param state The SPROM opcode state describing the layout of @p io.
385
* @param entry The variable's SPROM opcode index entry.
386
* @param value The value to encode to @p io as per @p entry.
387
* @param io I/O context to which @p value should be written, or NULL
388
* if no output should be produced. This may be used to validate
389
* values prior to write.
390
*
391
* @retval 0 success
392
* @retval EFTYPE If value coercion from @p value to the type required by
393
* @p entry is unsupported.
394
* @retval ERANGE If value coercion from @p value would overflow
395
* (or underflow) the type required by @p entry.
396
* @retval non-zero If serialization otherwise fails, a regular unix error
397
* code will be returned.
398
*/
399
static int
400
bhnd_nvram_sprom_write_var(bhnd_sprom_opcode_state *state,
401
bhnd_sprom_opcode_idx_entry *entry, bhnd_nvram_val *value,
402
struct bhnd_nvram_io *io)
403
{
404
const struct bhnd_nvram_vardefn *var;
405
uint32_t u32[BHND_SPROM_ARRAY_MAXLEN];
406
bhnd_nvram_type itype, var_base_type;
407
size_t ipos, ilen, nelem;
408
int error;
409
410
/* Fetch variable definition and the native element type */
411
var = bhnd_nvram_get_vardefn(entry->vid);
412
BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
413
414
var_base_type = bhnd_nvram_base_type(var->type);
415
416
/* Fetch the element count from the SPROM variable layout definition */
417
if ((error = bhnd_sprom_opcode_eval_var(state, entry)))
418
return (error);
419
420
nelem = state->var.nelem;
421
BHND_NV_ASSERT(nelem <= var->nelem, ("SPROM nelem=%zu exceeds maximum "
422
"NVRAM nelem=%hhu", nelem, var->nelem));
423
424
/* Promote the data to a common 32-bit representation */
425
if (bhnd_nvram_is_signed_type(var_base_type))
426
itype = BHND_NVRAM_TYPE_INT32_ARRAY;
427
else
428
itype = BHND_NVRAM_TYPE_UINT32_ARRAY;
429
430
/* Calculate total size of the 32-bit promoted representation */
431
if ((ilen = bhnd_nvram_value_size(NULL, 0, itype, nelem)) == 0) {
432
/* Variable-width types are unsupported */
433
BHND_NV_LOG("invalid %s SPROM variable type %d\n",
434
var->name, var->type);
435
return (EFTYPE);
436
}
437
438
/* The native representation must fit within our scratch array */
439
if (ilen > sizeof(u32)) {
440
BHND_NV_LOG("error encoding '%s', SPROM_ARRAY_MAXLEN "
441
"incorrect\n", var->name);
442
return (EFTYPE);
443
}
444
445
/* Initialize our common 32-bit value representation */
446
if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) {
447
/* No value provided; can this variable be encoded as missing
448
* by setting all bits to one? */
449
if (!(var->flags & BHND_NVRAM_VF_IGNALL1)) {
450
BHND_NV_LOG("missing required property: %s\n",
451
var->name);
452
return (EINVAL);
453
}
454
455
/* Set all bits */
456
memset(u32, 0xFF, ilen);
457
} else {
458
bhnd_nvram_val bcm_val;
459
const void *var_ptr;
460
bhnd_nvram_type var_type, raw_type;
461
size_t var_len, enc_nelem;
462
463
/* Try to coerce the value to the native variable format. */
464
error = bhnd_nvram_val_convert_init(&bcm_val, var->fmt, value,
465
BHND_NVRAM_VAL_DYNAMIC|BHND_NVRAM_VAL_BORROW_DATA);
466
if (error) {
467
BHND_NV_LOG("error converting input type %s to %s "
468
"format\n",
469
bhnd_nvram_type_name(bhnd_nvram_val_type(value)),
470
bhnd_nvram_val_fmt_name(var->fmt));
471
return (error);
472
}
473
474
var_ptr = bhnd_nvram_val_bytes(&bcm_val, &var_len, &var_type);
475
476
/*
477
* Promote to a common 32-bit representation.
478
*
479
* We must use the raw type to interpret the input data as its
480
* underlying integer representation -- otherwise, coercion
481
* would attempt to parse the input as its complex
482
* representation.
483
*
484
* For example, direct CHAR -> UINT32 coercion would attempt to
485
* parse the character as a decimal integer, rather than
486
* promoting the raw UTF8 byte value to a 32-bit value.
487
*/
488
raw_type = bhnd_nvram_raw_type(var_type);
489
error = bhnd_nvram_value_coerce(var_ptr, var_len, raw_type,
490
u32, &ilen, itype);
491
492
/* Clean up temporary value representation */
493
bhnd_nvram_val_release(&bcm_val);
494
495
/* Report coercion failure */
496
if (error) {
497
BHND_NV_LOG("error promoting %s to %s: %d\n",
498
bhnd_nvram_type_name(var_type),
499
bhnd_nvram_type_name(itype), error);
500
return (error);
501
}
502
503
/* Encoded element count must match SPROM's definition */
504
error = bhnd_nvram_value_nelem(u32, ilen, itype, &enc_nelem);
505
if (error)
506
return (error);
507
508
if (enc_nelem != nelem) {
509
const char *type_name;
510
511
type_name = bhnd_nvram_type_name(var_base_type);
512
BHND_NV_LOG("invalid %s property value '%s[%zu]': "
513
"required %s[%zu]", var->name, type_name,
514
enc_nelem, type_name, nelem);
515
return (EFTYPE);
516
}
517
}
518
519
/*
520
* Seek to the start of the variable's SPROM layout definition and
521
* iterate over all bindings.
522
*/
523
if ((error = bhnd_sprom_opcode_seek(state, entry))) {
524
BHND_NV_LOG("variable seek failed: %d\n", error);
525
return (error);
526
}
527
528
ipos = 0;
529
while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) {
530
bhnd_sprom_opcode_bind *binding;
531
bhnd_sprom_opcode_var *binding_var;
532
size_t offset;
533
uint32_t skip_out_bytes;
534
535
BHND_NV_ASSERT(
536
state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
537
("invalid var state"));
538
BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state"));
539
540
binding_var = &state->var;
541
binding = &state->var.bind;
542
543
/* Calculate output skip bytes for this binding.
544
*
545
* Skip directions are defined in terms of decoding, and
546
* reversed when encoding. */
547
skip_out_bytes = binding->skip_in;
548
error = bhnd_sprom_opcode_apply_scale(state, &skip_out_bytes);
549
if (error)
550
return (error);
551
552
/* Bind */
553
offset = state->offset;
554
for (size_t i = 0; i < binding->count; i++) {
555
if (ipos >= nelem) {
556
BHND_NV_LOG("input skip %u positioned %zu "
557
"beyond nelem %zu\n", binding->skip_out,
558
ipos, nelem);
559
return (EINVAL);
560
}
561
562
/* Write next offset */
563
if (io != NULL) {
564
error = bhnd_nvram_sprom_write_offset(var, io,
565
binding_var->base_type,
566
offset,
567
binding_var->mask,
568
binding_var->shift,
569
u32[ipos]);
570
if (error)
571
return (error);
572
}
573
574
/* Adjust output position; this was already verified to
575
* not overflow/underflow during SPROM opcode
576
* evaluation */
577
if (binding->skip_in_negative) {
578
offset -= skip_out_bytes;
579
} else {
580
offset += skip_out_bytes;
581
}
582
583
/* Skip advancing input if additional bindings are
584
* required to fully encode intv */
585
if (binding->skip_out == 0)
586
continue;
587
588
/* Advance input position */
589
if (SIZE_MAX - binding->skip_out < ipos) {
590
BHND_NV_LOG("output skip %u would overflow "
591
"%zu\n", binding->skip_out, ipos);
592
return (EINVAL);
593
}
594
595
ipos += binding->skip_out;
596
}
597
}
598
599
/* Did we iterate all bindings until hitting end of the variable
600
* definition? */
601
BHND_NV_ASSERT(error != 0, ("loop terminated early"));
602
if (error != ENOENT)
603
return (error);
604
605
return (0);
606
}
607
608
static int
609
bhnd_nvram_sprom_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
610
bhnd_nvram_plist *options, void *outp, size_t *olen)
611
{
612
bhnd_sprom_opcode_state state;
613
struct bhnd_nvram_io *io;
614
bhnd_nvram_prop *prop;
615
bhnd_sprom_opcode_idx_entry *entry;
616
const bhnd_sprom_layout *layout;
617
size_t limit;
618
uint8_t crc;
619
uint8_t sromrev;
620
int error;
621
622
limit = *olen;
623
layout = NULL;
624
io = NULL;
625
626
/* Fetch sromrev property */
627
if (!bhnd_nvram_plist_contains(props, BHND_NVAR_SROMREV)) {
628
BHND_NV_LOG("missing required property: %s\n",
629
BHND_NVAR_SROMREV);
630
return (EINVAL);
631
}
632
633
error = bhnd_nvram_plist_get_uint8(props, BHND_NVAR_SROMREV, &sromrev);
634
if (error) {
635
BHND_NV_LOG("error reading sromrev property: %d\n", error);
636
return (EFTYPE);
637
}
638
639
/* Find SPROM layout definition */
640
if ((layout = bhnd_nvram_sprom_get_layout(sromrev)) == NULL) {
641
BHND_NV_LOG("unsupported sromrev: %hhu\n", sromrev);
642
return (EFTYPE);
643
}
644
645
/* Provide required size to caller */
646
*olen = layout->size;
647
if (outp == NULL)
648
return (0);
649
else if (limit < *olen)
650
return (ENOMEM);
651
652
/* Initialize SPROM layout interpreter */
653
if ((error = bhnd_sprom_opcode_init(&state, layout))) {
654
BHND_NV_LOG("error initializing opcode state: %d\n", error);
655
return (ENXIO);
656
}
657
658
/* Check for unsupported properties */
659
prop = NULL;
660
while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
661
const char *name;
662
663
/* Fetch the corresponding SPROM layout index entry */
664
name = bhnd_nvram_prop_name(prop);
665
entry = bhnd_sprom_opcode_index_find(&state, name);
666
if (entry == NULL) {
667
BHND_NV_LOG("property '%s' unsupported by sromrev "
668
"%hhu\n", name, layout->rev);
669
error = EINVAL;
670
goto finished;
671
}
672
}
673
674
/* Zero-initialize output */
675
memset(outp, 0, *olen);
676
677
/* Allocate wrapping I/O context for output buffer */
678
io = bhnd_nvram_ioptr_new(outp, *olen, *olen, BHND_NVRAM_IOPTR_RDWR);
679
if (io == NULL) {
680
error = ENOMEM;
681
goto finished;
682
}
683
684
/*
685
* Serialize all SPROM variable data.
686
*/
687
entry = NULL;
688
while ((entry = bhnd_sprom_opcode_index_next(&state, entry)) != NULL) {
689
const struct bhnd_nvram_vardefn *var;
690
bhnd_nvram_val *val;
691
692
var = bhnd_nvram_get_vardefn(entry->vid);
693
BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
694
695
/* Fetch prop; will be NULL if unavailable */
696
prop = bhnd_nvram_plist_get_prop(props, var->name);
697
if (prop != NULL) {
698
val = bhnd_nvram_prop_val(prop);
699
} else {
700
val = BHND_NVRAM_VAL_NULL;
701
}
702
703
/* Attempt to serialize the property value to the appropriate
704
* offset within the output buffer */
705
error = bhnd_nvram_sprom_write_var(&state, entry, val, io);
706
if (error) {
707
BHND_NV_LOG("error serializing %s to required type "
708
"%s: %d\n", var->name,
709
bhnd_nvram_type_name(var->type), error);
710
711
/* ENOMEM is reserved for signaling that the output
712
* buffer capacity is insufficient */
713
if (error == ENOMEM)
714
error = EINVAL;
715
716
goto finished;
717
}
718
}
719
720
/*
721
* Write magic value, if any.
722
*/
723
if (!(layout->flags & SPROM_LAYOUT_MAGIC_NONE)) {
724
uint16_t magic;
725
726
magic = htole16(layout->magic_value);
727
error = bhnd_nvram_io_write(io, layout->magic_offset, &magic,
728
sizeof(magic));
729
if (error) {
730
BHND_NV_LOG("error writing magic value: %d\n", error);
731
goto finished;
732
}
733
}
734
735
/* Calculate the CRC over all SPROM data, not including the CRC byte. */
736
crc = ~bhnd_nvram_crc8(outp, layout->crc_offset,
737
BHND_NVRAM_CRC8_INITIAL);
738
739
/* Write the checksum. */
740
error = bhnd_nvram_io_write(io, layout->crc_offset, &crc, sizeof(crc));
741
if (error) {
742
BHND_NV_LOG("error writing CRC value: %d\n", error);
743
goto finished;
744
}
745
746
/*
747
* Success!
748
*/
749
error = 0;
750
751
finished:
752
bhnd_sprom_opcode_fini(&state);
753
754
if (io != NULL)
755
bhnd_nvram_io_free(io);
756
757
return (error);
758
}
759
760
static int
761
bhnd_nvram_sprom_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
762
{
763
struct bhnd_nvram_sprom *sp;
764
int error;
765
766
sp = (struct bhnd_nvram_sprom *)nv;
767
768
/* Identify the SPROM input data */
769
if ((error = bhnd_nvram_sprom_ident(io, &sp->layout)))
770
return (error);
771
772
/* Copy SPROM image to our shadow buffer */
773
sp->data = bhnd_nvram_iobuf_copy_range(io, 0, sp->layout->size);
774
if (sp->data == NULL)
775
goto failed;
776
777
/* Initialize SPROM binding eval state */
778
if ((error = bhnd_sprom_opcode_init(&sp->state, sp->layout)))
779
goto failed;
780
781
return (0);
782
783
failed:
784
if (sp->data != NULL)
785
bhnd_nvram_io_free(sp->data);
786
787
return (error);
788
}
789
790
static void
791
bhnd_nvram_sprom_free(struct bhnd_nvram_data *nv)
792
{
793
struct bhnd_nvram_sprom *sp = (struct bhnd_nvram_sprom *)nv;
794
795
bhnd_sprom_opcode_fini(&sp->state);
796
bhnd_nvram_io_free(sp->data);
797
}
798
799
size_t
800
bhnd_nvram_sprom_count(struct bhnd_nvram_data *nv)
801
{
802
struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv;
803
return (sprom->layout->num_vars);
804
}
805
806
static bhnd_nvram_plist *
807
bhnd_nvram_sprom_options(struct bhnd_nvram_data *nv)
808
{
809
return (NULL);
810
}
811
812
static uint32_t
813
bhnd_nvram_sprom_caps(struct bhnd_nvram_data *nv)
814
{
815
return (BHND_NVRAM_DATA_CAP_INDEXED);
816
}
817
818
static const char *
819
bhnd_nvram_sprom_next(struct bhnd_nvram_data *nv, void **cookiep)
820
{
821
struct bhnd_nvram_sprom *sp;
822
bhnd_sprom_opcode_idx_entry *entry;
823
const struct bhnd_nvram_vardefn *var;
824
825
sp = (struct bhnd_nvram_sprom *)nv;
826
827
/* Find next index entry that is not disabled by virtue of IGNALL1 */
828
entry = *cookiep;
829
while ((entry = bhnd_sprom_opcode_index_next(&sp->state, entry))) {
830
/* Update cookiep and fetch variable definition */
831
*cookiep = entry;
832
var = SPROM_COOKIE_TO_NVRAM_VAR(*cookiep);
833
BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
834
835
/* We might need to parse the variable's value to determine
836
* whether it should be treated as unset */
837
if (var->flags & BHND_NVRAM_VF_IGNALL1) {
838
int error;
839
size_t len;
840
841
error = bhnd_nvram_sprom_getvar(nv, *cookiep, NULL,
842
&len, var->type);
843
if (error) {
844
BHND_NV_ASSERT(error == ENOENT, ("unexpected "
845
"error parsing variable: %d", error));
846
continue;
847
}
848
}
849
850
/* Found! */
851
return (var->name);
852
}
853
854
/* Reached end of index entries */
855
return (NULL);
856
}
857
858
static void *
859
bhnd_nvram_sprom_find(struct bhnd_nvram_data *nv, const char *name)
860
{
861
struct bhnd_nvram_sprom *sp;
862
bhnd_sprom_opcode_idx_entry *entry;
863
864
sp = (struct bhnd_nvram_sprom *)nv;
865
866
entry = bhnd_sprom_opcode_index_find(&sp->state, name);
867
return (entry);
868
}
869
870
/**
871
* Write @p value of @p type to the SPROM @p data at @p offset, applying
872
* @p mask and @p shift, and OR with the existing data.
873
*
874
* @param var The NVRAM variable definition.
875
* @param data The SPROM data to be modified.
876
* @param type The type to write at @p offset.
877
* @param offset The data offset to be written.
878
* @param mask The mask to be applied to @p value after shifting.
879
* @param shift The shift to be applied to @p value; if positive, a left
880
* shift will be applied, if negative, a right shift (this is the reverse of the
881
* decoding behavior)
882
* @param value The value to be written. The parsed value will be OR'd with the
883
* current contents of @p data at @p offset.
884
*/
885
static int
886
bhnd_nvram_sprom_write_offset(const struct bhnd_nvram_vardefn *var,
887
struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset,
888
uint32_t mask, int8_t shift, uint32_t value)
889
{
890
union bhnd_nvram_sprom_storage scratch;
891
int error;
892
893
#define NV_WRITE_INT(_widen, _repr, _swap) do { \
894
/* Narrow the 32-bit representation */ \
895
scratch._repr[1] = (_widen)value; \
896
\
897
/* Shift and mask the new value */ \
898
if (shift > 0) \
899
scratch._repr[1] <<= shift; \
900
else if (shift < 0) \
901
scratch._repr[1] >>= -shift; \
902
scratch._repr[1] &= mask; \
903
\
904
/* Swap to output byte order */ \
905
scratch._repr[1] = _swap(scratch._repr[1]); \
906
\
907
/* Fetch the current value */ \
908
error = bhnd_nvram_io_read(data, offset, \
909
&scratch._repr[0], sizeof(scratch._repr[0])); \
910
if (error) { \
911
BHND_NV_LOG("error reading %s SPROM offset " \
912
"%#zx: %d\n", var->name, offset, error); \
913
return (EFTYPE); \
914
} \
915
\
916
/* Mask and set our new value's bits in the current \
917
* value */ \
918
if (shift >= 0) \
919
scratch._repr[0] &= ~_swap(mask << shift); \
920
else if (shift < 0) \
921
scratch._repr[0] &= ~_swap(mask >> (-shift)); \
922
scratch._repr[0] |= scratch._repr[1]; \
923
\
924
/* Perform write */ \
925
error = bhnd_nvram_io_write(data, offset, \
926
&scratch._repr[0], sizeof(scratch._repr[0])); \
927
if (error) { \
928
BHND_NV_LOG("error writing %s SPROM offset " \
929
"%#zx: %d\n", var->name, offset, error); \
930
return (EFTYPE); \
931
} \
932
} while(0)
933
934
/* Apply mask/shift and widen to a common 32bit representation */
935
switch (type) {
936
case BHND_NVRAM_TYPE_UINT8:
937
NV_WRITE_INT(uint32_t, u8, );
938
break;
939
case BHND_NVRAM_TYPE_UINT16:
940
NV_WRITE_INT(uint32_t, u16, htole16);
941
break;
942
case BHND_NVRAM_TYPE_UINT32:
943
NV_WRITE_INT(uint32_t, u32, htole32);
944
break;
945
case BHND_NVRAM_TYPE_INT8:
946
NV_WRITE_INT(int32_t, i8, );
947
break;
948
case BHND_NVRAM_TYPE_INT16:
949
NV_WRITE_INT(int32_t, i16, htole16);
950
break;
951
case BHND_NVRAM_TYPE_INT32:
952
NV_WRITE_INT(int32_t, i32, htole32);
953
break;
954
case BHND_NVRAM_TYPE_CHAR:
955
NV_WRITE_INT(uint32_t, u8, );
956
break;
957
default:
958
BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
959
return (EFTYPE);
960
}
961
#undef NV_WRITE_INT
962
963
return (0);
964
}
965
966
/**
967
* Read the value of @p type from the SPROM @p data at @p offset, apply @p mask
968
* and @p shift, and OR with the existing @p value.
969
*
970
* @param var The NVRAM variable definition.
971
* @param data The SPROM data to be decoded.
972
* @param type The type to read at @p offset
973
* @param offset The data offset to be read.
974
* @param mask The mask to be applied to the value read at @p offset.
975
* @param shift The shift to be applied after masking; if positive, a right
976
* shift will be applied, if negative, a left shift.
977
* @param value The read destination; the parsed value will be OR'd with the
978
* current contents of @p value.
979
*/
980
static int
981
bhnd_nvram_sprom_read_offset(const struct bhnd_nvram_vardefn *var,
982
struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset,
983
uint32_t mask, int8_t shift, uint32_t *value)
984
{
985
union bhnd_nvram_sprom_storage scratch;
986
int error;
987
988
#define NV_PARSE_INT(_widen, _repr, _swap) do { \
989
/* Perform read */ \
990
error = bhnd_nvram_io_read(data, offset, \
991
&scratch._repr[0], sizeof(scratch._repr[0])); \
992
if (error) { \
993
BHND_NV_LOG("error reading %s SPROM offset " \
994
"%#zx: %d\n", var->name, offset, error); \
995
return (EFTYPE); \
996
} \
997
\
998
/* Swap to host byte order */ \
999
scratch._repr[0] = _swap(scratch._repr[0]); \
1000
\
1001
/* Mask and shift the value */ \
1002
scratch._repr[0] &= mask; \
1003
if (shift > 0) { \
1004
scratch. _repr[0] >>= shift; \
1005
} else if (shift < 0) { \
1006
scratch. _repr[0] <<= -shift; \
1007
} \
1008
\
1009
/* Widen to 32-bit representation and OR with current \
1010
* value */ \
1011
(*value) |= (_widen)scratch._repr[0]; \
1012
} while(0)
1013
1014
/* Apply mask/shift and widen to a common 32bit representation */
1015
switch (type) {
1016
case BHND_NVRAM_TYPE_UINT8:
1017
NV_PARSE_INT(uint32_t, u8, );
1018
break;
1019
case BHND_NVRAM_TYPE_UINT16:
1020
NV_PARSE_INT(uint32_t, u16, le16toh);
1021
break;
1022
case BHND_NVRAM_TYPE_UINT32:
1023
NV_PARSE_INT(uint32_t, u32, le32toh);
1024
break;
1025
case BHND_NVRAM_TYPE_INT8:
1026
NV_PARSE_INT(int32_t, i8, );
1027
break;
1028
case BHND_NVRAM_TYPE_INT16:
1029
NV_PARSE_INT(int32_t, i16, le16toh);
1030
break;
1031
case BHND_NVRAM_TYPE_INT32:
1032
NV_PARSE_INT(int32_t, i32, le32toh);
1033
break;
1034
case BHND_NVRAM_TYPE_CHAR:
1035
NV_PARSE_INT(uint32_t, u8, );
1036
break;
1037
default:
1038
BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
1039
return (EFTYPE);
1040
}
1041
#undef NV_PARSE_INT
1042
1043
return (0);
1044
}
1045
1046
/**
1047
* Read a SPROM variable value from @p io.
1048
*
1049
* @param state The SPROM opcode state describing the layout of @p io.
1050
* @param entry The variable's SPROM opcode index entry.
1051
* @param io The input I/O context.
1052
* @param storage Storage to be used with @p val.
1053
* @param[out] val Value instance to be initialized with the
1054
* parsed variable data.
1055
*
1056
* The returned @p val instance will hold a borrowed reference to @p storage,
1057
* and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
1058
* the lifetime of @p storage.
1059
*
1060
* The caller is responsible for releasing any allocated value state
1061
* via bhnd_nvram_val_release().
1062
*/
1063
static int
1064
bhnd_nvram_sprom_read_var(struct bhnd_sprom_opcode_state *state,
1065
struct bhnd_sprom_opcode_idx_entry *entry, struct bhnd_nvram_io *io,
1066
union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
1067
{
1068
union bhnd_nvram_sprom_storage *inp;
1069
const struct bhnd_nvram_vardefn *var;
1070
bhnd_nvram_type var_btype;
1071
uint32_t intv;
1072
size_t ilen, ipos, iwidth;
1073
size_t nelem;
1074
bool all_bits_set;
1075
int error;
1076
1077
/* Fetch canonical variable definition */
1078
var = bhnd_nvram_get_vardefn(entry->vid);
1079
BHND_NV_ASSERT(var != NULL, ("invalid entry"));
1080
1081
/*
1082
* Fetch the array length from the SPROM variable definition.
1083
*
1084
* This generally be identical to the array length provided by the
1085
* canonical NVRAM variable definition, but some SPROM layouts may
1086
* define a smaller element count.
1087
*/
1088
if ((error = bhnd_sprom_opcode_eval_var(state, entry))) {
1089
BHND_NV_LOG("variable evaluation failed: %d\n", error);
1090
return (error);
1091
}
1092
1093
nelem = state->var.nelem;
1094
if (nelem > var->nelem) {
1095
BHND_NV_LOG("SPROM array element count %zu cannot be "
1096
"represented by '%s' element count of %hhu\n", nelem,
1097
var->name, var->nelem);
1098
return (EFTYPE);
1099
}
1100
1101
/* Fetch the var's base element type */
1102
var_btype = bhnd_nvram_base_type(var->type);
1103
1104
/* Calculate total byte length of the native encoding */
1105
if ((iwidth = bhnd_nvram_value_size(NULL, 0, var_btype, 1)) == 0) {
1106
/* SPROM does not use (and we do not support) decoding of
1107
* variable-width data types */
1108
BHND_NV_LOG("invalid SPROM data type: %d", var->type);
1109
return (EFTYPE);
1110
}
1111
ilen = nelem * iwidth;
1112
1113
/* Decode into our caller's local storage */
1114
inp = storage;
1115
if (ilen > sizeof(*storage)) {
1116
BHND_NV_LOG("error decoding '%s', SPROM_ARRAY_MAXLEN "
1117
"incorrect\n", var->name);
1118
return (EFTYPE);
1119
}
1120
1121
/* Zero-initialize our decode buffer; any output elements skipped
1122
* during decode should default to zero. */
1123
memset(inp, 0, ilen);
1124
1125
/*
1126
* Decode the SPROM data, iteratively decoding up to nelem values.
1127
*/
1128
if ((error = bhnd_sprom_opcode_seek(state, entry))) {
1129
BHND_NV_LOG("variable seek failed: %d\n", error);
1130
return (error);
1131
}
1132
1133
ipos = 0;
1134
intv = 0x0;
1135
if (var->flags & BHND_NVRAM_VF_IGNALL1)
1136
all_bits_set = true;
1137
else
1138
all_bits_set = false;
1139
while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) {
1140
bhnd_sprom_opcode_bind *binding;
1141
bhnd_sprom_opcode_var *binding_var;
1142
bhnd_nvram_type intv_type;
1143
size_t offset;
1144
size_t nbyte;
1145
uint32_t skip_in_bytes;
1146
void *ptr;
1147
1148
BHND_NV_ASSERT(
1149
state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
1150
("invalid var state"));
1151
BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state"));
1152
1153
binding_var = &state->var;
1154
binding = &state->var.bind;
1155
1156
if (ipos >= nelem) {
1157
BHND_NV_LOG("output skip %u positioned "
1158
"%zu beyond nelem %zu\n",
1159
binding->skip_out, ipos, nelem);
1160
return (EINVAL);
1161
}
1162
1163
/* Calculate input skip bytes for this binding */
1164
skip_in_bytes = binding->skip_in;
1165
error = bhnd_sprom_opcode_apply_scale(state, &skip_in_bytes);
1166
if (error)
1167
return (error);
1168
1169
/* Bind */
1170
offset = state->offset;
1171
for (size_t i = 0; i < binding->count; i++) {
1172
/* Read the offset value, OR'ing with the current
1173
* value of intv */
1174
error = bhnd_nvram_sprom_read_offset(var, io,
1175
binding_var->base_type,
1176
offset,
1177
binding_var->mask,
1178
binding_var->shift,
1179
&intv);
1180
if (error)
1181
return (error);
1182
1183
/* If IGNALL1, record whether value does not have
1184
* all bits set. */
1185
if (var->flags & BHND_NVRAM_VF_IGNALL1 &&
1186
all_bits_set)
1187
{
1188
uint32_t all1;
1189
1190
all1 = binding_var->mask;
1191
if (binding_var->shift > 0)
1192
all1 >>= binding_var->shift;
1193
else if (binding_var->shift < 0)
1194
all1 <<= -binding_var->shift;
1195
1196
if ((intv & all1) != all1)
1197
all_bits_set = false;
1198
}
1199
1200
/* Adjust input position; this was already verified to
1201
* not overflow/underflow during SPROM opcode
1202
* evaluation */
1203
if (binding->skip_in_negative) {
1204
offset -= skip_in_bytes;
1205
} else {
1206
offset += skip_in_bytes;
1207
}
1208
1209
/* Skip writing to inp if additional bindings are
1210
* required to fully populate intv */
1211
if (binding->skip_out == 0)
1212
continue;
1213
1214
/* We use bhnd_nvram_value_coerce() to perform
1215
* overflow-checked coercion from the widened
1216
* uint32/int32 intv value to the requested output
1217
* type */
1218
if (bhnd_nvram_is_signed_type(var_btype))
1219
intv_type = BHND_NVRAM_TYPE_INT32;
1220
else
1221
intv_type = BHND_NVRAM_TYPE_UINT32;
1222
1223
/* Calculate address of the current element output
1224
* position */
1225
ptr = (uint8_t *)inp + (iwidth * ipos);
1226
1227
/* Perform coercion of the array element */
1228
nbyte = iwidth;
1229
error = bhnd_nvram_value_coerce(&intv, sizeof(intv),
1230
intv_type, ptr, &nbyte, var_btype);
1231
if (error)
1232
return (error);
1233
1234
/* Clear temporary state */
1235
intv = 0x0;
1236
1237
/* Advance output position */
1238
if (SIZE_MAX - binding->skip_out < ipos) {
1239
BHND_NV_LOG("output skip %u would overflow "
1240
"%zu\n", binding->skip_out, ipos);
1241
return (EINVAL);
1242
}
1243
1244
ipos += binding->skip_out;
1245
}
1246
}
1247
1248
/* Did we iterate all bindings until hitting end of the variable
1249
* definition? */
1250
BHND_NV_ASSERT(error != 0, ("loop terminated early"));
1251
if (error != ENOENT) {
1252
return (error);
1253
}
1254
1255
/* If marked IGNALL1 and all bits are set, treat variable as
1256
* unavailable */
1257
if ((var->flags & BHND_NVRAM_VF_IGNALL1) && all_bits_set)
1258
return (ENOENT);
1259
1260
/* Provide value wrapper */
1261
return (bhnd_nvram_val_init(val, var->fmt, inp, ilen, var->type,
1262
BHND_NVRAM_VAL_BORROW_DATA));
1263
}
1264
1265
/**
1266
* Common variable decoding; fetches and decodes variable to @p val,
1267
* using @p storage for actual data storage.
1268
*
1269
* The returned @p val instance will hold a borrowed reference to @p storage,
1270
* and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
1271
* the lifetime of @p storage.
1272
*
1273
* The caller is responsible for releasing any allocated value state
1274
* via bhnd_nvram_val_release().
1275
*/
1276
static int
1277
bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep,
1278
union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
1279
{
1280
struct bhnd_nvram_sprom *sp;
1281
bhnd_sprom_opcode_idx_entry *entry;
1282
const struct bhnd_nvram_vardefn *var __diagused;
1283
1284
BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
1285
1286
sp = (struct bhnd_nvram_sprom *)nv;
1287
entry = cookiep;
1288
1289
/* Fetch canonical variable definition */
1290
var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
1291
BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
1292
1293
return (bhnd_nvram_sprom_read_var(&sp->state, entry, sp->data, storage,
1294
val));
1295
}
1296
1297
static int
1298
bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
1299
void *cookiep2)
1300
{
1301
struct bhnd_sprom_opcode_idx_entry *e1, *e2;
1302
1303
e1 = cookiep1;
1304
e2 = cookiep2;
1305
1306
/* Use the index entry order; this matches the order of variables
1307
* returned via bhnd_nvram_sprom_next() */
1308
if (e1 < e2)
1309
return (-1);
1310
else if (e1 > e2)
1311
return (1);
1312
1313
return (0);
1314
}
1315
1316
static int
1317
bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
1318
size_t *len, bhnd_nvram_type otype)
1319
{
1320
bhnd_nvram_val val;
1321
union bhnd_nvram_sprom_storage storage;
1322
int error;
1323
1324
/* Decode variable to a new value instance */
1325
error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
1326
if (error)
1327
return (error);
1328
1329
/* Perform value coercion */
1330
error = bhnd_nvram_val_encode(&val, buf, len, otype);
1331
1332
/* Clean up */
1333
bhnd_nvram_val_release(&val);
1334
return (error);
1335
}
1336
1337
static int
1338
bhnd_nvram_sprom_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
1339
bhnd_nvram_val **value)
1340
{
1341
bhnd_nvram_val val;
1342
union bhnd_nvram_sprom_storage storage;
1343
int error;
1344
1345
/* Decode variable to a new value instance */
1346
error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
1347
if (error)
1348
return (error);
1349
1350
/* Attempt to copy to heap */
1351
*value = bhnd_nvram_val_copy(&val);
1352
bhnd_nvram_val_release(&val);
1353
1354
if (*value == NULL)
1355
return (ENOMEM);
1356
1357
return (0);
1358
}
1359
1360
static const void *
1361
bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
1362
size_t *len, bhnd_nvram_type *type)
1363
{
1364
/* Unsupported */
1365
return (NULL);
1366
}
1367
1368
static const char *
1369
bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
1370
{
1371
const struct bhnd_nvram_vardefn *var;
1372
1373
BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
1374
1375
var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
1376
BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
1377
1378
return (var->name);
1379
}
1380
1381
static int
1382
bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
1383
bhnd_nvram_val *value, bhnd_nvram_val **result)
1384
{
1385
struct bhnd_nvram_sprom *sp;
1386
const struct bhnd_nvram_vardefn *var;
1387
bhnd_sprom_opcode_idx_entry *entry;
1388
bhnd_nvram_val *spval;
1389
int error;
1390
1391
sp = (struct bhnd_nvram_sprom *)nv;
1392
1393
/* Is this an externally immutable variable name? */
1394
if (bhnd_sprom_is_external_immutable(name))
1395
return (EINVAL);
1396
1397
/* Variable must be defined in our SPROM layout */
1398
if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
1399
return (ENOENT);
1400
1401
var = bhnd_nvram_get_vardefn(entry->vid);
1402
BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
1403
1404
/* Value must be convertible to the native variable type */
1405
error = bhnd_nvram_val_convert_new(&spval, var->fmt, value,
1406
BHND_NVRAM_VAL_DYNAMIC);
1407
if (error)
1408
return (error);
1409
1410
/* Value must be encodeable by our SPROM layout */
1411
error = bhnd_nvram_sprom_write_var(&sp->state, entry, spval, NULL);
1412
if (error) {
1413
bhnd_nvram_val_release(spval);
1414
return (error);
1415
}
1416
1417
/* Success. Transfer our ownership of the converted value to the
1418
* caller */
1419
*result = spval;
1420
return (0);
1421
}
1422
1423
static int
1424
bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
1425
{
1426
struct bhnd_nvram_sprom *sp;
1427
const struct bhnd_nvram_vardefn *var;
1428
bhnd_sprom_opcode_idx_entry *entry;
1429
1430
sp = (struct bhnd_nvram_sprom *)nv;
1431
1432
/* Is this an externally immutable variable name? */
1433
if (bhnd_sprom_is_external_immutable(name))
1434
return (EINVAL);
1435
1436
/* Variable must be defined in our SPROM layout */
1437
if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
1438
return (ENOENT);
1439
1440
var = bhnd_nvram_get_vardefn(entry->vid);
1441
BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
1442
1443
/* Variable must be capable of representing a NULL/deleted value.
1444
*
1445
* Since SPROM's layout is fixed, this requires IGNALL -- if
1446
* all bits are set, an IGNALL variable is treated as unset. */
1447
if (!(var->flags & BHND_NVRAM_VF_IGNALL1))
1448
return (EINVAL);
1449
1450
return (0);
1451
}
1452
1453
/**
1454
* Return true if @p name represents a special immutable variable name
1455
* (e.g. sromrev) that cannot be updated in an SPROM existing image.
1456
*
1457
* @param name The name to check.
1458
*/
1459
static bool
1460
bhnd_sprom_is_external_immutable(const char *name)
1461
{
1462
/* The layout revision is immutable and cannot be changed */
1463
if (strcmp(name, BHND_NVAR_SROMREV) == 0)
1464
return (true);
1465
1466
return (false);
1467
}
1468
1469