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 #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
bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io * io,const bhnd_sprom_layout * layout,uint16_t * magic)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
bhnd_nvram_sprom_ident(struct bhnd_nvram_io * io,const bhnd_sprom_layout ** ident)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
bhnd_nvram_sprom_probe(struct bhnd_nvram_io * io)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
bhnd_nvram_sprom_getvar_direct(struct bhnd_nvram_io * io,const char * name,void * buf,size_t * len,bhnd_nvram_type type)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 *
bhnd_nvram_sprom_get_layout(uint8_t sromrev)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
bhnd_nvram_sprom_write_var(bhnd_sprom_opcode_state * state,bhnd_sprom_opcode_idx_entry * entry,bhnd_nvram_val * value,struct bhnd_nvram_io * io)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
bhnd_nvram_sprom_serialize(bhnd_nvram_data_class * cls,bhnd_nvram_plist * props,bhnd_nvram_plist * options,void * outp,size_t * olen)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
bhnd_nvram_sprom_new(struct bhnd_nvram_data * nv,struct bhnd_nvram_io * io)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
bhnd_nvram_sprom_free(struct bhnd_nvram_data * nv)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
bhnd_nvram_sprom_count(struct bhnd_nvram_data * nv)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 *
bhnd_nvram_sprom_options(struct bhnd_nvram_data * nv)807 bhnd_nvram_sprom_options(struct bhnd_nvram_data *nv)
808 {
809 return (NULL);
810 }
811
812 static uint32_t
bhnd_nvram_sprom_caps(struct bhnd_nvram_data * nv)813 bhnd_nvram_sprom_caps(struct bhnd_nvram_data *nv)
814 {
815 return (BHND_NVRAM_DATA_CAP_INDEXED);
816 }
817
818 static const char *
bhnd_nvram_sprom_next(struct bhnd_nvram_data * nv,void ** cookiep)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 *
bhnd_nvram_sprom_find(struct bhnd_nvram_data * nv,const char * name)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
bhnd_nvram_sprom_write_offset(const struct bhnd_nvram_vardefn * var,struct bhnd_nvram_io * data,bhnd_nvram_type type,size_t offset,uint32_t mask,int8_t shift,uint32_t value)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
bhnd_nvram_sprom_read_offset(const struct bhnd_nvram_vardefn * var,struct bhnd_nvram_io * data,bhnd_nvram_type type,size_t offset,uint32_t mask,int8_t shift,uint32_t * value)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
bhnd_nvram_sprom_read_var(struct bhnd_sprom_opcode_state * state,struct bhnd_sprom_opcode_idx_entry * entry,struct bhnd_nvram_io * io,union bhnd_nvram_sprom_storage * storage,bhnd_nvram_val * val)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
bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data * nv,void * cookiep,union bhnd_nvram_sprom_storage * storage,bhnd_nvram_val * val)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
bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data * nv,void * cookiep1,void * cookiep2)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
bhnd_nvram_sprom_getvar(struct bhnd_nvram_data * nv,void * cookiep,void * buf,size_t * len,bhnd_nvram_type otype)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
bhnd_nvram_sprom_copy_val(struct bhnd_nvram_data * nv,void * cookiep,bhnd_nvram_val ** value)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 *
bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data * nv,void * cookiep,size_t * len,bhnd_nvram_type * type)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 *
bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data * nv,void * cookiep)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
bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data * nv,const char * name,bhnd_nvram_val * value,bhnd_nvram_val ** result)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
bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data * nv,const char * name)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
bhnd_sprom_is_external_immutable(const char * name)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