2 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/endian.h>
36 #include <sys/param.h>
37 #include <sys/ctype.h>
38 #include <sys/malloc.h>
39 #include <sys/systm.h>
41 #include <machine/_inttypes.h>
52 #include "bhnd_nvram_map.h"
54 #include "bhnd_nvram_private.h"
55 #include "bhnd_nvram_datavar.h"
57 #include "bhnd_nvram_data_spromvar.h"
60 * BHND SPROM NVRAM data class
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.
67 static const bhnd_sprom_layout *bhnd_nvram_sprom_get_layout(uint8_t sromrev);
69 static int bhnd_nvram_sprom_ident(
70 struct bhnd_nvram_io *io,
71 const bhnd_sprom_layout **ident,
72 struct bhnd_nvram_io **shadow);
74 static int bhnd_nvram_sprom_write_var(
75 bhnd_sprom_opcode_state *state,
76 bhnd_sprom_opcode_idx_entry *entry,
77 bhnd_nvram_val *value,
78 struct bhnd_nvram_io *io);
80 static int bhnd_nvram_sprom_write_offset(
81 const struct bhnd_nvram_vardefn *var,
82 struct bhnd_nvram_io *data,
83 bhnd_nvram_type type, size_t offset,
84 uint32_t mask, int8_t shift,
87 static int bhnd_nvram_sprom_read_offset(
88 const struct bhnd_nvram_vardefn *var,
89 struct bhnd_nvram_io *data,
90 bhnd_nvram_type type, size_t offset,
91 uint32_t mask, int8_t shift,
94 static bool bhnd_sprom_is_external_immutable(
97 BHND_NVRAM_DATA_CLASS_DEFN(sprom, "Broadcom SPROM",
98 BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_sprom))
100 #define SPROM_COOKIE_TO_VID(_cookie) \
101 (((struct bhnd_sprom_opcode_idx_entry *)(_cookie))->vid)
103 #define SPROM_COOKIE_TO_NVRAM_VAR(_cookie) \
104 bhnd_nvram_get_vardefn(SPROM_COOKIE_TO_VID(_cookie))
107 * Read the magic value from @p io, and verify that it matches
108 * the @p layout's expected magic value.
110 * If @p layout does not defined a magic value, @p magic is set to 0x0
111 * and success is returned.
113 * @param io An I/O context mapping the SPROM data to be identified.
114 * @param layout The SPROM layout against which @p io should be verified.
115 * @param[out] magic On success, the SPROM magic value.
118 * @retval non-zero If checking @p io otherwise fails, a regular unix
119 * error code will be returned.
122 bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io *io,
123 const bhnd_sprom_layout *layout, uint16_t *magic)
127 /* Skip if layout does not define a magic value */
128 if (layout->flags & SPROM_LAYOUT_MAGIC_NONE)
131 /* Read the magic value */
132 error = bhnd_nvram_io_read(io, layout->magic_offset, magic,
137 *magic = le16toh(*magic);
139 /* If the signature does not match, skip to next layout */
140 if (*magic != layout->magic_value)
147 * Attempt to identify the format of the SPROM data mapped by @p io.
149 * The SPROM data format does not provide any identifying information at a
150 * known offset, instead requiring that we iterate over the known SPROM image
151 * sizes until we are able to compute a valid checksum (and, for later
152 * revisions, validate a signature at a revision-specific offset).
154 * @param io An I/O context mapping the SPROM data to be identified.
155 * @param[out] ident On success, the identified SPROM layout.
156 * @param[out] shadow On success, a correctly sized iobuf instance mapping
157 * a copy of the identified SPROM image. The caller is
158 * responsible for deallocating this instance via
159 * bhnd_nvram_io_free()
162 * @retval non-zero If identifying @p io otherwise fails, a regular unix
163 * error code will be returned.
166 bhnd_nvram_sprom_ident(struct bhnd_nvram_io *io,
167 const bhnd_sprom_layout **ident, struct bhnd_nvram_io **shadow)
169 struct bhnd_nvram_io *buf;
175 /* Find the largest SPROM layout size */
177 for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
178 sprom_sz_max = bhnd_nv_ummax(sprom_sz_max,
179 bhnd_sprom_layouts[i].size);
182 /* Allocate backing buffer and initialize CRC state */
183 buf = bhnd_nvram_iobuf_empty(0, sprom_sz_max);
184 crc = BHND_NVRAM_CRC8_INITIAL;
187 /* We iterate the SPROM layouts smallest to largest, allowing us to
188 * perform incremental checksum calculation */
189 for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
190 const bhnd_sprom_layout *layout;
198 layout = &bhnd_sprom_layouts[i];
199 nbytes = bhnd_nvram_io_getsize(buf);
201 if ((layout->flags & SPROM_LAYOUT_MAGIC_NONE)) {
207 /* Layout instances must be ordered from smallest to largest by
208 * the nvram_map compiler */
209 if (nbytes > layout->size)
210 BHND_NV_PANIC("SPROM layout is defined out-of-order");
212 /* Calculate number of additional bytes to be read */
213 nr = layout->size - nbytes;
215 /* Adjust the buffer size and fetch a write pointer */
216 if ((error = bhnd_nvram_io_setsize(buf, layout->size)))
219 error = bhnd_nvram_io_write_ptr(buf, nbytes, &ptr, nr, NULL);
223 /* Read image data and update CRC (errors are reported
224 * after the signature check) */
225 if ((error = bhnd_nvram_io_read(io, nbytes, ptr, nr)))
228 crc = bhnd_nvram_crc8(ptr, nr, crc);
229 crc_valid = (crc == BHND_NVRAM_CRC8_VALID);
233 /* Fetch SPROM revision */
234 error = bhnd_nvram_io_read(buf, layout->srev_offset, &srev,
239 /* Early sromrev 1 devices (specifically some BCM440x enet
240 * cards) are reported to have been incorrectly programmed
241 * with a revision of 0x10. */
242 if (layout->rev == 1 && srev == 0x10)
245 /* Check revision against the layout definition */
246 if (srev != layout->rev)
249 /* Check the magic value, skipping to the next layout on
251 error = bhnd_nvram_sprom_check_magic(buf, layout, &magic);
253 /* If the CRC is was valid, log the mismatch */
254 if (crc_valid || BHND_NV_VERBOSE) {
255 BHND_NV_LOG("invalid sprom %hhu signature: "
256 "0x%hx (expected 0x%hx)\n", srev,
257 magic, layout->magic_value);
266 /* Check for an earlier CRC error */
268 /* If the magic check succeeded, then we may just have
269 * data corruption -- log the CRC error */
270 if (have_magic || BHND_NV_VERBOSE) {
271 BHND_NV_LOG("sprom %hhu CRC error (crc=%#hhx, "
272 "expected=%#x)\n", srev, crc,
273 BHND_NVRAM_CRC8_VALID);
285 /* No match -- set error and fallthrough */
287 if (crc_errors > 0 && BHND_NV_VERBOSE) {
288 BHND_NV_LOG("sprom parsing failed with %zu CRC errors\n",
293 bhnd_nvram_io_free(buf);
298 bhnd_nvram_sprom_probe(struct bhnd_nvram_io *io)
300 const bhnd_sprom_layout *layout;
301 struct bhnd_nvram_io *shadow;
304 /* Try to parse the input */
305 if ((error = bhnd_nvram_sprom_ident(io, &layout, &shadow)))
308 /* Clean up the shadow iobuf */
309 bhnd_nvram_io_free(shadow);
311 return (BHND_NVRAM_DATA_PROBE_DEFAULT);
316 * Return the SPROM layout definition for the given @p sromrev, or NULL if
319 static const bhnd_sprom_layout *
320 bhnd_nvram_sprom_get_layout(uint8_t sromrev)
322 /* Find matching SPROM layout definition */
323 for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
324 if (bhnd_sprom_layouts[i].rev == sromrev)
325 return (&bhnd_sprom_layouts[i]);
333 * Serialize a SPROM variable.
335 * @param state The SPROM opcode state describing the layout of @p io.
336 * @param entry The variable's SPROM opcode index entry.
337 * @param value The value to encode to @p io as per @p entry.
338 * @param io I/O context to which @p value should be written, or NULL
339 * if no output should be produced. This may be used to validate
340 * values prior to write.
343 * @retval EFTYPE If value coercion from @p value to the type required by
344 * @p entry is unsupported.
345 * @retval ERANGE If value coercion from @p value would overflow
346 * (or underflow) the type required by @p entry.
347 * @retval non-zero If serialization otherwise fails, a regular unix error
348 * code will be returned.
351 bhnd_nvram_sprom_write_var(bhnd_sprom_opcode_state *state,
352 bhnd_sprom_opcode_idx_entry *entry, bhnd_nvram_val *value,
353 struct bhnd_nvram_io *io)
355 const struct bhnd_nvram_vardefn *var;
356 uint32_t u32[BHND_SPROM_ARRAY_MAXLEN];
357 bhnd_nvram_type itype, var_base_type;
358 size_t ipos, ilen, nelem;
361 /* Fetch variable definition and the native element type */
362 var = bhnd_nvram_get_vardefn(entry->vid);
363 BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
365 var_base_type = bhnd_nvram_base_type(var->type);
367 /* Fetch the element count from the SPROM variable layout definition */
368 if ((error = bhnd_sprom_opcode_parse_var(state, entry)))
371 nelem = state->var.nelem;
372 BHND_NV_ASSERT(nelem <= var->nelem, ("SPROM nelem=%zu exceeds maximum "
373 "NVRAM nelem=%hhu", nelem, var->nelem));
375 /* Promote the data to a common 32-bit representation */
376 if (bhnd_nvram_is_signed_type(var_base_type))
377 itype = BHND_NVRAM_TYPE_INT32_ARRAY;
379 itype = BHND_NVRAM_TYPE_UINT32_ARRAY;
381 /* Calculate total size of the 32-bit promoted representation */
382 if ((ilen = bhnd_nvram_value_size(NULL, 0, itype, nelem)) == 0) {
383 /* Variable-width types are unsupported */
384 BHND_NV_LOG("invalid %s SPROM variable type %d\n",
385 var->name, var->type);
389 /* The native representation must fit within our scratch array */
390 if (ilen > sizeof(u32)) {
391 BHND_NV_LOG("error encoding '%s', SPROM_ARRAY_MAXLEN "
392 "incorrect\n", var->name);
396 /* Initialize our common 32-bit value representation */
397 if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) {
398 /* No value provided; can this variable be encoded as missing
399 * by setting all bits to one? */
400 if (!(var->flags & BHND_NVRAM_VF_IGNALL1)) {
401 BHND_NV_LOG("missing required property: %s\n",
407 memset(u32, 0xFF, ilen);
409 bhnd_nvram_val bcm_val;
411 bhnd_nvram_type var_type, raw_type;
412 size_t var_len, enc_nelem;
414 /* Try to coerce the value to the native variable format. */
415 error = bhnd_nvram_val_convert_init(&bcm_val, var->fmt, value,
416 BHND_NVRAM_VAL_DYNAMIC|BHND_NVRAM_VAL_BORROW_DATA);
418 BHND_NV_LOG("error converting input type %s to %s "
420 bhnd_nvram_type_name(bhnd_nvram_val_type(value)),
421 bhnd_nvram_val_fmt_name(var->fmt));
425 var_ptr = bhnd_nvram_val_bytes(&bcm_val, &var_len, &var_type);
428 * Promote to a common 32-bit representation.
430 * We must use the raw type to interpret the input data as its
431 * underlying integer representation -- otherwise, coercion
432 * would attempt to parse the input as its complex
435 * For example, direct CHAR -> UINT32 coercion would attempt to
436 * parse the character as a decimal integer, rather than
437 * promoting the raw UTF8 byte value to a 32-bit value.
439 raw_type = bhnd_nvram_raw_type(var_type);
440 error = bhnd_nvram_value_coerce(var_ptr, var_len, raw_type,
443 /* Clean up temporary value representation */
444 bhnd_nvram_val_release(&bcm_val);
446 /* Report coercion failure */
448 BHND_NV_LOG("error promoting %s to %s: %d\n",
449 bhnd_nvram_type_name(var_type),
450 bhnd_nvram_type_name(itype), error);
454 /* Encoded element count must match SPROM's definition */
455 error = bhnd_nvram_value_nelem(u32, ilen, itype, &enc_nelem);
459 if (enc_nelem != nelem) {
460 const char *type_name;
462 type_name = bhnd_nvram_type_name(var_base_type);
463 BHND_NV_LOG("invalid %s property value '%s[%zu]': "
464 "required %s[%zu]", var->name, type_name,
465 enc_nelem, type_name, nelem);
471 * Seek to the start of the variable's SPROM layout definition and
472 * iterate over all bindings.
474 if ((error = bhnd_sprom_opcode_seek(state, entry))) {
475 BHND_NV_LOG("variable seek failed: %d\n", error);
480 while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) {
481 bhnd_sprom_opcode_bind *binding;
482 bhnd_sprom_opcode_var *binding_var;
484 uint32_t skip_out_bytes;
487 state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
488 ("invalid var state"));
489 BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state"));
491 binding_var = &state->var;
492 binding = &state->var.bind;
494 /* Calculate output skip bytes for this binding.
496 * Skip directions are defined in terms of decoding, and
497 * reversed when encoding. */
498 skip_out_bytes = binding->skip_in;
499 error = bhnd_sprom_opcode_apply_scale(state, &skip_out_bytes);
504 offset = state->offset;
505 for (size_t i = 0; i < binding->count; i++) {
507 BHND_NV_LOG("input skip %u positioned %zu "
508 "beyond nelem %zu\n", binding->skip_out,
513 /* Write next offset */
515 error = bhnd_nvram_sprom_write_offset(var, io,
516 binding_var->base_type,
525 /* Adjust output position; this was already verified to
526 * not overflow/underflow during SPROM opcode
528 if (binding->skip_in_negative) {
529 offset -= skip_out_bytes;
531 offset += skip_out_bytes;
534 /* Skip advancing input if additional bindings are
535 * required to fully encode intv */
536 if (binding->skip_out == 0)
539 /* Advance input position */
540 if (SIZE_MAX - binding->skip_out < ipos) {
541 BHND_NV_LOG("output skip %u would overflow "
542 "%zu\n", binding->skip_out, ipos);
546 ipos += binding->skip_out;
550 /* Did we iterate all bindings until hitting end of the variable
552 BHND_NV_ASSERT(error != 0, ("loop terminated early"));
560 bhnd_nvram_sprom_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
561 bhnd_nvram_plist *options, void *outp, size_t *olen)
563 bhnd_sprom_opcode_state state;
564 struct bhnd_nvram_io *io;
565 bhnd_nvram_prop *prop;
566 bhnd_sprom_opcode_idx_entry *entry;
567 const bhnd_sprom_layout *layout;
577 /* Fetch sromrev property */
578 if (!bhnd_nvram_plist_contains(props, BHND_NVAR_SROMREV)) {
579 BHND_NV_LOG("missing required property: %s\n",
584 error = bhnd_nvram_plist_get_uint8(props, BHND_NVAR_SROMREV, &sromrev);
586 BHND_NV_LOG("error reading sromrev property: %d\n", error);
590 /* Find SPROM layout definition */
591 if ((layout = bhnd_nvram_sprom_get_layout(sromrev)) == NULL) {
592 BHND_NV_LOG("unsupported sromrev: %hhu\n", sromrev);
596 /* Provide required size to caller */
597 *olen = layout->size;
600 else if (limit < *olen)
603 /* Initialize SPROM layout interpreter */
604 if ((error = bhnd_sprom_opcode_init(&state, layout))) {
605 BHND_NV_LOG("error initializing opcode state: %d\n", error);
609 /* Check for unsupported properties */
611 while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
614 /* Fetch the corresponding SPROM layout index entry */
615 name = bhnd_nvram_prop_name(prop);
616 entry = bhnd_sprom_opcode_index_find(&state, name);
618 BHND_NV_LOG("property '%s' unsupported by sromrev "
619 "%hhu\n", name, layout->rev);
625 /* Zero-initialize output */
626 memset(outp, 0, *olen);
628 /* Allocate wrapping I/O context for output buffer */
629 io = bhnd_nvram_ioptr_new(outp, *olen, *olen, BHND_NVRAM_IOPTR_RDWR);
636 * Serialize all SPROM variable data.
639 while ((entry = bhnd_sprom_opcode_index_next(&state, entry)) != NULL) {
640 const struct bhnd_nvram_vardefn *var;
643 var = bhnd_nvram_get_vardefn(entry->vid);
644 BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
646 /* Fetch prop; will be NULL if unavailable */
647 prop = bhnd_nvram_plist_get_prop(props, var->name);
649 val = bhnd_nvram_prop_val(prop);
651 val = BHND_NVRAM_VAL_NULL;
654 /* Attempt to serialize the property value to the appropriate
655 * offset within the output buffer */
656 error = bhnd_nvram_sprom_write_var(&state, entry, val, io);
658 BHND_NV_LOG("error serializing %s to required type "
659 "%s: %d\n", var->name,
660 bhnd_nvram_type_name(var->type), error);
662 /* ENOMEM is reserved for signaling that the output
663 * buffer capacity is insufficient */
672 * Write magic value, if any.
674 if (!(layout->flags & SPROM_LAYOUT_MAGIC_NONE)) {
677 magic = htole16(layout->magic_value);
678 error = bhnd_nvram_io_write(io, layout->magic_offset, &magic,
681 BHND_NV_LOG("error writing magic value: %d\n", error);
686 /* Calculate the CRC over all SPROM data, not including the CRC byte. */
687 crc = ~bhnd_nvram_crc8(outp, layout->crc_offset,
688 BHND_NVRAM_CRC8_INITIAL);
690 /* Write the checksum. */
691 error = bhnd_nvram_io_write(io, layout->crc_offset, &crc, sizeof(crc));
693 BHND_NV_LOG("error writing CRC value: %d\n", error);
703 bhnd_sprom_opcode_fini(&state);
706 bhnd_nvram_io_free(io);
712 bhnd_nvram_sprom_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
714 struct bhnd_nvram_sprom *sp;
717 sp = (struct bhnd_nvram_sprom *)nv;
719 /* Identify the SPROM input data */
720 if ((error = bhnd_nvram_sprom_ident(io, &sp->layout, &sp->data)))
723 /* Initialize SPROM binding eval state */
724 if ((error = bhnd_sprom_opcode_init(&sp->state, sp->layout)))
730 if (sp->data != NULL)
731 bhnd_nvram_io_free(sp->data);
737 bhnd_nvram_sprom_free(struct bhnd_nvram_data *nv)
739 struct bhnd_nvram_sprom *sp = (struct bhnd_nvram_sprom *)nv;
741 bhnd_sprom_opcode_fini(&sp->state);
742 bhnd_nvram_io_free(sp->data);
746 bhnd_nvram_sprom_count(struct bhnd_nvram_data *nv)
748 struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv;
749 return (sprom->layout->num_vars);
752 static bhnd_nvram_plist *
753 bhnd_nvram_sprom_options(struct bhnd_nvram_data *nv)
759 bhnd_nvram_sprom_caps(struct bhnd_nvram_data *nv)
761 return (BHND_NVRAM_DATA_CAP_INDEXED);
765 bhnd_nvram_sprom_next(struct bhnd_nvram_data *nv, void **cookiep)
767 struct bhnd_nvram_sprom *sp;
768 bhnd_sprom_opcode_idx_entry *entry;
769 const struct bhnd_nvram_vardefn *var;
771 sp = (struct bhnd_nvram_sprom *)nv;
773 /* Find next index entry that is not disabled by virtue of IGNALL1 */
775 while ((entry = bhnd_sprom_opcode_index_next(&sp->state, entry))) {
776 /* Update cookiep and fetch variable definition */
778 var = SPROM_COOKIE_TO_NVRAM_VAR(*cookiep);
780 /* We might need to parse the variable's value to determine
781 * whether it should be treated as unset */
782 if (var->flags & BHND_NVRAM_VF_IGNALL1) {
786 error = bhnd_nvram_sprom_getvar(nv, *cookiep, NULL,
789 BHND_NV_ASSERT(error == ENOENT, ("unexpected "
790 "error parsing variable: %d", error));
799 /* Reached end of index entries */
804 bhnd_nvram_sprom_find(struct bhnd_nvram_data *nv, const char *name)
806 struct bhnd_nvram_sprom *sp;
807 bhnd_sprom_opcode_idx_entry *entry;
809 sp = (struct bhnd_nvram_sprom *)nv;
811 entry = bhnd_sprom_opcode_index_find(&sp->state, name);
816 * Write @p value of @p type to the SPROM @p data at @p offset, applying
817 * @p mask and @p shift, and OR with the existing data.
819 * @param var The NVRAM variable definition.
820 * @param data The SPROM data to be modified.
821 * @param type The type to write at @p offset.
822 * @param offset The data offset to be written.
823 * @param mask The mask to be applied to @p value after shifting.
824 * @param shift The shift to be applied to @p value; if positive, a left
825 * shift will be applied, if negative, a right shift (this is the reverse of the
827 * @param value The value to be written. The parsed value will be OR'd with the
828 * current contents of @p data at @p offset.
831 bhnd_nvram_sprom_write_offset(const struct bhnd_nvram_vardefn *var,
832 struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset,
833 uint32_t mask, int8_t shift, uint32_t value)
835 union bhnd_nvram_sprom_storage scratch;
838 #define NV_WRITE_INT(_widen, _repr, _swap) do { \
839 /* Narrow the 32-bit representation */ \
840 scratch._repr[1] = (_widen)value; \
842 /* Shift and mask the new value */ \
844 scratch._repr[1] <<= shift; \
845 else if (shift < 0) \
846 scratch._repr[1] >>= -shift; \
847 scratch._repr[1] &= mask; \
849 /* Swap to output byte order */ \
850 scratch._repr[1] = _swap(scratch._repr[1]); \
852 /* Fetch the current value */ \
853 error = bhnd_nvram_io_read(data, offset, \
854 &scratch._repr[0], sizeof(scratch._repr[0])); \
856 BHND_NV_LOG("error reading %s SPROM offset " \
857 "%#zx: %d\n", var->name, offset, error); \
861 /* Mask and set our new value's bits in the current \
864 scratch._repr[0] &= ~_swap(mask << shift); \
865 else if (shift < 0) \
866 scratch._repr[0] &= ~_swap(mask >> (-shift)); \
867 scratch._repr[0] |= scratch._repr[1]; \
869 /* Perform write */ \
870 error = bhnd_nvram_io_write(data, offset, \
871 &scratch._repr[0], sizeof(scratch._repr[0])); \
873 BHND_NV_LOG("error writing %s SPROM offset " \
874 "%#zx: %d\n", var->name, offset, error); \
879 /* Apply mask/shift and widen to a common 32bit representation */
881 case BHND_NVRAM_TYPE_UINT8:
882 NV_WRITE_INT(uint32_t, u8, );
884 case BHND_NVRAM_TYPE_UINT16:
885 NV_WRITE_INT(uint32_t, u16, htole16);
887 case BHND_NVRAM_TYPE_UINT32:
888 NV_WRITE_INT(uint32_t, u32, htole32);
890 case BHND_NVRAM_TYPE_INT8:
891 NV_WRITE_INT(int32_t, i8, );
893 case BHND_NVRAM_TYPE_INT16:
894 NV_WRITE_INT(int32_t, i16, htole16);
896 case BHND_NVRAM_TYPE_INT32:
897 NV_WRITE_INT(int32_t, i32, htole32);
899 case BHND_NVRAM_TYPE_CHAR:
900 NV_WRITE_INT(uint32_t, u8, );
903 BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
912 * Read the value of @p type from the SPROM @p data at @p offset, apply @p mask
913 * and @p shift, and OR with the existing @p value.
915 * @param var The NVRAM variable definition.
916 * @param data The SPROM data to be decoded.
917 * @param type The type to read at @p offset
918 * @param offset The data offset to be read.
919 * @param mask The mask to be applied to the value read at @p offset.
920 * @param shift The shift to be applied after masking; if positive, a right
921 * shift will be applied, if negative, a left shift.
922 * @param value The read destination; the parsed value will be OR'd with the
923 * current contents of @p value.
926 bhnd_nvram_sprom_read_offset(const struct bhnd_nvram_vardefn *var,
927 struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset,
928 uint32_t mask, int8_t shift, uint32_t *value)
930 union bhnd_nvram_sprom_storage scratch;
933 #define NV_PARSE_INT(_widen, _repr, _swap) do { \
935 error = bhnd_nvram_io_read(data, offset, \
936 &scratch._repr[0], sizeof(scratch._repr[0])); \
938 BHND_NV_LOG("error reading %s SPROM offset " \
939 "%#zx: %d\n", var->name, offset, error); \
943 /* Swap to host byte order */ \
944 scratch._repr[0] = _swap(scratch._repr[0]); \
946 /* Mask and shift the value */ \
947 scratch._repr[0] &= mask; \
949 scratch. _repr[0] >>= shift; \
950 } else if (shift < 0) { \
951 scratch. _repr[0] <<= -shift; \
954 /* Widen to 32-bit representation and OR with current \
956 (*value) |= (_widen)scratch._repr[0]; \
959 /* Apply mask/shift and widen to a common 32bit representation */
961 case BHND_NVRAM_TYPE_UINT8:
962 NV_PARSE_INT(uint32_t, u8, );
964 case BHND_NVRAM_TYPE_UINT16:
965 NV_PARSE_INT(uint32_t, u16, le16toh);
967 case BHND_NVRAM_TYPE_UINT32:
968 NV_PARSE_INT(uint32_t, u32, le32toh);
970 case BHND_NVRAM_TYPE_INT8:
971 NV_PARSE_INT(int32_t, i8, );
973 case BHND_NVRAM_TYPE_INT16:
974 NV_PARSE_INT(int32_t, i16, le16toh);
976 case BHND_NVRAM_TYPE_INT32:
977 NV_PARSE_INT(int32_t, i32, le32toh);
979 case BHND_NVRAM_TYPE_CHAR:
980 NV_PARSE_INT(uint32_t, u8, );
983 BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
992 * Common variable decoding; fetches and decodes variable to @p val,
993 * using @p storage for actual data storage.
995 * The returned @p val instance will hold a borrowed reference to @p storage,
996 * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
997 * the lifetime of @p storage.
999 * The caller is responsible for releasing any allocated value state
1000 * via bhnd_nvram_val_release().
1003 bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep,
1004 union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
1006 struct bhnd_nvram_sprom *sp;
1007 bhnd_sprom_opcode_idx_entry *entry;
1008 const struct bhnd_nvram_vardefn *var;
1009 union bhnd_nvram_sprom_storage *inp;
1010 bhnd_nvram_type var_btype;
1012 size_t ilen, ipos, iwidth;
1017 sp = (struct bhnd_nvram_sprom *)nv;
1020 BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
1022 /* Fetch canonical variable definition */
1023 var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
1024 BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
1027 * Fetch the array length from the SPROM variable definition.
1029 * This generally be identical to the array length provided by the
1030 * canonical NVRAM variable definition, but some SPROM layouts may
1031 * define a smaller element count.
1033 if ((error = bhnd_sprom_opcode_parse_var(&sp->state, entry))) {
1034 BHND_NV_LOG("variable evaluation failed: %d\n", error);
1038 nelem = sp->state.var.nelem;
1039 if (nelem > var->nelem) {
1040 BHND_NV_LOG("SPROM array element count %zu cannot be "
1041 "represented by '%s' element count of %hhu\n", nelem,
1042 var->name, var->nelem);
1046 /* Fetch the var's base element type */
1047 var_btype = bhnd_nvram_base_type(var->type);
1049 /* Calculate total byte length of the native encoding */
1050 if ((iwidth = bhnd_nvram_value_size(NULL, 0, var_btype, 1)) == 0) {
1051 /* SPROM does not use (and we do not support) decoding of
1052 * variable-width data types */
1053 BHND_NV_LOG("invalid SPROM data type: %d", var->type);
1056 ilen = nelem * iwidth;
1058 /* Decode into our caller's local storage */
1060 if (ilen > sizeof(*storage)) {
1061 BHND_NV_LOG("error decoding '%s', SPROM_ARRAY_MAXLEN "
1062 "incorrect\n", var->name);
1066 /* Zero-initialize our decode buffer; any output elements skipped
1067 * during decode should default to zero. */
1068 memset(inp, 0, ilen);
1071 * Decode the SPROM data, iteratively decoding up to nelem values.
1073 if ((error = bhnd_sprom_opcode_seek(&sp->state, entry))) {
1074 BHND_NV_LOG("variable seek failed: %d\n", error);
1080 if (var->flags & BHND_NVRAM_VF_IGNALL1)
1081 all_bits_set = true;
1083 all_bits_set = false;
1084 while ((error = bhnd_sprom_opcode_next_binding(&sp->state)) == 0) {
1085 bhnd_sprom_opcode_bind *binding;
1086 bhnd_sprom_opcode_var *binding_var;
1087 bhnd_nvram_type intv_type;
1090 uint32_t skip_in_bytes;
1094 sp->state.var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
1095 ("invalid var state"));
1096 BHND_NV_ASSERT(sp->state.var.have_bind, ("invalid bind state"));
1098 binding_var = &sp->state.var;
1099 binding = &sp->state.var.bind;
1101 if (ipos >= nelem) {
1102 BHND_NV_LOG("output skip %u positioned "
1103 "%zu beyond nelem %zu\n",
1104 binding->skip_out, ipos, nelem);
1108 /* Calculate input skip bytes for this binding */
1109 skip_in_bytes = binding->skip_in;
1110 error = bhnd_sprom_opcode_apply_scale(&sp->state,
1116 offset = sp->state.offset;
1117 for (size_t i = 0; i < binding->count; i++) {
1118 /* Read the offset value, OR'ing with the current
1120 error = bhnd_nvram_sprom_read_offset(var, sp->data,
1121 binding_var->base_type,
1129 /* If IGNALL1, record whether value does not have
1131 if (var->flags & BHND_NVRAM_VF_IGNALL1 &&
1136 all1 = binding_var->mask;
1137 if (binding_var->shift > 0)
1138 all1 >>= binding_var->shift;
1139 else if (binding_var->shift < 0)
1140 all1 <<= -binding_var->shift;
1142 if ((intv & all1) != all1)
1143 all_bits_set = false;
1146 /* Adjust input position; this was already verified to
1147 * not overflow/underflow during SPROM opcode
1149 if (binding->skip_in_negative) {
1150 offset -= skip_in_bytes;
1152 offset += skip_in_bytes;
1155 /* Skip writing to inp if additional bindings are
1156 * required to fully populate intv */
1157 if (binding->skip_out == 0)
1160 /* We use bhnd_nvram_value_coerce() to perform
1161 * overflow-checked coercion from the widened
1162 * uint32/int32 intv value to the requested output
1164 if (bhnd_nvram_is_signed_type(var_btype))
1165 intv_type = BHND_NVRAM_TYPE_INT32;
1167 intv_type = BHND_NVRAM_TYPE_UINT32;
1169 /* Calculate address of the current element output
1171 ptr = (uint8_t *)inp + (iwidth * ipos);
1173 /* Perform coercion of the array element */
1175 error = bhnd_nvram_value_coerce(&intv, sizeof(intv),
1176 intv_type, ptr, &nbyte, var_btype);
1180 /* Clear temporary state */
1183 /* Advance output position */
1184 if (SIZE_MAX - binding->skip_out < ipos) {
1185 BHND_NV_LOG("output skip %u would overflow "
1186 "%zu\n", binding->skip_out, ipos);
1190 ipos += binding->skip_out;
1194 /* Did we iterate all bindings until hitting end of the variable
1196 BHND_NV_ASSERT(error != 0, ("loop terminated early"));
1197 if (error != ENOENT) {
1201 /* If marked IGNALL1 and all bits are set, treat variable as
1203 if ((var->flags & BHND_NVRAM_VF_IGNALL1) && all_bits_set)
1206 /* Provide value wrapper */
1207 return (bhnd_nvram_val_init(val, var->fmt, inp, ilen, var->type,
1208 BHND_NVRAM_VAL_BORROW_DATA));
1213 bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
1216 struct bhnd_sprom_opcode_idx_entry *e1, *e2;
1221 /* Use the index entry order; this matches the order of variables
1222 * returned via bhnd_nvram_sprom_next() */
1232 bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
1233 size_t *len, bhnd_nvram_type otype)
1236 union bhnd_nvram_sprom_storage storage;
1239 /* Decode variable to a new value instance */
1240 error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
1244 /* Perform value coercion */
1245 error = bhnd_nvram_val_encode(&val, buf, len, otype);
1248 bhnd_nvram_val_release(&val);
1253 bhnd_nvram_sprom_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
1254 bhnd_nvram_val **value)
1257 union bhnd_nvram_sprom_storage storage;
1260 /* Decode variable to a new value instance */
1261 error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
1265 /* Attempt to copy to heap */
1266 *value = bhnd_nvram_val_copy(&val);
1267 bhnd_nvram_val_release(&val);
1276 bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
1277 size_t *len, bhnd_nvram_type *type)
1284 bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
1286 const struct bhnd_nvram_vardefn *var;
1288 BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
1290 var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
1291 BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
1297 bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
1298 bhnd_nvram_val *value, bhnd_nvram_val **result)
1300 struct bhnd_nvram_sprom *sp;
1301 const struct bhnd_nvram_vardefn *var;
1302 bhnd_sprom_opcode_idx_entry *entry;
1303 bhnd_nvram_val *spval;
1306 sp = (struct bhnd_nvram_sprom *)nv;
1308 /* Is this an externally immutable variable name? */
1309 if (bhnd_sprom_is_external_immutable(name))
1312 /* Variable must be defined in our SPROM layout */
1313 if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
1316 var = bhnd_nvram_get_vardefn(entry->vid);
1317 BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
1319 /* Value must be convertible to the native variable type */
1320 error = bhnd_nvram_val_convert_new(&spval, var->fmt, value,
1321 BHND_NVRAM_VAL_DYNAMIC);
1325 /* Value must be encodeable by our SPROM layout */
1326 error = bhnd_nvram_sprom_write_var(&sp->state, entry, spval, NULL);
1328 bhnd_nvram_val_release(spval);
1332 /* Success. Transfer our ownership of the converted value to the
1339 bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
1341 struct bhnd_nvram_sprom *sp;
1342 const struct bhnd_nvram_vardefn *var;
1343 bhnd_sprom_opcode_idx_entry *entry;
1345 sp = (struct bhnd_nvram_sprom *)nv;
1347 /* Is this an externally immutable variable name? */
1348 if (bhnd_sprom_is_external_immutable(name))
1351 /* Variable must be defined in our SPROM layout */
1352 if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
1355 var = bhnd_nvram_get_vardefn(entry->vid);
1357 /* Variable must be capable of representing a NULL/deleted value.
1359 * Since SPROM's layout is fixed, this requires IGNALL -- if
1360 * all bits are set, an IGNALL variable is treated as unset. */
1361 if (!(var->flags & BHND_NVRAM_VF_IGNALL1))
1368 * Return true if @p name represents a special immutable variable name
1369 * (e.g. sromrev) that cannot be updated in an SPROM existing image.
1371 * @param name The name to check.
1374 bhnd_sprom_is_external_immutable(const char *name)
1376 /* The layout revision is immutable and cannot be changed */
1377 if (strcmp(name, BHND_NVAR_SROMREV) == 0)