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