Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F147381172
D8762.id23114.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
147 KB
Referenced Files
None
Subscribers
None
D8762.id23114.diff
View Options
Index: head/sys/conf/files
===================================================================
--- head/sys/conf/files
+++ head/sys/conf/files
@@ -1236,6 +1236,7 @@
dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_data_btxt.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_data_sprom.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_data_sprom_subr.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_data_tlv.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_if.m optional bhnd
dev/bhnd/nvram/bhnd_nvram_io.c optional bhnd
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data.h
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data.h
@@ -91,6 +91,11 @@
};
const char *bhnd_nvram_data_class_desc(bhnd_nvram_data_class *cls);
+uint32_t bhnd_nvram_data_class_caps(bhnd_nvram_data_class *cls);
+
+int bhnd_nvram_data_serialize(bhnd_nvram_data_class *cls,
+ bhnd_nvram_plist *props, bhnd_nvram_plist *options,
+ void *outp, size_t *olen);
int bhnd_nvram_data_probe(bhnd_nvram_data_class *cls,
struct bhnd_nvram_io *io);
@@ -110,12 +115,6 @@
bhnd_nvram_data_class *bhnd_nvram_data_get_class(struct bhnd_nvram_data *nv);
size_t bhnd_nvram_data_count(struct bhnd_nvram_data *nv);
-int bhnd_nvram_data_size(struct bhnd_nvram_data *nv,
- size_t *size);
-
-int bhnd_nvram_data_serialize(struct bhnd_nvram_data *nv,
- void *buf, size_t *len);
-
bhnd_nvram_plist *bhnd_nvram_data_options(struct bhnd_nvram_data *nv);
uint32_t bhnd_nvram_data_caps(struct bhnd_nvram_data *nv);
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data.c
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data.c
@@ -65,6 +65,54 @@
}
/**
+ * Return the class-level capability flags (@see BHND_NVRAM_DATA_CAP_*) for
+ * of @p cls.
+ *
+ * @param cls The NVRAM class.
+ */
+uint32_t
+bhnd_nvram_data_class_caps(bhnd_nvram_data_class *cls)
+{
+ return (cls->caps);
+}
+
+/**
+ * Serialize all NVRAM properties in @p plist using @p cls's NVRAM data
+ * format, writing the result to @p outp.
+ *
+ * @param cls The NVRAM data class to be used to perform
+ * serialization.
+ * @param props The raw property values to be serialized to
+ * @p outp, in serialization order.
+ * @param options Serialization options for @p cls, or NULL.
+ * @param[out] outp On success, the serialed NVRAM data will be
+ * written to this buffer. This argment may be
+ * NULL if the value is not desired.
+ * @param[in,out] olen The capacity of @p buf. On success, will be set
+ * to the actual length of the serialized data.
+ *
+ * @retval 0 success
+ *
+ * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too
+ * small to hold the serialized data.
+ * @retval EINVAL If a property value required by @p cls is not found in
+ * @p plist.
+ * @retval EFTYPE If a property value in @p plist cannot be represented
+ * as the data type required by @p cls.
+ * @retval ERANGE If a property value in @p plist would would overflow
+ * (or underflow) the data type required by @p cls.
+ * @retval non-zero If serialization otherwise fails, a regular unix error
+ * code will be returned.
+ */
+int
+bhnd_nvram_data_serialize(bhnd_nvram_data_class *cls,
+ bhnd_nvram_plist *props, bhnd_nvram_plist *options, void *outp,
+ size_t *olen)
+{
+ return (cls->op_serialize(cls, props, options, outp, olen));
+}
+
+/**
* Probe to see if this NVRAM data class class supports the data mapped by the
* given I/O context, returning a BHND_NVRAM_DATA_PROBE probe result.
*
@@ -293,51 +341,6 @@
}
/**
- * Compute the size of the serialized form of @p nv.
- *
- * Serialization may be performed via bhnd_nvram_data_serialize().
- *
- * @param nv The NVRAM data to be queried.
- * @param[out] len On success, will be set to the computed size.
- *
- * @retval 0 success
- * @retval non-zero if computing the serialized size otherwise fails, a
- * regular unix error code will be returned.
- */
-int
-bhnd_nvram_data_size(struct bhnd_nvram_data *nv, size_t *len)
-{
- return (nv->cls->op_size(nv, len));
-}
-
-/**
- * Serialize the NVRAM data to @p buf, using the NVRAM data class' native
- * format.
- *
- * The resulting serialization may be reparsed with @p nv's BHND NVRAM data
- * class.
- *
- * @param nv The NVRAM data to be serialized.
- * @param[out] buf On success, the serialed NVRAM data will be
- * written to this buffer. This argment may be
- * NULL if the value is not desired.
- * @param[in,out] len The capacity of @p buf. On success, will be set
- * to the actual length of the serialized data.
- *
- * @retval 0 success
- * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too
- * small to hold the serialized data.
- * @retval non-zero If serialization otherwise fails, a regular unix error
- * code will be returned.
- */
-int
-bhnd_nvram_data_serialize(struct bhnd_nvram_data *nv,
- void *buf, size_t *len)
-{
- return (nv->cls->op_serialize(nv, buf, len));
-}
-
-/**
* Return the capability flags (@see BHND_NVRAM_DATA_CAP_*) for @p nv.
*
* @param nv The NVRAM data to be queried.
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c
@@ -129,7 +129,8 @@
size_t count; /**< total variable count */
};
-BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", sizeof(struct bhnd_nvram_bcm))
+BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", BHND_NVRAM_DATA_CAP_DEVPATHS,
+ sizeof(struct bhnd_nvram_bcm))
static int
bhnd_nvram_bcm_probe(struct bhnd_nvram_io *io)
@@ -146,6 +147,190 @@
return (BHND_NVRAM_DATA_PROBE_DEFAULT);
}
+static int
+bhnd_nvram_bcm_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
+ bhnd_nvram_plist *options, void *outp, size_t *olen)
+{
+ struct bhnd_nvram_bcmhdr hdr;
+ bhnd_nvram_prop *prop;
+ size_t limit, nbytes;
+ uint32_t sdram_ncdl;
+ uint16_t sdram_init, sdram_cfg, sdram_refresh;
+ uint8_t bcm_ver, crc8;
+ int error;
+
+ /* Determine output byte limit */
+ if (outp != NULL)
+ limit = *olen;
+ else
+ limit = 0;
+
+ /* Fetch required header variables */
+#define PROPS_GET_HDRVAR(_name, _dest, _type) do { \
+ const char *name = BCM_NVRAM_ ## _name ## _VAR; \
+ if (!bhnd_nvram_plist_contains(props, name)) { \
+ BHND_NV_LOG("missing required property: %s\n", \
+ name); \
+ return (EFTYPE); \
+ } \
+ \
+ error = bhnd_nvram_plist_get_encoded(props, name, \
+ (_dest), sizeof(*(_dest)), \
+ BHND_NVRAM_TYPE_ ##_type); \
+ if (error) { \
+ BHND_NV_LOG("error reading required header " \
+ "%s property: %d\n", name, error); \
+ return (EFTYPE); \
+ } \
+} while (0)
+
+ PROPS_GET_HDRVAR(SDRAM_NCDL, &sdram_ncdl, UINT32);
+ PROPS_GET_HDRVAR(CFG0_SDRAM_INIT, &sdram_init, UINT16);
+ PROPS_GET_HDRVAR(CFG1_SDRAM_CFG, &sdram_cfg, UINT16);
+ PROPS_GET_HDRVAR(CFG1_SDRAM_REFRESH, &sdram_refresh, UINT16);
+
+#undef PROPS_GET_HDRVAR
+
+ /* Fetch BCM nvram version from options */
+ if (options != NULL &&
+ bhnd_nvram_plist_contains(options, BCM_NVRAM_ENCODE_OPT_VERSION))
+ {
+ error = bhnd_nvram_plist_get_uint8(options,
+ BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver);
+ if (error) {
+ BHND_NV_LOG("error reading %s uint8 option value: %d\n",
+ BCM_NVRAM_ENCODE_OPT_VERSION, error);
+ return (EINVAL);
+ }
+ } else {
+ bcm_ver = BCM_NVRAM_CFG0_VER_DEFAULT;
+ }
+
+ /* Construct our header */
+ hdr = (struct bhnd_nvram_bcmhdr) {
+ .magic = htole32(BCM_NVRAM_MAGIC),
+ .size = 0,
+ .cfg0 = 0,
+ .cfg1 = 0,
+ .sdram_ncdl = htole32(sdram_ncdl)
+ };
+
+ hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, 0x0);
+ hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER, bcm_ver);
+ hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_SDRAM_INIT,
+ htole16(sdram_init));
+
+ hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_CFG,
+ htole16(sdram_cfg));
+ hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_REFRESH,
+ htole16(sdram_refresh));
+
+ /* Write the header */
+ nbytes = sizeof(hdr);
+ if (limit >= nbytes)
+ memcpy(outp, &hdr, sizeof(hdr));
+
+ /* Write all properties */
+ prop = NULL;
+ while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
+ const char *name;
+ char *p;
+ size_t prop_limit;
+ size_t name_len, value_len;
+
+ if (outp == NULL || limit < nbytes) {
+ p = NULL;
+ prop_limit = 0;
+ } else {
+ p = ((char *)outp) + nbytes;
+ prop_limit = limit - nbytes;
+ }
+
+ /* Fetch and write name + '=' to output */
+ name = bhnd_nvram_prop_name(prop);
+ name_len = strlen(name) + 1;
+
+ if (prop_limit > name_len) {
+ memcpy(p, name, name_len - 1);
+ p[name_len - 1] = '=';
+
+ prop_limit -= name_len;
+ p += name_len;
+ } else {
+ prop_limit = 0;
+ p = NULL;
+ }
+
+ /* Advance byte count */
+ if (SIZE_MAX - nbytes < name_len)
+ return (EFTYPE); /* would overflow size_t */
+
+ nbytes += name_len;
+
+ /* Attempt to write NUL-terminated value to output */
+ value_len = prop_limit;
+ error = bhnd_nvram_prop_encode(prop, p, &value_len,
+ BHND_NVRAM_TYPE_STRING);
+
+ /* If encoding failed for any reason other than ENOMEM (which
+ * we'll detect and report after encoding all properties),
+ * return immediately */
+ if (error && error != ENOMEM) {
+ BHND_NV_LOG("error serializing %s to required type "
+ "%s: %d\n", name,
+ bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
+ error);
+ return (error);
+ }
+
+ /* Advance byte count */
+ if (SIZE_MAX - nbytes < value_len)
+ return (EFTYPE); /* would overflow size_t */
+
+ nbytes += value_len;
+ }
+
+ /* Write terminating '\0' */
+ if (limit > nbytes)
+ *((char *)outp + nbytes) = '\0';
+
+ if (nbytes == SIZE_MAX)
+ return (EFTYPE); /* would overflow size_t */
+ else
+ nbytes++;
+
+ /* Update header length; this must fit within the header's 32-bit size
+ * field */
+ if (nbytes <= UINT32_MAX) {
+ hdr.size = (uint32_t)nbytes;
+ } else {
+ BHND_NV_LOG("size %zu exceeds maximum supported size of %u "
+ "bytes\n", nbytes, UINT32_MAX);
+ return (EFTYPE);
+ }
+
+ /* Provide required length */
+ *olen = nbytes;
+ if (limit < *olen) {
+ if (outp == NULL)
+ return (0);
+
+ return (ENOMEM);
+ }
+
+ /* Calculate the CRC value */
+ BHND_NV_ASSERT(nbytes >= BCM_NVRAM_CRC_SKIP, ("invalid output size"));
+ crc8 = bhnd_nvram_crc8((uint8_t *)outp + BCM_NVRAM_CRC_SKIP,
+ nbytes - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
+
+ /* Update CRC and write the finalized header */
+ BHND_NV_ASSERT(nbytes >= sizeof(hdr), ("invalid output size"));
+ hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, crc8);
+ memcpy(outp, &hdr, sizeof(hdr));
+
+ return (0);
+}
+
/**
* Initialize @p bcm with the provided NVRAM data mapped by @p src.
*
@@ -411,127 +596,6 @@
return (bcm->opts);
}
-static int
-bhnd_nvram_bcm_size(struct bhnd_nvram_data *nv, size_t *size)
-{
- return (bhnd_nvram_bcm_serialize(nv, NULL, size));
-}
-
-static int
-bhnd_nvram_bcm_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len)
-{
- struct bhnd_nvram_bcm *bcm;
- struct bhnd_nvram_bcmhdr hdr;
- void *cookiep;
- const char *name;
- size_t nbytes, limit;
- uint8_t crc;
- int error;
-
- bcm = (struct bhnd_nvram_bcm *)nv;
- nbytes = 0;
-
- /* Save the output buffer limit */
- if (buf == NULL)
- limit = 0;
- else
- limit = *len;
-
- /* Reserve space for the NVRAM header */
- nbytes += sizeof(struct bhnd_nvram_bcmhdr);
-
- /* Write all variables to the output buffer */
- cookiep = NULL;
- while ((name = bhnd_nvram_data_next(nv, &cookiep))) {
- uint8_t *outp;
- size_t olen;
- size_t name_len, val_len;
-
- if (limit > nbytes) {
- outp = (uint8_t *)buf + nbytes;
- olen = limit - nbytes;
- } else {
- outp = NULL;
- olen = 0;
- }
-
- /* Determine length of variable name */
- name_len = strlen(name) + 1;
-
- /* Write the variable name and '=' delimiter */
- if (olen >= name_len) {
- /* Copy name */
- memcpy(outp, name, name_len - 1);
-
- /* Append '=' */
- *(outp + name_len - 1) = '=';
- }
-
- /* Adjust byte counts */
- if (SIZE_MAX - name_len < nbytes)
- return (ERANGE);
-
- nbytes += name_len;
-
- /* Reposition output */
- if (limit > nbytes) {
- outp = (uint8_t *)buf + nbytes;
- olen = limit - nbytes;
- } else {
- outp = NULL;
- olen = 0;
- }
-
- /* Coerce to NUL-terminated C string, writing to the output
- * buffer (or just calculating the length if outp is NULL) */
- val_len = olen;
- error = bhnd_nvram_data_getvar(nv, cookiep, outp, &val_len,
- BHND_NVRAM_TYPE_STRING);
-
- if (error && error != ENOMEM)
- return (error);
-
- /* Adjust byte counts */
- if (SIZE_MAX - val_len < nbytes)
- return (ERANGE);
-
- nbytes += val_len;
- }
-
- /* Write terminating NUL */
- if (nbytes < limit)
- *((uint8_t *)buf + nbytes) = '\0';
- nbytes++;
-
- /* Provide actual size */
- *len = nbytes;
- if (buf == NULL || nbytes > limit) {
- if (buf != NULL)
- return (ENOMEM);
-
- return (0);
- }
-
- /* Fetch current NVRAM header */
- if ((error = bhnd_nvram_io_read(bcm->data, 0x0, &hdr, sizeof(hdr))))
- return (error);
-
- /* Update values covered by CRC and write to output buffer */
- hdr.size = htole32(*len);
- memcpy(buf, &hdr, sizeof(hdr));
-
- /* Calculate new CRC */
- crc = bhnd_nvram_crc8((uint8_t *)buf + BCM_NVRAM_CRC_SKIP,
- *len - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
-
- /* Update header with valid CRC */
- hdr.cfg0 &= ~BCM_NVRAM_CFG0_CRC_MASK;
- hdr.cfg0 |= (crc << BCM_NVRAM_CFG0_CRC_SHIFT);
- memcpy(buf, &hdr, sizeof(hdr));
-
- return (0);
-}
-
static uint32_t
bhnd_nvram_bcm_caps(struct bhnd_nvram_data *nv)
{
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c
@@ -72,7 +72,7 @@
};
BHND_NVRAM_DATA_CLASS_DEFN(bcmraw, "Broadcom (RAW)",
- sizeof(struct bhnd_nvram_bcmraw))
+ BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_bcmraw))
static int
bhnd_nvram_bcmraw_probe(struct bhnd_nvram_io *io)
@@ -132,6 +132,103 @@
return (BHND_NVRAM_DATA_PROBE_MAYBE + 1);
}
+static int
+bhnd_nvram_bcmraw_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
+ bhnd_nvram_plist *options, void *outp, size_t *olen)
+{
+ bhnd_nvram_prop *prop;
+ size_t limit, nbytes;
+ int error;
+
+ /* Determine output byte limit */
+ if (outp != NULL)
+ limit = *olen;
+ else
+ limit = 0;
+
+ nbytes = 0;
+
+ /* Write all properties */
+ prop = NULL;
+ while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
+ const char *name;
+ char *p;
+ size_t prop_limit;
+ size_t name_len, value_len;
+
+ if (outp == NULL || limit < nbytes) {
+ p = NULL;
+ prop_limit = 0;
+ } else {
+ p = ((char *)outp) + nbytes;
+ prop_limit = limit - nbytes;
+ }
+
+ /* Fetch and write name + '=' to output */
+ name = bhnd_nvram_prop_name(prop);
+ name_len = strlen(name) + 1;
+
+ if (prop_limit > name_len) {
+ memcpy(p, name, name_len - 1);
+ p[name_len - 1] = '=';
+
+ prop_limit -= name_len;
+ p += name_len;
+ } else {
+ prop_limit = 0;
+ p = NULL;
+ }
+
+ /* Advance byte count */
+ if (SIZE_MAX - nbytes < name_len)
+ return (EFTYPE); /* would overflow size_t */
+
+ nbytes += name_len;
+
+ /* Attempt to write NUL-terminated value to output */
+ value_len = prop_limit;
+ error = bhnd_nvram_prop_encode(prop, p, &value_len,
+ BHND_NVRAM_TYPE_STRING);
+
+ /* If encoding failed for any reason other than ENOMEM (which
+ * we'll detect and report after encoding all properties),
+ * return immediately */
+ if (error && error != ENOMEM) {
+ BHND_NV_LOG("error serializing %s to required type "
+ "%s: %d\n", name,
+ bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
+ error);
+ return (error);
+ }
+
+ /* Advance byte count */
+ if (SIZE_MAX - nbytes < value_len)
+ return (EFTYPE); /* would overflow size_t */
+
+ nbytes += value_len;
+ }
+
+ /* Write terminating '\0' */
+ if (limit > nbytes)
+ *((char *)outp + nbytes) = '\0';
+
+ if (nbytes == SIZE_MAX)
+ return (EFTYPE); /* would overflow size_t */
+ else
+ nbytes++;
+
+ /* Provide required length */
+ *olen = nbytes;
+ if (limit < *olen) {
+ if (outp == NULL)
+ return (0);
+
+ return (ENOMEM);
+ }
+
+ return (0);
+}
+
/**
* Initialize @p bcm with the provided NVRAM data mapped by @p src.
*
@@ -249,85 +346,18 @@
bhnd_nv_free(bcm->data);
}
-static size_t
-bhnd_nvram_bcmraw_count(struct bhnd_nvram_data *nv)
-{
- struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;
-
- return (bcm->count);
-}
-
static bhnd_nvram_plist *
bhnd_nvram_bcmraw_options(struct bhnd_nvram_data *nv)
{
return (NULL);
}
-static int
-bhnd_nvram_bcmraw_size(struct bhnd_nvram_data *nv, size_t *size)
-{
- return (bhnd_nvram_bcmraw_serialize(nv, NULL, size));
-}
-
-static int
-bhnd_nvram_bcmraw_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len)
+static size_t
+bhnd_nvram_bcmraw_count(struct bhnd_nvram_data *nv)
{
- struct bhnd_nvram_bcmraw *bcm;
- char * const p = (char *)buf;
- size_t limit;
- size_t offset;
-
- bcm = (struct bhnd_nvram_bcmraw *)nv;
-
- /* Save the output buffer limit */
- if (buf == NULL)
- limit = 0;
- else
- limit = *len;
-
- /* The serialized form will be exactly the length
- * of our backing buffer representation */
- *len = bcm->size;
-
- /* Skip serialization if not requested, or report ENOMEM if
- * buffer is too small */
- if (buf == NULL) {
- return (0);
- } else if (*len > limit) {
- return (ENOMEM);
- }
-
- /* Write all variables to the output buffer */
- memcpy(buf, bcm->data, *len);
-
- /* Rewrite all '\0' delimiters back to '=' */
- offset = 0;
- while (offset < bcm->size) {
- size_t name_len, value_len;
-
- name_len = strlen(p + offset);
-
- /* EOF? */
- if (name_len == 0) {
- BHND_NV_ASSERT(*(p + offset) == '\0',
- ("no NUL terminator"));
-
- offset++;
- break;
- }
-
- /* Rewrite 'name\0' to 'name=' */
- offset += name_len;
- BHND_NV_ASSERT(*(p + offset) == '\0', ("incorrect offset"));
-
- *(p + offset) = '=';
- offset++;
-
- value_len = strlen(p + offset);
- offset += value_len + 1;
- }
+ struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;
- return (0);
+ return (bcm->count);
}
static uint32_t
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h
@@ -32,9 +32,13 @@
#ifndef _BHND_NVRAM_BHND_NVRAM_BCMREG_H_
#define _BHND_NVRAM_BHND_NVRAM_BCMREG_H_
-#define BCM_NVRAM_GET_BITS(_value, _field) \
+#define BCM_NVRAM_GET_BITS(_value, _field) \
((_value & _field ## _MASK) >> _field ## _SHIFT)
+#define BCM_NVRAM_SET_BITS(_value, _field, _bits) \
+ ((_value & ~(_field ## _MASK)) | \
+ (((_bits) << _field ## _SHIFT) & _field ## _MASK))
+
/* BCM NVRAM header fields */
#define BCM_NVRAM_MAGIC 0x48534C46 /* 'FLSH' */
#define BCM_NVRAM_VERSION 1
@@ -45,6 +49,7 @@
#define BCM_NVRAM_CFG0_CRC_SHIFT 0
#define BCM_NVRAM_CFG0_VER_MASK 0x0000FF00
#define BCM_NVRAM_CFG0_VER_SHIFT 8
+#define BCM_NVRAM_CFG0_VER_DEFAULT 1 /* default version */
#define BCM_NVRAM_CFG0_SDRAM_INIT_FIELD cfg0
#define BCM_NVRAM_CFG0_SDRAM_INIT_MASK 0xFFFF0000
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c
@@ -69,7 +69,7 @@
};
BHND_NVRAM_DATA_CLASS_DEFN(btxt, "Broadcom Board Text",
- sizeof(struct bhnd_nvram_btxt))
+ BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_btxt))
/** Minimal identification header */
union bhnd_nvram_btxt_ident {
@@ -124,6 +124,100 @@
return (BHND_NVRAM_DATA_PROBE_MAYBE);
}
+static int
+bhnd_nvram_btxt_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
+ bhnd_nvram_plist *options, void *outp, size_t *olen)
+{
+ bhnd_nvram_prop *prop;
+ size_t limit, nbytes;
+ int error;
+
+ /* Determine output byte limit */
+ if (outp != NULL)
+ limit = *olen;
+ else
+ limit = 0;
+
+ nbytes = 0;
+
+ /* Write all properties */
+ prop = NULL;
+ while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
+ const char *name;
+ char *p;
+ size_t prop_limit;
+ size_t name_len, value_len;
+
+ if (outp == NULL || limit < nbytes) {
+ p = NULL;
+ prop_limit = 0;
+ } else {
+ p = ((char *)outp) + nbytes;
+ prop_limit = limit - nbytes;
+ }
+
+ /* Fetch and write 'name=' to output */
+ name = bhnd_nvram_prop_name(prop);
+ name_len = strlen(name) + 1;
+
+ if (prop_limit > name_len) {
+ memcpy(p, name, name_len - 1);
+ p[name_len - 1] = '=';
+
+ prop_limit -= name_len;
+ p += name_len;
+ } else {
+ prop_limit = 0;
+ p = NULL;
+ }
+
+ /* Advance byte count */
+ if (SIZE_MAX - nbytes < name_len)
+ return (EFTYPE); /* would overflow size_t */
+
+ nbytes += name_len;
+
+ /* Write NUL-terminated value to output, rewrite NUL as
+ * '\n' record delimiter */
+ value_len = prop_limit;
+ error = bhnd_nvram_prop_encode(prop, p, &value_len,
+ BHND_NVRAM_TYPE_STRING);
+ if (p != NULL && error == 0) {
+ /* Replace trailing '\0' with newline */
+ BHND_NV_ASSERT(value_len > 0, ("string length missing "
+ "minimum required trailing NUL"));
+
+ *(p + (value_len - 1)) = '\n';
+ } else if (error && error != ENOMEM) {
+ /* If encoding failed for any reason other than ENOMEM
+ * (which we'll detect and report after encoding all
+ * properties), return immediately */
+ BHND_NV_LOG("error serializing %s to required type "
+ "%s: %d\n", name,
+ bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
+ error);
+ return (error);
+ }
+
+ /* Advance byte count */
+ if (SIZE_MAX - nbytes < value_len)
+ return (EFTYPE); /* would overflow size_t */
+
+ nbytes += value_len;
+ }
+
+ /* Provide required length */
+ *olen = nbytes;
+ if (limit < *olen) {
+ if (outp == NULL)
+ return (0);
+
+ return (ENOMEM);
+ }
+
+ return (0);
+}
+
/**
* Initialize @p btxt with the provided board text data mapped by @p src.
*
@@ -261,52 +355,6 @@
return (NULL);
}
-static int
-bhnd_nvram_btxt_size(struct bhnd_nvram_data *nv, size_t *size)
-{
- struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv;
-
- /* The serialized form will be identical in length
- * to our backing buffer representation */
- *size = bhnd_nvram_io_getsize(btxt->data);
- return (0);
-}
-
-static int
-bhnd_nvram_btxt_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len)
-{
- struct bhnd_nvram_btxt *btxt;
- size_t limit;
- int error;
-
- btxt = (struct bhnd_nvram_btxt *)nv;
-
- limit = *len;
-
- /* Provide actual output size */
- if ((error = bhnd_nvram_data_size(nv, len)))
- return (error);
-
- if (buf == NULL) {
- return (0);
- } else if (limit < *len) {
- return (ENOMEM);
- }
-
- /* Copy our internal representation to the output buffer */
- if ((error = bhnd_nvram_io_read(btxt->data, 0x0, buf, *len)))
- return (error);
-
- /* Restore the original key=value format, rewriting all '\0'
- * key\0value delimiters back to '=' */
- for (char *p = buf; (size_t)(p - (char *)buf) < *len; p++) {
- if (*p == '\0')
- *p = '=';
- }
-
- return (0);
-}
-
static uint32_t
bhnd_nvram_btxt_caps(struct bhnd_nvram_data *nv)
{
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c
@@ -49,8 +49,9 @@
#include <string.h>
#endif /* _KERNEL */
-#include "bhnd_nvram_private.h"
+#include "bhnd_nvram_map.h"
+#include "bhnd_nvram_private.h"
#include "bhnd_nvram_datavar.h"
#include "bhnd_nvram_data_spromvar.h"
@@ -62,44 +63,45 @@
* used on Broadcom wireless and wired adapters, that provides a subset of the
* variables defined by Broadcom SoC NVRAM formats.
*/
-BHND_NVRAM_DATA_CLASS_DEFN(sprom, "Broadcom SPROM",
- sizeof(struct bhnd_nvram_sprom))
-static int sprom_sort_idx(const void *lhs, const void *rhs);
+static const bhnd_sprom_layout *bhnd_nvram_sprom_get_layout(uint8_t sromrev);
+
+static int bhnd_nvram_sprom_ident(
+ struct bhnd_nvram_io *io,
+ const bhnd_sprom_layout **ident,
+ struct bhnd_nvram_io **shadow);
+
+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);
+
+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);
+
+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);
-static int sprom_opcode_state_init(struct sprom_opcode_state *state,
- const struct bhnd_sprom_layout *layout);
-static int sprom_opcode_state_reset(struct sprom_opcode_state *state);
-static int sprom_opcode_state_seek(struct sprom_opcode_state *state,
- struct sprom_opcode_idx *indexed);
-
-static int sprom_opcode_next_var(struct sprom_opcode_state *state);
-static int sprom_opcode_parse_var(struct sprom_opcode_state *state,
- struct sprom_opcode_idx *indexed);
-
-static int sprom_opcode_next_binding(struct sprom_opcode_state *state);
-
-static int sprom_opcode_set_type(struct sprom_opcode_state *state,
- bhnd_nvram_type type);
-
-static int sprom_opcode_set_var(struct sprom_opcode_state *state,
- size_t vid);
-static int sprom_opcode_clear_var(struct sprom_opcode_state *state);
-static int sprom_opcode_flush_bind(struct sprom_opcode_state *state);
-static int sprom_opcode_read_opval32(struct sprom_opcode_state *state,
- uint8_t type, uint32_t *opval);
-static int sprom_opcode_apply_scale(struct sprom_opcode_state *state,
- uint32_t *value);
-
-static int sprom_opcode_step(struct sprom_opcode_state *state,
- uint8_t *opcode);
-
-#define SPROM_OP_BAD(_state, _fmt, ...) \
- BHND_NV_LOG("bad encoding at %td: " _fmt, \
- (_state)->input - (_state)->layout->bindings, ##__VA_ARGS__)
+static bool bhnd_sprom_is_external_immutable(
+ const char *name);
-#define SPROM_COOKIE_TO_NVRAM(_cookie) \
- bhnd_nvram_get_vardefn(((struct sprom_opcode_idx *)_cookie)->vid)
+BHND_NVRAM_DATA_CLASS_DEFN(sprom, "Broadcom SPROM",
+ BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_sprom))
+
+#define SPROM_COOKIE_TO_VID(_cookie) \
+ (((struct bhnd_sprom_opcode_idx_entry *)(_cookie))->vid)
+
+#define SPROM_COOKIE_TO_NVRAM_VAR(_cookie) \
+ bhnd_nvram_get_vardefn(SPROM_COOKIE_TO_VID(_cookie))
/**
* Read the magic value from @p io, and verify that it matches
@@ -118,7 +120,7 @@
*/
static int
bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io *io,
- const struct bhnd_sprom_layout *layout, uint16_t *magic)
+ const bhnd_sprom_layout *layout, uint16_t *magic)
{
int error;
@@ -162,7 +164,7 @@
*/
static int
bhnd_nvram_sprom_ident(struct bhnd_nvram_io *io,
- const struct bhnd_sprom_layout **ident, struct bhnd_nvram_io **shadow)
+ const bhnd_sprom_layout **ident, struct bhnd_nvram_io **shadow)
{
struct bhnd_nvram_io *buf;
uint8_t crc;
@@ -185,13 +187,13 @@
/* We iterate the SPROM layouts smallest to largest, allowing us to
* perform incremental checksum calculation */
for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
- const struct bhnd_sprom_layout *layout;
- void *ptr;
- size_t nbytes, nr;
- uint16_t magic;
- uint8_t srev;
- bool crc_valid;
- bool have_magic;
+ const bhnd_sprom_layout *layout;
+ void *ptr;
+ size_t nbytes, nr;
+ uint16_t magic;
+ uint8_t srev;
+ bool crc_valid;
+ bool have_magic;
layout = &bhnd_sprom_layouts[i];
nbytes = bhnd_nvram_io_getsize(buf);
@@ -295,9 +297,9 @@
static int
bhnd_nvram_sprom_probe(struct bhnd_nvram_io *io)
{
- const struct bhnd_sprom_layout *layout;
- struct bhnd_nvram_io *shadow;
- int error;
+ const bhnd_sprom_layout *layout;
+ struct bhnd_nvram_io *shadow;
+ int error;
/* Try to parse the input */
if ((error = bhnd_nvram_sprom_ident(io, &layout, &shadow)))
@@ -309,117 +311,435 @@
return (BHND_NVRAM_DATA_PROBE_DEFAULT);
}
+
+/**
+ * Return the SPROM layout definition for the given @p sromrev, or NULL if
+ * not found.
+ */
+static const bhnd_sprom_layout *
+bhnd_nvram_sprom_get_layout(uint8_t sromrev)
+{
+ /* Find matching SPROM layout definition */
+ for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
+ if (bhnd_sprom_layouts[i].rev == sromrev)
+ return (&bhnd_sprom_layouts[i]);
+ }
+
+ /* Not found */
+ return (NULL);
+}
+
+/**
+ * Serialize a SPROM variable.
+ *
+ * @param state The SPROM opcode state describing the layout of @p io.
+ * @param entry The variable's SPROM opcode index entry.
+ * @param value The value to encode to @p io as per @p entry.
+ * @param io I/O context to which @p value should be written, or NULL
+ * if no output should be produced. This may be used to validate
+ * values prior to write.
+ *
+ * @retval 0 success
+ * @retval EFTYPE If value coercion from @p value to the type required by
+ * @p entry is unsupported.
+ * @retval ERANGE If value coercion from @p value would overflow
+ * (or underflow) the type required by @p entry.
+ * @retval non-zero If serialization otherwise fails, a regular unix error
+ * code will be returned.
+ */
static int
-bhnd_nvram_sprom_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
+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)
{
- struct bhnd_nvram_sprom *sp;
- size_t num_vars;
+ const struct bhnd_nvram_vardefn *var;
+ uint32_t u32[BHND_SPROM_ARRAY_MAXLEN];
+ bhnd_nvram_type itype, var_base_type;
+ size_t ipos, ilen, nelem;
int error;
- sp = (struct bhnd_nvram_sprom *)nv;
+ /* Fetch variable definition and the native element type */
+ var = bhnd_nvram_get_vardefn(entry->vid);
+ BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
- /* Identify the SPROM input data */
- if ((error = bhnd_nvram_sprom_ident(io, &sp->layout, &sp->data)))
- goto failed;
+ var_base_type = bhnd_nvram_base_type(var->type);
- /* Initialize SPROM binding eval state */
- if ((error = sprom_opcode_state_init(&sp->state, sp->layout)))
- goto failed;
+ /* Fetch the element count from the SPROM variable layout definition */
+ if ((error = bhnd_sprom_opcode_parse_var(state, entry)))
+ return (error);
- /* Allocate our opcode index */
- sp->num_idx = sp->layout->num_vars;
- if ((sp->idx = bhnd_nv_calloc(sp->num_idx, sizeof(*sp->idx))) == NULL)
- goto failed;
+ nelem = state->var.nelem;
+ BHND_NV_ASSERT(nelem <= var->nelem, ("SPROM nelem=%zu exceeds maximum "
+ "NVRAM nelem=%hhu", nelem, var->nelem));
+
+ /* Promote the data to a common 32-bit representation */
+ if (bhnd_nvram_is_signed_type(var_base_type))
+ itype = BHND_NVRAM_TYPE_INT32_ARRAY;
+ else
+ itype = BHND_NVRAM_TYPE_UINT32_ARRAY;
- /* Parse out index entries from our stateful opcode stream */
- for (num_vars = 0; num_vars < sp->num_idx; num_vars++) {
- size_t opcodes;
-
- /* Seek to next entry */
- if ((error = sprom_opcode_next_var(&sp->state))) {
- SPROM_OP_BAD(&sp->state,
- "error reading expected variable entry: %d\n",
- error);
- goto failed;
- }
+ /* Calculate total size of the 32-bit promoted representation */
+ if ((ilen = bhnd_nvram_value_size(NULL, 0, itype, nelem)) == 0) {
+ /* Variable-width types are unsupported */
+ BHND_NV_LOG("invalid %s SPROM variable type %d\n",
+ var->name, var->type);
+ return (EFTYPE);
+ }
+
+ /* The native representation must fit within our scratch array */
+ if (ilen > sizeof(u32)) {
+ BHND_NV_LOG("error encoding '%s', SPROM_ARRAY_MAXLEN "
+ "incorrect\n", var->name);
+ return (EFTYPE);
+ }
- /* We limit the SPROM index representations to the minimal
- * type widths capable of covering all known layouts */
+ /* Initialize our common 32-bit value representation */
+ if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) {
+ /* No value provided; can this variable be encoded as missing
+ * by setting all bits to one? */
+ if (!(var->flags & BHND_NVRAM_VF_IGNALL1)) {
+ BHND_NV_LOG("missing required property: %s\n",
+ var->name);
+ return (EINVAL);
+ }
- /* Save SPROM image offset */
- if (sp->state.offset > UINT16_MAX) {
- SPROM_OP_BAD(&sp->state,
- "cannot index large offset %u\n", sp->state.offset);
+ /* Set all bits */
+ memset(u32, 0xFF, ilen);
+ } else {
+ bhnd_nvram_val bcm_val;
+ const void *var_ptr;
+ bhnd_nvram_type var_type, raw_type;
+ size_t var_len, enc_nelem;
+
+ /* Try to coerce the value to the native variable format. */
+ error = bhnd_nvram_val_convert_init(&bcm_val, var->fmt, value,
+ BHND_NVRAM_VAL_DYNAMIC|BHND_NVRAM_VAL_BORROW_DATA);
+ if (error) {
+ BHND_NV_LOG("error converting input type %s to %s "
+ "format\n",
+ bhnd_nvram_type_name(bhnd_nvram_val_type(value)),
+ bhnd_nvram_val_fmt_name(var->fmt));
+ return (error);
}
- sp->idx[num_vars].offset = sp->state.offset;
- /* Save current variable ID */
- if (sp->state.vid > UINT16_MAX) {
- SPROM_OP_BAD(&sp->state,
- "cannot index large vid %zu\n", sp->state.vid);
+ var_ptr = bhnd_nvram_val_bytes(&bcm_val, &var_len, &var_type);
+
+ /*
+ * Promote to a common 32-bit representation.
+ *
+ * We must use the raw type to interpret the input data as its
+ * underlying integer representation -- otherwise, coercion
+ * would attempt to parse the input as its complex
+ * representation.
+ *
+ * For example, direct CHAR -> UINT32 coercion would attempt to
+ * parse the character as a decimal integer, rather than
+ * promoting the raw UTF8 byte value to a 32-bit value.
+ */
+ raw_type = bhnd_nvram_raw_type(var_type);
+ error = bhnd_nvram_value_coerce(var_ptr, var_len, raw_type,
+ u32, &ilen, itype);
+
+ /* Clean up temporary value representation */
+ bhnd_nvram_val_release(&bcm_val);
+
+ /* Report coercion failure */
+ if (error) {
+ BHND_NV_LOG("error promoting %s to %s: %d\n",
+ bhnd_nvram_type_name(var_type),
+ bhnd_nvram_type_name(itype), error);
+ return (error);
}
- sp->idx[num_vars].vid = sp->state.vid;
- /* Save opcode position */
- opcodes = (sp->state.input - sp->layout->bindings);
- if (opcodes > UINT16_MAX) {
- SPROM_OP_BAD(&sp->state,
- "cannot index large opcode offset %zu\n", opcodes);
+ /* Encoded element count must match SPROM's definition */
+ error = bhnd_nvram_value_nelem(u32, ilen, itype, &enc_nelem);
+ if (error)
+ return (error);
+
+ if (enc_nelem != nelem) {
+ const char *type_name;
+
+ type_name = bhnd_nvram_type_name(var_base_type);
+ BHND_NV_LOG("invalid %s property value '%s[%zu]': "
+ "required %s[%zu]", var->name, type_name,
+ enc_nelem, type_name, nelem);
+ return (EFTYPE);
}
- sp->idx[num_vars].opcodes = opcodes;
}
- /* Should have reached end of binding table; next read must return
- * ENOENT */
- if ((error = sprom_opcode_next_var(&sp->state)) != ENOENT) {
- BHND_NV_LOG("expected EOF parsing binding table: %d\n", error);
- goto failed;
+ /*
+ * Seek to the start of the variable's SPROM layout definition and
+ * iterate over all bindings.
+ */
+ if ((error = bhnd_sprom_opcode_seek(state, entry))) {
+ BHND_NV_LOG("variable seek failed: %d\n", error);
+ return (error);
+ }
+
+ ipos = 0;
+ while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) {
+ bhnd_sprom_opcode_bind *binding;
+ bhnd_sprom_opcode_var *binding_var;
+ size_t offset;
+ uint32_t skip_out_bytes;
+
+ BHND_NV_ASSERT(
+ state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
+ ("invalid var state"));
+ BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state"));
+
+ binding_var = &state->var;
+ binding = &state->var.bind;
+
+ /* Calculate output skip bytes for this binding.
+ *
+ * Skip directions are defined in terms of decoding, and
+ * reversed when encoding. */
+ skip_out_bytes = binding->skip_in;
+ error = bhnd_sprom_opcode_apply_scale(state, &skip_out_bytes);
+ if (error)
+ return (error);
+
+ /* Bind */
+ offset = state->offset;
+ for (size_t i = 0; i < binding->count; i++) {
+ if (ipos >= nelem) {
+ BHND_NV_LOG("input skip %u positioned %zu "
+ "beyond nelem %zu\n", binding->skip_out,
+ ipos, nelem);
+ return (EINVAL);
+ }
+
+ /* Write next offset */
+ if (io != NULL) {
+ error = bhnd_nvram_sprom_write_offset(var, io,
+ binding_var->base_type,
+ offset,
+ binding_var->mask,
+ binding_var->shift,
+ u32[ipos]);
+ if (error)
+ return (error);
+ }
+
+ /* Adjust output position; this was already verified to
+ * not overflow/underflow during SPROM opcode
+ * evaluation */
+ if (binding->skip_in_negative) {
+ offset -= skip_out_bytes;
+ } else {
+ offset += skip_out_bytes;
+ }
+
+ /* Skip advancing input if additional bindings are
+ * required to fully encode intv */
+ if (binding->skip_out == 0)
+ continue;
+
+ /* Advance input position */
+ if (SIZE_MAX - binding->skip_out < ipos) {
+ BHND_NV_LOG("output skip %u would overflow "
+ "%zu\n", binding->skip_out, ipos);
+ return (EINVAL);
+ }
+
+ ipos += binding->skip_out;
+ }
}
- /* Sort index by variable ID, ascending */
- qsort(sp->idx, sp->num_idx, sizeof(sp->idx[0]), sprom_sort_idx);
+ /* Did we iterate all bindings until hitting end of the variable
+ * definition? */
+ BHND_NV_ASSERT(error != 0, ("loop terminated early"));
+ if (error != ENOENT)
+ return (error);
return (0);
+}
+
+static int
+bhnd_nvram_sprom_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
+ bhnd_nvram_plist *options, void *outp, size_t *olen)
+{
+ bhnd_sprom_opcode_state state;
+ struct bhnd_nvram_io *io;
+ bhnd_nvram_prop *prop;
+ bhnd_sprom_opcode_idx_entry *entry;
+ const bhnd_sprom_layout *layout;
+ size_t limit;
+ uint8_t crc;
+ uint8_t sromrev;
+ int error;
+
+ limit = *olen;
+ layout = NULL;
+ io = NULL;
+
+ /* Fetch sromrev property */
+ if (!bhnd_nvram_plist_contains(props, BHND_NVAR_SROMREV)) {
+ BHND_NV_LOG("missing required property: %s\n",
+ BHND_NVAR_SROMREV);
+ return (EINVAL);
+ }
+
+ error = bhnd_nvram_plist_get_uint8(props, BHND_NVAR_SROMREV, &sromrev);
+ if (error) {
+ BHND_NV_LOG("error reading sromrev property: %d\n", error);
+ return (EFTYPE);
+ }
+
+ /* Find SPROM layout definition */
+ if ((layout = bhnd_nvram_sprom_get_layout(sromrev)) == NULL) {
+ BHND_NV_LOG("unsupported sromrev: %hhu\n", sromrev);
+ return (EFTYPE);
+ }
+
+ /* Provide required size to caller */
+ *olen = layout->size;
+ if (outp == NULL)
+ return (0);
+ else if (limit < *olen)
+ return (ENOMEM);
+
+ /* Initialize SPROM layout interpreter */
+ if ((error = bhnd_sprom_opcode_init(&state, layout))) {
+ BHND_NV_LOG("error initializing opcode state: %d\n", error);
+ return (ENXIO);
+ }
+
+ /* Check for unsupported properties */
+ prop = NULL;
+ while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
+ const char *name;
+
+ /* Fetch the corresponding SPROM layout index entry */
+ name = bhnd_nvram_prop_name(prop);
+ entry = bhnd_sprom_opcode_index_find(&state, name);
+ if (entry == NULL) {
+ BHND_NV_LOG("property '%s' unsupported by sromrev "
+ "%hhu\n", name, layout->rev);
+ error = EINVAL;
+ goto finished;
+ }
+ }
+
+ /* Zero-initialize output */
+ memset(outp, 0, *olen);
+
+ /* Allocate wrapping I/O context for output buffer */
+ io = bhnd_nvram_ioptr_new(outp, *olen, *olen, BHND_NVRAM_IOPTR_RDWR);
+ if (io == NULL) {
+ error = ENOMEM;
+ goto finished;
+ }
+
+ /*
+ * Serialize all SPROM variable data.
+ */
+ entry = NULL;
+ while ((entry = bhnd_sprom_opcode_index_next(&state, entry)) != NULL) {
+ const struct bhnd_nvram_vardefn *var;
+ bhnd_nvram_val *val;
+
+ var = bhnd_nvram_get_vardefn(entry->vid);
+ BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
+
+ /* Fetch prop; will be NULL if unavailable */
+ prop = bhnd_nvram_plist_get_prop(props, var->name);
+ if (prop != NULL) {
+ val = bhnd_nvram_prop_val(prop);
+ } else {
+ val = BHND_NVRAM_VAL_NULL;
+ }
+
+ /* Attempt to serialize the property value to the appropriate
+ * offset within the output buffer */
+ error = bhnd_nvram_sprom_write_var(&state, entry, val, io);
+ if (error) {
+ BHND_NV_LOG("error serializing %s to required type "
+ "%s: %d\n", var->name,
+ bhnd_nvram_type_name(var->type), error);
+
+ /* ENOMEM is reserved for signaling that the output
+ * buffer capacity is insufficient */
+ if (error == ENOMEM)
+ error = EINVAL;
+
+ goto finished;
+ }
+ }
+
+ /*
+ * Write magic value, if any.
+ */
+ if (!(layout->flags & SPROM_LAYOUT_MAGIC_NONE)) {
+ uint16_t magic;
+
+ magic = htole16(layout->magic_value);
+ error = bhnd_nvram_io_write(io, layout->magic_offset, &magic,
+ sizeof(magic));
+ if (error) {
+ BHND_NV_LOG("error writing magic value: %d\n", error);
+ goto finished;
+ }
+ }
+
+ /* Calculate the CRC over all SPROM data, not including the CRC byte. */
+ crc = ~bhnd_nvram_crc8(outp, layout->crc_offset,
+ BHND_NVRAM_CRC8_INITIAL);
+
+ /* Write the checksum. */
+ error = bhnd_nvram_io_write(io, layout->crc_offset, &crc, sizeof(crc));
+ if (error) {
+ BHND_NV_LOG("error writing CRC value: %d\n", error);
+ goto finished;
+ }
-failed:
- if (sp->data != NULL)
- bhnd_nvram_io_free(sp->data);
+ /*
+ * Success!
+ */
+ error = 0;
- if (sp->idx != NULL)
- bhnd_nv_free(sp->idx);
+finished:
+ bhnd_sprom_opcode_fini(&state);
+
+ if (io != NULL)
+ bhnd_nvram_io_free(io);
return (error);
}
-/* sort function for sprom_opcode_idx values */
static int
-sprom_sort_idx(const void *lhs, const void *rhs)
+bhnd_nvram_sprom_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
{
- const struct sprom_opcode_idx *l, *r;
+ struct bhnd_nvram_sprom *sp;
+ int error;
- l = lhs;
- r = rhs;
+ sp = (struct bhnd_nvram_sprom *)nv;
+
+ /* Identify the SPROM input data */
+ if ((error = bhnd_nvram_sprom_ident(io, &sp->layout, &sp->data)))
+ goto failed;
+
+ /* Initialize SPROM binding eval state */
+ if ((error = bhnd_sprom_opcode_init(&sp->state, sp->layout)))
+ goto failed;
- if (l->vid < r->vid)
- return (-1);
- if (l->vid > r->vid)
- return (1);
return (0);
+
+failed:
+ if (sp->data != NULL)
+ bhnd_nvram_io_free(sp->data);
+
+ return (error);
}
static void
bhnd_nvram_sprom_free(struct bhnd_nvram_data *nv)
{
struct bhnd_nvram_sprom *sp = (struct bhnd_nvram_sprom *)nv;
-
- bhnd_nvram_io_free(sp->data);
- bhnd_nv_free(sp->idx);
-}
-static bhnd_nvram_plist *
-bhnd_nvram_sprom_options(struct bhnd_nvram_data *nv)
-{
- return (NULL);
+ bhnd_sprom_opcode_fini(&sp->state);
+ bhnd_nvram_io_free(sp->data);
}
size_t
@@ -429,41 +749,10 @@
return (sprom->layout->num_vars);
}
-static int
-bhnd_nvram_sprom_size(struct bhnd_nvram_data *nv, size_t *size)
-{
- struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv;
-
- /* The serialized form will be identical in length
- * to our backing buffer representation */
- *size = bhnd_nvram_io_getsize(sprom->data);
- return (0);
-}
-
-static int
-bhnd_nvram_sprom_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len)
+static bhnd_nvram_plist *
+bhnd_nvram_sprom_options(struct bhnd_nvram_data *nv)
{
- struct bhnd_nvram_sprom *sprom;
- size_t limit, req_len;
- int error;
-
- sprom = (struct bhnd_nvram_sprom *)nv;
- limit = *len;
-
- /* Provide the required size */
- if ((error = bhnd_nvram_sprom_size(nv, &req_len)))
- return (error);
-
- *len = req_len;
-
- if (buf == NULL) {
- return (0);
- } else if (*len > limit) {
- return (ENOMEM);
- }
-
- /* Write to the output buffer */
- return (bhnd_nvram_io_read(sprom->data, 0x0, buf, *len));
+ return (NULL);
}
static uint32_t
@@ -476,39 +765,17 @@
bhnd_nvram_sprom_next(struct bhnd_nvram_data *nv, void **cookiep)
{
struct bhnd_nvram_sprom *sp;
- struct sprom_opcode_idx *idx_entry;
- size_t idx_next;
+ bhnd_sprom_opcode_idx_entry *entry;
const struct bhnd_nvram_vardefn *var;
sp = (struct bhnd_nvram_sprom *)nv;
- /* Seek to appropriate starting point */
- if (*cookiep == NULL) {
- /* Start search at first index entry */
- idx_next = 0;
- } else {
- /* Determine current index position */
- idx_entry = *cookiep;
- idx_next = (size_t)(idx_entry - sp->idx);
- BHND_NV_ASSERT(idx_next < sp->num_idx,
- ("invalid index %zu; corrupt cookie?", idx_next));
-
- /* Advance to next entry */
- idx_next++;
-
- /* Check for EOF */
- if (idx_next == sp->num_idx)
- return (NULL);
- }
-
- /* Skip entries that are disabled by virtue of IGNALL1 */
- for (; idx_next < sp->num_idx; idx_next++) {
- /* Fetch index entry and update cookiep */
- idx_entry = &sp->idx[idx_next];
- *cookiep = idx_entry;
-
- /* Fetch variable definition */
- var = bhnd_nvram_get_vardefn(idx_entry->vid);
+ /* Find next index entry that is not disabled by virtue of IGNALL1 */
+ entry = *cookiep;
+ while ((entry = bhnd_sprom_opcode_index_next(&sp->state, entry))) {
+ /* Update cookiep and fetch variable definition */
+ *cookiep = entry;
+ var = SPROM_COOKIE_TO_NVRAM_VAR(*cookiep);
/* We might need to parse the variable's value to determine
* whether it should be treated as unset */
@@ -521,7 +788,6 @@
if (error) {
BHND_NV_ASSERT(error == ENOENT, ("unexpected "
"error parsing variable: %d", error));
-
continue;
}
}
@@ -534,165 +800,215 @@
return (NULL);
}
-/* bsearch function used by bhnd_nvram_sprom_find() */
-static int
-bhnd_nvram_sprom_find_vid_compare(const void *key, const void *rhs)
-{
- const struct sprom_opcode_idx *r;
- size_t l;
-
- l = *(const size_t *)key;
- r = rhs;
-
- if (l < r->vid)
- return (-1);
- if (l > r->vid)
- return (1);
- return (0);
-}
-
static void *
bhnd_nvram_sprom_find(struct bhnd_nvram_data *nv, const char *name)
{
struct bhnd_nvram_sprom *sp;
- const struct bhnd_nvram_vardefn *var;
- size_t vid;
+ bhnd_sprom_opcode_idx_entry *entry;
sp = (struct bhnd_nvram_sprom *)nv;
- /* Determine the variable ID for the given name */
- if ((var = bhnd_nvram_find_vardefn(name)) == NULL)
- return (NULL);
-
- vid = bhnd_nvram_get_vardefn_id(var);
-
- /* Search our index for the variable ID */
- return (bsearch(&vid, sp->idx, sp->num_idx, sizeof(sp->idx[0]),
- bhnd_nvram_sprom_find_vid_compare));
+ entry = bhnd_sprom_opcode_index_find(&sp->state, name);
+ return (entry);
}
/**
- * Read the value of @p type from the SPROM data at @p offset, apply @p mask
- * and @p shift, and OR with the existing @p value.
- *
- * @param sp The SPROM data instance.
- * @param var The NVRAM variable definition
- * @param type The type to read at @p offset
- * @param offset The data offset to be read.
- * @param mask The mask to be applied to the value read at @p offset.
- * @param shift The shift to be applied after masking; if positive, a right
- * shift will be applied, if negative, a left shift.
- * @param value The read destination; the parsed value will be OR'd with the
- * current contents of @p value.
- */
-static int
-bhnd_nvram_sprom_read_offset(struct bhnd_nvram_sprom *sp,
- const struct bhnd_nvram_vardefn *var, bhnd_nvram_type type,
- size_t offset, uint32_t mask, int8_t shift,
- union bhnd_nvram_sprom_intv *value)
-{
- size_t sp_width;
- int error;
- union {
- uint8_t u8;
- uint16_t u16;
- uint32_t u32;
- int8_t s8;
- int16_t s16;
- int32_t s32;
- } sp_value;
-
- /* Determine type width */
- sp_width = bhnd_nvram_type_width(type);
- if (sp_width == 0) {
- /* Variable-width types are unsupported */
- BHND_NV_LOG("invalid %s SPROM offset type %d\n", var->name,
- type);
- return (EFTYPE);
- }
-
- /* Perform read */
- error = bhnd_nvram_io_read(sp->data, offset, &sp_value,
- sp_width);
- if (error) {
- BHND_NV_LOG("error reading %s SPROM offset %#zx: %d\n",
- var->name, offset, error);
- return (EFTYPE);
- }
+ * Write @p value of @p type to the SPROM @p data at @p offset, applying
+ * @p mask and @p shift, and OR with the existing data.
+ *
+ * @param var The NVRAM variable definition.
+ * @param data The SPROM data to be modified.
+ * @param type The type to write at @p offset.
+ * @param offset The data offset to be written.
+ * @param mask The mask to be applied to @p value after shifting.
+ * @param shift The shift to be applied to @p value; if positive, a left
+ * shift will be applied, if negative, a right shift (this is the reverse of the
+ * decoding behavior)
+ * @param value The value to be written. The parsed value will be OR'd with the
+ * current contents of @p data at @p offset.
+ */
+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)
+{
+ union bhnd_nvram_sprom_storage scratch;
+ int error;
-#define NV_PARSE_INT(_type, _src, _dest, _swap) do { \
- /* Swap to host byte order */ \
- sp_value. _src = (_type) _swap(sp_value. _src); \
- \
- /* Mask and shift the value */ \
- sp_value. _src &= mask; \
- if (shift > 0) { \
- sp_value. _src >>= shift; \
- } else if (shift < 0) { \
- sp_value. _src <<= -shift; \
- } \
- \
- /* Emit output, widening to 32-bit representation */ \
- value-> _dest |= sp_value. _src; \
+#define NV_WRITE_INT(_widen, _repr, _swap) do { \
+ /* Narrow the 32-bit representation */ \
+ scratch._repr[1] = (_widen)value; \
+ \
+ /* Shift and mask the new value */ \
+ if (shift > 0) \
+ scratch._repr[1] <<= shift; \
+ else if (shift < 0) \
+ scratch._repr[1] >>= -shift; \
+ scratch._repr[1] &= mask; \
+ \
+ /* Swap to output byte order */ \
+ scratch._repr[1] = _swap(scratch._repr[1]); \
+ \
+ /* Fetch the current value */ \
+ error = bhnd_nvram_io_read(data, offset, \
+ &scratch._repr[0], sizeof(scratch._repr[0])); \
+ if (error) { \
+ BHND_NV_LOG("error reading %s SPROM offset " \
+ "%#zx: %d\n", var->name, offset, error); \
+ return (EFTYPE); \
+ } \
+ \
+ /* Mask and set our new value's bits in the current \
+ * value */ \
+ if (shift >= 0) \
+ scratch._repr[0] &= ~_swap(mask << shift); \
+ else if (shift < 0) \
+ scratch._repr[0] &= ~_swap(mask >> (-shift)); \
+ scratch._repr[0] |= scratch._repr[1]; \
+ \
+ /* Perform write */ \
+ error = bhnd_nvram_io_write(data, offset, \
+ &scratch._repr[0], sizeof(scratch._repr[0])); \
+ if (error) { \
+ BHND_NV_LOG("error writing %s SPROM offset " \
+ "%#zx: %d\n", var->name, offset, error); \
+ return (EFTYPE); \
+ } \
} while(0)
/* Apply mask/shift and widen to a common 32bit representation */
switch (type) {
case BHND_NVRAM_TYPE_UINT8:
- NV_PARSE_INT(uint8_t, u8, u32, );
+ NV_WRITE_INT(uint32_t, u8, );
break;
case BHND_NVRAM_TYPE_UINT16:
- NV_PARSE_INT(uint16_t, u16, u32, le16toh);
+ NV_WRITE_INT(uint32_t, u16, htole16);
break;
case BHND_NVRAM_TYPE_UINT32:
- NV_PARSE_INT(uint32_t, u32, u32, le32toh);
+ NV_WRITE_INT(uint32_t, u32, htole32);
break;
case BHND_NVRAM_TYPE_INT8:
- NV_PARSE_INT(int8_t, s8, s32, );
+ NV_WRITE_INT(int32_t, i8, );
break;
case BHND_NVRAM_TYPE_INT16:
- NV_PARSE_INT(int16_t, s16, s32, le16toh);
+ NV_WRITE_INT(int32_t, i16, htole16);
break;
case BHND_NVRAM_TYPE_INT32:
- NV_PARSE_INT(int32_t, s32, s32, le32toh);
+ NV_WRITE_INT(int32_t, i32, htole32);
break;
case BHND_NVRAM_TYPE_CHAR:
- NV_PARSE_INT(uint8_t, u8, u32, );
+ NV_WRITE_INT(uint32_t, u8, );
break;
-
- case BHND_NVRAM_TYPE_UINT64:
- case BHND_NVRAM_TYPE_INT64:
- case BHND_NVRAM_TYPE_STRING:
- /* fallthrough (unused by SPROM) */
default:
BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
return (EFTYPE);
}
+#undef NV_WRITE_INT
return (0);
}
/**
- * Common variable decoding; fetches and decodes variable to @p val,
- * using @p storage for actual data storage.
+ * Read the value of @p type from the SPROM @p data at @p offset, apply @p mask
+ * and @p shift, and OR with the existing @p value.
*
- * The returned @p val instance will hold a borrowed reference to @p storage,
- * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
- * the lifetime of @p storage.
- *
- * The caller is responsible for releasing any allocated value state
- * via bhnd_nvram_val_release().
- */
-static int
-bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep,
- union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
-{
+ * @param var The NVRAM variable definition.
+ * @param data The SPROM data to be decoded.
+ * @param type The type to read at @p offset
+ * @param offset The data offset to be read.
+ * @param mask The mask to be applied to the value read at @p offset.
+ * @param shift The shift to be applied after masking; if positive, a right
+ * shift will be applied, if negative, a left shift.
+ * @param value The read destination; the parsed value will be OR'd with the
+ * current contents of @p value.
+ */
+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)
+{
+ union bhnd_nvram_sprom_storage scratch;
+ int error;
+
+#define NV_PARSE_INT(_widen, _repr, _swap) do { \
+ /* Perform read */ \
+ error = bhnd_nvram_io_read(data, offset, \
+ &scratch._repr[0], sizeof(scratch._repr[0])); \
+ if (error) { \
+ BHND_NV_LOG("error reading %s SPROM offset " \
+ "%#zx: %d\n", var->name, offset, error); \
+ return (EFTYPE); \
+ } \
+ \
+ /* Swap to host byte order */ \
+ scratch._repr[0] = _swap(scratch._repr[0]); \
+ \
+ /* Mask and shift the value */ \
+ scratch._repr[0] &= mask; \
+ if (shift > 0) { \
+ scratch. _repr[0] >>= shift; \
+ } else if (shift < 0) { \
+ scratch. _repr[0] <<= -shift; \
+ } \
+ \
+ /* Widen to 32-bit representation and OR with current \
+ * value */ \
+ (*value) |= (_widen)scratch._repr[0]; \
+} while(0)
+
+ /* Apply mask/shift and widen to a common 32bit representation */
+ switch (type) {
+ case BHND_NVRAM_TYPE_UINT8:
+ NV_PARSE_INT(uint32_t, u8, );
+ break;
+ case BHND_NVRAM_TYPE_UINT16:
+ NV_PARSE_INT(uint32_t, u16, le16toh);
+ break;
+ case BHND_NVRAM_TYPE_UINT32:
+ NV_PARSE_INT(uint32_t, u32, le32toh);
+ break;
+ case BHND_NVRAM_TYPE_INT8:
+ NV_PARSE_INT(int32_t, i8, );
+ break;
+ case BHND_NVRAM_TYPE_INT16:
+ NV_PARSE_INT(int32_t, i16, le16toh);
+ break;
+ case BHND_NVRAM_TYPE_INT32:
+ NV_PARSE_INT(int32_t, i32, le32toh);
+ break;
+ case BHND_NVRAM_TYPE_CHAR:
+ NV_PARSE_INT(uint32_t, u8, );
+ break;
+ default:
+ BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
+ return (EFTYPE);
+ }
+#undef NV_PARSE_INT
+
+ return (0);
+}
+
+/**
+ * Common variable decoding; fetches and decodes variable to @p val,
+ * using @p storage for actual data storage.
+ *
+ * The returned @p val instance will hold a borrowed reference to @p storage,
+ * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
+ * the lifetime of @p storage.
+ *
+ * The caller is responsible for releasing any allocated value state
+ * via bhnd_nvram_val_release().
+ */
+static int
+bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep,
+ union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
+{
struct bhnd_nvram_sprom *sp;
- struct sprom_opcode_idx *entry;
+ bhnd_sprom_opcode_idx_entry *entry;
const struct bhnd_nvram_vardefn *var;
union bhnd_nvram_sprom_storage *inp;
bhnd_nvram_type var_btype;
- union bhnd_nvram_sprom_intv intv;
+ uint32_t intv;
size_t ilen, ipos, iwidth;
size_t nelem;
bool all_bits_set;
@@ -704,7 +1020,7 @@
BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
/* Fetch canonical variable definition */
- var = SPROM_COOKIE_TO_NVRAM(cookiep);
+ var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
/*
@@ -714,7 +1030,7 @@
* canonical NVRAM variable definition, but some SPROM layouts may
* define a smaller element count.
*/
- if ((error = sprom_opcode_parse_var(&sp->state, entry))) {
+ if ((error = bhnd_sprom_opcode_parse_var(&sp->state, entry))) {
BHND_NV_LOG("variable evaluation failed: %d\n", error);
return (error);
}
@@ -754,25 +1070,25 @@
/*
* Decode the SPROM data, iteratively decoding up to nelem values.
*/
- if ((error = sprom_opcode_state_seek(&sp->state, entry))) {
+ if ((error = bhnd_sprom_opcode_seek(&sp->state, entry))) {
BHND_NV_LOG("variable seek failed: %d\n", error);
return (error);
}
ipos = 0;
- intv.u32 = 0x0;
+ intv = 0x0;
if (var->flags & BHND_NVRAM_VF_IGNALL1)
all_bits_set = true;
else
all_bits_set = false;
- while ((error = sprom_opcode_next_binding(&sp->state)) == 0) {
- struct sprom_opcode_bind *binding;
- struct sprom_opcode_var *binding_var;
- bhnd_nvram_type intv_type;
- size_t offset;
- size_t nbyte;
- uint32_t skip_in_bytes;
- void *ptr;
+ while ((error = bhnd_sprom_opcode_next_binding(&sp->state)) == 0) {
+ bhnd_sprom_opcode_bind *binding;
+ bhnd_sprom_opcode_var *binding_var;
+ bhnd_nvram_type intv_type;
+ size_t offset;
+ size_t nbyte;
+ uint32_t skip_in_bytes;
+ void *ptr;
BHND_NV_ASSERT(
sp->state.var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
@@ -791,7 +1107,8 @@
/* Calculate input skip bytes for this binding */
skip_in_bytes = binding->skip_in;
- error = sprom_opcode_apply_scale(&sp->state, &skip_in_bytes);
+ error = bhnd_sprom_opcode_apply_scale(&sp->state,
+ &skip_in_bytes);
if (error)
return (error);
@@ -800,7 +1117,7 @@
for (size_t i = 0; i < binding->count; i++) {
/* Read the offset value, OR'ing with the current
* value of intv */
- error = bhnd_nvram_sprom_read_offset(sp, var,
+ error = bhnd_nvram_sprom_read_offset(var, sp->data,
binding_var->base_type,
offset,
binding_var->mask,
@@ -822,7 +1139,7 @@
else if (binding_var->shift < 0)
all1 <<= -binding_var->shift;
- if ((intv.u32 & all1) != all1)
+ if ((intv & all1) != all1)
all_bits_set = false;
}
@@ -855,14 +1172,13 @@
/* Perform coercion of the array element */
nbyte = iwidth;
- error = bhnd_nvram_value_coerce(&intv.u32,
- sizeof(intv.u32), intv_type, ptr, &nbyte,
- var_btype);
+ error = bhnd_nvram_value_coerce(&intv, sizeof(intv),
+ intv_type, ptr, &nbyte, var_btype);
if (error)
return (error);
/* Clear temporary state */
- intv.u32 = 0x0;
+ intv = 0x0;
/* Advance output position */
if (SIZE_MAX - binding->skip_out < ipos) {
@@ -897,7 +1213,7 @@
bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
void *cookiep2)
{
- struct sprom_opcode_idx_entry *e1, *e2;
+ struct bhnd_sprom_opcode_idx_entry *e1, *e2;
e1 = cookiep1;
e2 = cookiep2;
@@ -971,7 +1287,7 @@
BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
- var = SPROM_COOKIE_TO_NVRAM(cookiep);
+ var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
return (var->name);
@@ -981,1094 +1297,85 @@
bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
bhnd_nvram_val *value, bhnd_nvram_val **result)
{
- // XXX TODO
- return (ENXIO);
-}
-
-static int
-bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
-{
- // XXX TODO
- return (ENXIO);
-}
-
-/**
- * Initialize SPROM opcode evaluation state.
- *
- * @param state The opcode state to be initialized.
- * @param layout The SPROM layout to be parsed by this instance.
- *
- *
- * @retval 0 success
- * @retval non-zero If initialization fails, a regular unix error code will be
- * returned.
- */
-static int
-sprom_opcode_state_init(struct sprom_opcode_state *state,
- const struct bhnd_sprom_layout *layout)
-{
- memset(state, 0, sizeof(*state));
-
- state->layout = layout;
- state->input = layout->bindings;
- state->var_state = SPROM_OPCODE_VAR_STATE_NONE;
-
- bit_set(state->revs, layout->rev);
-
- return (0);
-}
-
-/**
- * Reset SPROM opcode evaluation state; future evaluation will be performed
- * starting at the first opcode.
- *
- * @param state The opcode state to be reset.
- *
- * @retval 0 success
- * @retval non-zero If reset fails, a regular unix error code will be returned.
- */
-static int
-sprom_opcode_state_reset(struct sprom_opcode_state *state)
-{
- return (sprom_opcode_state_init(state, state->layout));
-}
-
-/**
- * Reset SPROM opcode evaluation state and seek to the @p indexed position.
- *
- * @param state The opcode state to be reset.
- * @param indexed The indexed location to which we'll seek the opcode state.
- */
-static int
-sprom_opcode_state_seek(struct sprom_opcode_state *state,
- struct sprom_opcode_idx *indexed)
-{
- int error;
-
- BHND_NV_ASSERT(indexed->opcodes < state->layout->bindings_size,
- ("index entry references invalid opcode position"));
-
- /* Reset state */
- if ((error = sprom_opcode_state_reset(state)))
- return (error);
-
- /* Seek to the indexed sprom opcode offset */
- state->input = state->layout->bindings + indexed->opcodes;
-
- /* Restore the indexed sprom data offset and VID */
- state->offset = indexed->offset;
-
- /* Restore the indexed sprom variable ID */
- if ((error = sprom_opcode_set_var(state, indexed->vid)))
- return (error);
-
- return (0);
-}
-
-/**
- * Set the current revision range for @p state. This also resets
- * variable state.
- *
- * @param state The opcode state to update
- * @param start The first revision in the range.
- * @param end The last revision in the range.
- *
- * @retval 0 success
- * @retval non-zero If updating @p state fails, a regular unix error code will
- * be returned.
- */
-static inline int
-sprom_opcode_set_revs(struct sprom_opcode_state *state, uint8_t start,
- uint8_t end)
-{
- int error;
-
- /* Validate the revision range */
- if (start > SPROM_OP_REV_MAX ||
- end > SPROM_OP_REV_MAX ||
- end < start)
- {
- SPROM_OP_BAD(state, "invalid revision range: %hhu-%hhu\n",
- start, end);
- return (EINVAL);
- }
-
- /* Clear variable state */
- if ((error = sprom_opcode_clear_var(state)))
- return (error);
-
- /* Reset revision mask */
- memset(state->revs, 0x0, sizeof(state->revs));
- bit_nset(state->revs, start, end);
-
- return (0);
-}
-
-/**
- * Set the current variable's value mask for @p state.
- *
- * @param state The opcode state to update
- * @param mask The mask to be set
- *
- * @retval 0 success
- * @retval non-zero If updating @p state fails, a regular unix error code will
- * be returned.
- */
-static inline int
-sprom_opcode_set_mask(struct sprom_opcode_state *state, uint32_t mask)
-{
- if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
- SPROM_OP_BAD(state, "no open variable definition\n");
- return (EINVAL);
- }
-
- state->var.mask = mask;
- return (0);
-}
-
-/**
- * Set the current variable's value shift for @p state.
- *
- * @param state The opcode state to update
- * @param shift The shift to be set
- *
- * @retval 0 success
- * @retval non-zero If updating @p state fails, a regular unix error code will
- * be returned.
- */
-static inline int
-sprom_opcode_set_shift(struct sprom_opcode_state *state, int8_t shift)
-{
- if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
- SPROM_OP_BAD(state, "no open variable definition\n");
- return (EINVAL);
- }
-
- state->var.shift = shift;
- return (0);
-}
-
-/**
- * Register a new BIND/BINDN operation with @p state.
- *
- * @param state The opcode state to update.
- * @param count The number of elements to be bound.
- * @param skip_in The number of input elements to skip after each bind.
- * @param skip_in_negative If true, the input skip should be subtracted from
- * the current offset after each bind. If false, the input skip should be
- * added.
- * @param skip_out The number of output elements to skip after each bind.
- *
- * @retval 0 success
- * @retval EINVAL if a variable definition is not open.
- * @retval EINVAL if @p skip_in and @p count would trigger an overflow or
- * underflow when applied to the current input offset.
- * @retval ERANGE if @p skip_in would overflow uint32_t when multiplied by
- * @p count and the scale value.
- * @retval ERANGE if @p skip_out would overflow uint32_t when multiplied by
- * @p count and the scale value.
- * @retval non-zero If updating @p state otherwise fails, a regular unix error
- * code will be returned.
- */
-static inline int
-sprom_opcode_set_bind(struct sprom_opcode_state *state, uint8_t count,
- uint8_t skip_in, bool skip_in_negative, uint8_t skip_out)
-{
- uint32_t iskip_total;
- uint32_t iskip_scaled;
- int error;
-
- /* Must have an open variable */
- if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
- SPROM_OP_BAD(state, "no open variable definition\n");
- SPROM_OP_BAD(state, "BIND outside of variable definition\n");
- return (EINVAL);
- }
-
- /* Cannot overwite an existing bind definition */
- if (state->var.have_bind) {
- SPROM_OP_BAD(state, "BIND overwrites existing definition\n");
- return (EINVAL);
- }
-
- /* Must have a count of at least 1 */
- if (count == 0) {
- SPROM_OP_BAD(state, "BIND with zero count\n");
- return (EINVAL);
- }
-
- /* Scale skip_in by the current type width */
- iskip_scaled = skip_in;
- if ((error = sprom_opcode_apply_scale(state, &iskip_scaled)))
- return (error);
-
- /* Calculate total input bytes skipped: iskip_scaled * count) */
- if (iskip_scaled > 0 && UINT32_MAX / iskip_scaled < count) {
- SPROM_OP_BAD(state, "skip_in %hhu would overflow", skip_in);
- return (EINVAL);
- }
-
- iskip_total = iskip_scaled * count;
-
- /* Verify that the skip_in value won't under/overflow the current
- * input offset. */
- if (skip_in_negative) {
- if (iskip_total > state->offset) {
- SPROM_OP_BAD(state, "skip_in %hhu would underflow "
- "offset %u\n", skip_in, state->offset);
- return (EINVAL);
- }
- } else {
- if (UINT32_MAX - iskip_total < state->offset) {
- SPROM_OP_BAD(state, "skip_in %hhu would overflow "
- "offset %u\n", skip_in, state->offset);
- return (EINVAL);
- }
- }
-
- /* Set the actual count and skip values */
- state->var.have_bind = true;
- state->var.bind.count = count;
- state->var.bind.skip_in = skip_in;
- state->var.bind.skip_out = skip_out;
-
- state->var.bind.skip_in_negative = skip_in_negative;
-
- /* Update total bind count for the current variable */
- state->var.bind_total++;
-
- return (0);
-}
-
-
-/**
- * Apply and clear the current opcode bind state, if any.
- *
- * @param state The opcode state to update.
- *
- * @retval 0 success
- * @retval non-zero If updating @p state otherwise fails, a regular unix error
- * code will be returned.
- */
-static int
-sprom_opcode_flush_bind(struct sprom_opcode_state *state)
-{
- int error;
- uint32_t skip;
-
- /* Nothing to do? */
- if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN ||
- !state->var.have_bind)
- return (0);
-
- /* Apply SPROM offset adjustment */
- if (state->var.bind.count > 0) {
- skip = state->var.bind.skip_in * state->var.bind.count;
- if ((error = sprom_opcode_apply_scale(state, &skip)))
- return (error);
-
- if (state->var.bind.skip_in_negative) {
- state->offset -= skip;
- } else {
- state->offset += skip;
- }
- }
-
- /* Clear bind state */
- memset(&state->var.bind, 0, sizeof(state->var.bind));
- state->var.have_bind = false;
-
- return (0);
-}
-
-/**
- * Set the current type to @p type, and reset type-specific
- * stream state.
- *
- * @param state The opcode state to update.
- * @param type The new type.
- *
- * @retval 0 success
- * @retval EINVAL if @p vid is not a valid variable ID.
- */
-static int
-sprom_opcode_set_type(struct sprom_opcode_state *state, bhnd_nvram_type type)
-{
- bhnd_nvram_type base_type;
- size_t width;
- uint32_t mask;
-
- /* Must have an open variable definition */
- if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
- SPROM_OP_BAD(state, "type set outside variable definition\n");
- return (EINVAL);
- }
-
- /* Fetch type width for use as our scale value */
- width = bhnd_nvram_type_width(type);
- if (width == 0) {
- SPROM_OP_BAD(state, "unsupported variable-width type: %d\n",
- type);
- return (EINVAL);
- } else if (width > UINT32_MAX) {
- SPROM_OP_BAD(state, "invalid type width %zu for type: %d\n",
- width, type);
- return (EINVAL);
- }
-
- /* Determine default mask value for the element type */
- base_type = bhnd_nvram_base_type(type);
- switch (base_type) {
- case BHND_NVRAM_TYPE_UINT8:
- case BHND_NVRAM_TYPE_INT8:
- case BHND_NVRAM_TYPE_CHAR:
- mask = UINT8_MAX;
- break;
- case BHND_NVRAM_TYPE_UINT16:
- case BHND_NVRAM_TYPE_INT16:
- mask = UINT16_MAX;
- break;
- case BHND_NVRAM_TYPE_UINT32:
- case BHND_NVRAM_TYPE_INT32:
- mask = UINT32_MAX;
- break;
- case BHND_NVRAM_TYPE_STRING:
- /* fallthrough (unused by SPROM) */
- default:
- SPROM_OP_BAD(state, "unsupported type: %d\n", type);
- return (EINVAL);
- }
-
- /* Update state */
- state->var.base_type = base_type;
- state->var.mask = mask;
- state->var.scale = (uint32_t)width;
-
- return (0);
-}
-
-/**
- * Clear current variable state, if any.
- *
- * @param state The opcode state to update.
- */
-static int
-sprom_opcode_clear_var(struct sprom_opcode_state *state)
-{
- if (state->var_state == SPROM_OPCODE_VAR_STATE_NONE)
- return (0);
-
- BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
- ("incomplete variable definition"));
- BHND_NV_ASSERT(!state->var.have_bind, ("stale bind state"));
-
- memset(&state->var, 0, sizeof(state->var));
- state->var_state = SPROM_OPCODE_VAR_STATE_NONE;
-
- return (0);
-}
-
-/**
- * Set the current variable's array element count to @p nelem.
- *
- * @param state The opcode state to update.
- * @param nelem The new array length.
- *
- * @retval 0 success
- * @retval EINVAL if no open variable definition exists.
- * @retval EINVAL if @p nelem is zero.
- * @retval ENXIO if @p nelem is greater than one, and the current variable does
- * not have an array type.
- * @retval ENXIO if @p nelem exceeds the array length of the NVRAM variable
- * definition.
- */
-static int
-sprom_opcode_set_nelem(struct sprom_opcode_state *state, uint8_t nelem)
-{
- const struct bhnd_nvram_vardefn *var;
-
- /* Must have a defined variable */
- if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
- SPROM_OP_BAD(state, "array length set without open variable "
- "state");
- return (EINVAL);
- }
-
- /* Locate the actual variable definition */
- if ((var = bhnd_nvram_get_vardefn(state->vid)) == NULL) {
- SPROM_OP_BAD(state, "unknown variable ID: %zu\n", state->vid);
- return (EINVAL);
- }
-
- /* Must be greater than zero */
- if (nelem == 0) {
- SPROM_OP_BAD(state, "invalid nelem: %hhu\n", nelem);
- return (EINVAL);
- }
-
- /* If the variable is not an array-typed value, the array length
- * must be 1 */
- if (!bhnd_nvram_is_array_type(var->type) && nelem != 1) {
- SPROM_OP_BAD(state, "nelem %hhu on non-array %zu\n", nelem,
- state->vid);
- return (ENXIO);
- }
-
- /* Cannot exceed the variable's defined array length */
- if (nelem > var->nelem) {
- SPROM_OP_BAD(state, "nelem %hhu exceeds %zu length %hhu\n",
- nelem, state->vid, var->nelem);
- return (ENXIO);
- }
-
- /* Valid length; update state */
- state->var.nelem = nelem;
-
- return (0);
-}
-
-/**
- * Set the current variable ID to @p vid, and reset variable-specific
- * stream state.
- *
- * @param state The opcode state to update.
- * @param vid The new variable ID.
- *
- * @retval 0 success
- * @retval EINVAL if @p vid is not a valid variable ID.
- */
-static int
-sprom_opcode_set_var(struct sprom_opcode_state *state, size_t vid)
-{
+ struct bhnd_nvram_sprom *sp;
const struct bhnd_nvram_vardefn *var;
+ bhnd_sprom_opcode_idx_entry *entry;
+ bhnd_nvram_val *spval;
int error;
- BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_NONE,
- ("overwrite of open variable definition"));
+ sp = (struct bhnd_nvram_sprom *)nv;
- /* Locate the variable definition */
- if ((var = bhnd_nvram_get_vardefn(vid)) == NULL) {
- SPROM_OP_BAD(state, "unknown variable ID: %zu\n", vid);
+ /* Is this an externally immutable variable name? */
+ if (bhnd_sprom_is_external_immutable(name))
return (EINVAL);
- }
- /* Update vid and var state */
- state->vid = vid;
- state->var_state = SPROM_OPCODE_VAR_STATE_OPEN;
+ /* Variable must be defined in our SPROM layout */
+ if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
+ return (ENOENT);
- /* Initialize default variable record values */
- memset(&state->var, 0x0, sizeof(state->var));
+ var = bhnd_nvram_get_vardefn(entry->vid);
+ BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
- /* Set initial base type */
- if ((error = sprom_opcode_set_type(state, var->type)))
+ /* Value must be convertible to the native variable type */
+ error = bhnd_nvram_val_convert_new(&spval, var->fmt, value,
+ BHND_NVRAM_VAL_DYNAMIC);
+ if (error)
return (error);
- /* Set default array length */
- if ((error = sprom_opcode_set_nelem(state, var->nelem)))
+ /* Value must be encodeable by our SPROM layout */
+ error = bhnd_nvram_sprom_write_var(&sp->state, entry, spval, NULL);
+ if (error) {
+ bhnd_nvram_val_release(spval);
return (error);
-
- return (0);
-}
-
-/**
- * Mark the currently open variable definition as complete.
- *
- * @param state The opcode state to update.
- *
- * @retval 0 success
- * @retval EINVAL if no incomplete open variable definition exists.
- */
-static int
-sprom_opcode_end_var(struct sprom_opcode_state *state)
-{
- if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
- SPROM_OP_BAD(state, "no open variable definition\n");
- return (EINVAL);
- }
-
- state->var_state = SPROM_OPCODE_VAR_STATE_DONE;
- return (0);
-}
-
-/**
- * Apply the current scale to @p value.
- *
- * @param state The SPROM opcode state.
- * @param[in,out] value The value to scale
- *
- * @retval 0 success
- * @retval EINVAL if no open variable definition exists.
- * @retval EINVAL if applying the current scale would overflow.
- */
-static int
-sprom_opcode_apply_scale(struct sprom_opcode_state *state, uint32_t *value)
-{
- /* Must have a defined variable (and thus, scale) */
- if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
- SPROM_OP_BAD(state, "scaled value encoded without open "
- "variable state");
- return (EINVAL);
}
- /* Applying the scale value must not overflow */
- if (UINT32_MAX / state->var.scale < *value) {
- SPROM_OP_BAD(state, "cannot represent %" PRIu32 " * %" PRIu32
- "\n", *value, state->var.scale);
- return (EINVAL);
- }
-
- *value = (*value) * state->var.scale;
+ /* Success. Transfer our ownership of the converted value to the
+ * caller */
+ *result = spval;
return (0);
}
-/**
- * Read a SPROM_OP_DATA_* value from @p opcodes.
- *
- * @param state The SPROM opcode state.
- * @param type The SROM_OP_DATA_* type to be read.
- * @param opval On success, the 32bit data representation. If @p type is signed,
- * the value will be appropriately sign extended and may be directly cast to
- * int32_t.
- *
- * @retval 0 success
- * @retval non-zero If reading the value otherwise fails, a regular unix error
- * code will be returned.
- */
static int
-sprom_opcode_read_opval32(struct sprom_opcode_state *state, uint8_t type,
- uint32_t *opval)
+bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
{
- const uint8_t *p;
- int error;
-
- p = state->input;
- switch (type) {
- case SPROM_OP_DATA_I8:
- /* Convert to signed value first, then sign extend */
- *opval = (int32_t)(int8_t)(*p);
- p += 1;
- break;
- case SPROM_OP_DATA_U8:
- *opval = *p;
- p += 1;
- break;
- case SPROM_OP_DATA_U8_SCALED:
- *opval = *p;
+ struct bhnd_nvram_sprom *sp;
+ const struct bhnd_nvram_vardefn *var;
+ bhnd_sprom_opcode_idx_entry *entry;
- if ((error = sprom_opcode_apply_scale(state, opval)))
- return (error);
+ sp = (struct bhnd_nvram_sprom *)nv;
- p += 1;
- break;
- case SPROM_OP_DATA_U16:
- *opval = le16dec(p);
- p += 2;
- break;
- case SPROM_OP_DATA_U32:
- *opval = le32dec(p);
- p += 4;
- break;
- default:
- SPROM_OP_BAD(state, "unsupported data type: %hhu\n", type);
+ /* Is this an externally immutable variable name? */
+ if (bhnd_sprom_is_external_immutable(name))
return (EINVAL);
- }
-
- /* Update read address */
- state->input = p;
-
- return (0);
-}
-
-/**
- * Return true if our layout revision is currently defined by the SPROM
- * opcode state.
- *
- * This may be used to test whether the current opcode stream state applies
- * to the layout that we are actually parsing.
- *
- * A given opcode stream may cover multiple layout revisions, switching
- * between them prior to defining a set of variables.
- */
-static inline bool
-sprom_opcode_matches_layout_rev(struct sprom_opcode_state *state)
-{
- return (bit_test(state->revs, state->layout->rev));
-}
-
-/**
- * When evaluating @p state and @p opcode, rewrite @p opcode and the current
- * evaluation state, as required.
- *
- * If @p opcode is rewritten, it should be returned from
- * sprom_opcode_step() instead of the opcode parsed from @p state's opcode
- * stream.
- *
- * If @p opcode remains unmodified, then sprom_opcode_step() should proceed
- * to standard evaluation.
- */
-static int
-sprom_opcode_rewrite_opcode(struct sprom_opcode_state *state, uint8_t *opcode)
-{
- uint8_t op;
- int error;
-
- op = SPROM_OPCODE_OP(*opcode);
- switch (state->var_state) {
- case SPROM_OPCODE_VAR_STATE_NONE:
- /* No open variable definition */
- return (0);
-
- case SPROM_OPCODE_VAR_STATE_OPEN:
- /* Open variable definition; check for implicit closure. */
- /*
- * If a variable definition contains no explicit bind
- * instructions prior to closure, we must generate a DO_BIND
- * instruction with count and skip values of 1.
- */
- if (SPROM_OP_IS_VAR_END(op) &&
- state->var.bind_total == 0)
- {
- uint8_t count, skip_in, skip_out;
- bool skip_in_negative;
-
- /* Create bind with skip_in/skip_out of 1, count of 1 */
- count = 1;
- skip_in = 1;
- skip_out = 1;
- skip_in_negative = false;
-
- error = sprom_opcode_set_bind(state, count, skip_in,
- skip_in_negative, skip_out);
- if (error)
- return (error);
-
- /* Return DO_BIND */
- *opcode = SPROM_OPCODE_DO_BIND |
- (0 << SPROM_OP_BIND_SKIP_IN_SIGN) |
- (1 << SPROM_OP_BIND_SKIP_IN_SHIFT) |
- (1 << SPROM_OP_BIND_SKIP_OUT_SHIFT);
-
- return (0);
- }
-
- /*
- * If a variable is implicitly closed (e.g. by a new variable
- * definition), we must generate a VAR_END instruction.
- */
- if (SPROM_OP_IS_IMPLICIT_VAR_END(op)) {
- /* Mark as complete */
- if ((error = sprom_opcode_end_var(state)))
- return (error);
-
- /* Return VAR_END */
- *opcode = SPROM_OPCODE_VAR_END;
- return (0);
- }
- break;
+ /* Variable must be defined in our SPROM layout */
+ if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
+ return (ENOENT);
+ var = bhnd_nvram_get_vardefn(entry->vid);
- case SPROM_OPCODE_VAR_STATE_DONE:
- /* Previously completed variable definition. Discard variable
- * state */
- return (sprom_opcode_clear_var(state));
- }
+ /* Variable must be capable of representing a NULL/deleted value.
+ *
+ * Since SPROM's layout is fixed, this requires IGNALL -- if
+ * all bits are set, an IGNALL variable is treated as unset. */
+ if (!(var->flags & BHND_NVRAM_VF_IGNALL1))
+ return (EINVAL);
- /* Nothing to do */
return (0);
}
/**
- * Evaluate one opcode from @p state.
- *
- * @param state The opcode state to be evaluated.
- * @param[out] opcode On success, the evaluated opcode
- *
- * @retval 0 success
- * @retval ENOENT if EOF is reached
- * @retval non-zero if evaluation otherwise fails, a regular unix error
- * code will be returned.
- */
-static int
-sprom_opcode_step(struct sprom_opcode_state *state, uint8_t *opcode)
-{
- int error;
-
- while (*state->input != SPROM_OPCODE_EOF) {
- uint32_t val;
- uint8_t op, rewrite, immd;
-
- /* Fetch opcode */
- *opcode = *state->input;
- op = SPROM_OPCODE_OP(*opcode);
- immd = SPROM_OPCODE_IMM(*opcode);
-
- /* Clear any existing bind state */
- if ((error = sprom_opcode_flush_bind(state)))
- return (error);
-
- /* Insert local opcode based on current state? */
- rewrite = *opcode;
- if ((error = sprom_opcode_rewrite_opcode(state, &rewrite)))
- return (error);
-
- if (rewrite != *opcode) {
- /* Provide rewritten opcode */
- *opcode = rewrite;
-
- /* We must keep evaluating until we hit a state
- * applicable to the SPROM revision we're parsing */
- if (!sprom_opcode_matches_layout_rev(state))
- continue;
-
- return (0);
- }
-
- /* Advance input */
- state->input++;
-
- switch (op) {
- case SPROM_OPCODE_VAR_IMM:
- if ((error = sprom_opcode_set_var(state, immd)))
- return (error);
- break;
-
- case SPROM_OPCODE_VAR_REL_IMM:
- error = sprom_opcode_set_var(state, state->vid + immd);
- if (error)
- return (error);
- break;
-
- case SPROM_OPCODE_VAR:
- error = sprom_opcode_read_opval32(state, immd, &val);
- if (error)
- return (error);
-
- if ((error = sprom_opcode_set_var(state, val)))
- return (error);
-
- break;
-
- case SPROM_OPCODE_VAR_END:
- if ((error = sprom_opcode_end_var(state)))
- return (error);
- break;
-
- case SPROM_OPCODE_NELEM:
- immd = *state->input;
- if ((error = sprom_opcode_set_nelem(state, immd)))
- return (error);
-
- state->input++;
- break;
-
- case SPROM_OPCODE_DO_BIND:
- case SPROM_OPCODE_DO_BINDN: {
- uint8_t count, skip_in, skip_out;
- bool skip_in_negative;
-
- /* Fetch skip arguments */
- skip_in = (immd & SPROM_OP_BIND_SKIP_IN_MASK) >>
- SPROM_OP_BIND_SKIP_IN_SHIFT;
-
- skip_in_negative =
- ((immd & SPROM_OP_BIND_SKIP_IN_SIGN) != 0);
-
- skip_out = (immd & SPROM_OP_BIND_SKIP_OUT_MASK) >>
- SPROM_OP_BIND_SKIP_OUT_SHIFT;
-
- /* Fetch count argument (if any) */
- if (op == SPROM_OPCODE_DO_BINDN) {
- /* Count is provided as trailing U8 */
- count = *state->input;
- state->input++;
- } else {
- count = 1;
- }
-
- /* Set BIND state */
- error = sprom_opcode_set_bind(state, count, skip_in,
- skip_in_negative, skip_out);
- if (error)
- return (error);
-
- break;
- }
- case SPROM_OPCODE_DO_BINDN_IMM: {
- uint8_t count, skip_in, skip_out;
- bool skip_in_negative;
-
- /* Implicit skip_in/skip_out of 1, count encoded as immd
- * value */
- count = immd;
- skip_in = 1;
- skip_out = 1;
- skip_in_negative = false;
-
- error = sprom_opcode_set_bind(state, count, skip_in,
- skip_in_negative, skip_out);
- if (error)
- return (error);
- break;
- }
-
- case SPROM_OPCODE_REV_IMM:
- if ((error = sprom_opcode_set_revs(state, immd, immd)))
- return (error);
- break;
-
- case SPROM_OPCODE_REV_RANGE: {
- uint8_t range;
- uint8_t rstart, rend;
-
- /* Revision range is encoded in next byte, as
- * { uint8_t start:4, uint8_t end:4 } */
- range = *state->input;
- rstart = (range & SPROM_OP_REV_START_MASK) >>
- SPROM_OP_REV_START_SHIFT;
- rend = (range & SPROM_OP_REV_END_MASK) >>
- SPROM_OP_REV_END_SHIFT;
-
- /* Update revision bitmask */
- error = sprom_opcode_set_revs(state, rstart, rend);
- if (error)
- return (error);
-
- /* Advance input */
- state->input++;
- break;
- }
- case SPROM_OPCODE_MASK_IMM:
- if ((error = sprom_opcode_set_mask(state, immd)))
- return (error);
- break;
-
- case SPROM_OPCODE_MASK:
- error = sprom_opcode_read_opval32(state, immd, &val);
- if (error)
- return (error);
-
- if ((error = sprom_opcode_set_mask(state, val)))
- return (error);
- break;
-
- case SPROM_OPCODE_SHIFT_IMM:
- if ((error = sprom_opcode_set_shift(state, immd * 2)))
- return (error);
- break;
-
- case SPROM_OPCODE_SHIFT: {
- int8_t shift;
-
- if (immd == SPROM_OP_DATA_I8) {
- shift = (int8_t)(*state->input);
- } else if (immd == SPROM_OP_DATA_U8) {
- val = *state->input;
- if (val > INT8_MAX) {
- SPROM_OP_BAD(state, "invalid shift "
- "value: %#x\n", val);
- }
-
- shift = val;
- } else {
- SPROM_OP_BAD(state, "unsupported shift data "
- "type: %#hhx\n", immd);
- return (EINVAL);
- }
-
- if ((error = sprom_opcode_set_shift(state, shift)))
- return (error);
-
- state->input++;
- break;
- }
- case SPROM_OPCODE_OFFSET_REL_IMM:
- /* Fetch unscaled relative offset */
- val = immd;
-
- /* Apply scale */
- if ((error = sprom_opcode_apply_scale(state, &val)))
- return (error);
-
- /* Adding val must not overflow our offset */
- if (UINT32_MAX - state->offset < val) {
- BHND_NV_LOG("offset out of range\n");
- return (EINVAL);
- }
-
- /* Adjust offset */
- state->offset += val;
- break;
- case SPROM_OPCODE_OFFSET:
- error = sprom_opcode_read_opval32(state, immd, &val);
- if (error)
- return (error);
-
- state->offset = val;
- break;
-
- case SPROM_OPCODE_TYPE:
- /* Type follows as U8 */
- immd = *state->input;
- state->input++;
-
- /* fall through */
- case SPROM_OPCODE_TYPE_IMM:
- switch (immd) {
- case BHND_NVRAM_TYPE_UINT8:
- case BHND_NVRAM_TYPE_UINT16:
- case BHND_NVRAM_TYPE_UINT32:
- case BHND_NVRAM_TYPE_UINT64:
- case BHND_NVRAM_TYPE_INT8:
- case BHND_NVRAM_TYPE_INT16:
- case BHND_NVRAM_TYPE_INT32:
- case BHND_NVRAM_TYPE_INT64:
- case BHND_NVRAM_TYPE_CHAR:
- case BHND_NVRAM_TYPE_STRING:
- error = sprom_opcode_set_type(state,
- (bhnd_nvram_type)immd);
- if (error)
- return (error);
- break;
- default:
- BHND_NV_LOG("unrecognized type %#hhx\n", immd);
- return (EINVAL);
- }
- break;
-
- default:
- BHND_NV_LOG("unrecognized opcode %#hhx\n", *opcode);
- return (EINVAL);
- }
-
- /* We must keep evaluating until we hit a state applicable to
- * the SPROM revision we're parsing */
- if (sprom_opcode_matches_layout_rev(state))
- return (0);
- }
-
- /* End of opcode stream */
- return (ENOENT);
-}
-
-/**
- * Reset SPROM opcode evaluation state, seek to the @p indexed position,
- * and perform complete evaluation of the variable's opcodes.
+ * Return true if @p name represents a special immutable variable name
+ * (e.g. sromrev) that cannot be updated in an SPROM existing image.
*
- * @param state The opcode state to be to be evaluated.
- * @param indexed The indexed variable location.
- *
- * @retval 0 success
- * @retval non-zero If evaluation fails, a regular unix error code will be
- * returned.
+ * @param name The name to check.
*/
-static int
-sprom_opcode_parse_var(struct sprom_opcode_state *state,
- struct sprom_opcode_idx *indexed)
+static bool
+bhnd_sprom_is_external_immutable(const char *name)
{
- uint8_t opcode;
- int error;
-
- /* Seek to entry */
- if ((error = sprom_opcode_state_seek(state, indexed)))
- return (error);
+ /* The layout revision is immutable and cannot be changed */
+ if (strcmp(name, BHND_NVAR_SROMREV) == 0)
+ return (true);
- /* Parse full variable definition */
- while ((error = sprom_opcode_step(state, &opcode)) == 0) {
- /* Iterate until VAR_END */
- if (SPROM_OPCODE_OP(opcode) != SPROM_OPCODE_VAR_END)
- continue;
-
- BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
- ("incomplete variable definition"));
-
- return (0);
- }
-
- /* Error parsing definition */
- return (error);
-}
-
-/**
- * Evaluate @p state until the next variable definition is found.
- *
- * @param state The opcode state to be evaluated.
- *
- * @retval 0 success
- * @retval ENOENT if no additional variable definitions are available.
- * @retval non-zero if evaluation otherwise fails, a regular unix error
- * code will be returned.
- */
-static int
-sprom_opcode_next_var(struct sprom_opcode_state *state)
-{
- uint8_t opcode;
- int error;
-
- /* Step until we hit a variable opcode */
- while ((error = sprom_opcode_step(state, &opcode)) == 0) {
- switch (SPROM_OPCODE_OP(opcode)) {
- case SPROM_OPCODE_VAR:
- case SPROM_OPCODE_VAR_IMM:
- case SPROM_OPCODE_VAR_REL_IMM:
- BHND_NV_ASSERT(
- state->var_state == SPROM_OPCODE_VAR_STATE_OPEN,
- ("missing variable definition"));
-
- return (0);
- default:
- continue;
- }
- }
-
- /* Reached EOF, or evaluation failed */
- return (error);
-}
-
-/**
- * Evaluate @p state until the next binding for the current variable definition
- * is found.
- *
- * @param state The opcode state to be evaluated.
- *
- * @retval 0 success
- * @retval ENOENT if no additional binding opcodes are found prior to reaching
- * a new variable definition, or the end of @p state's binding opcodes.
- * @retval non-zero if evaluation otherwise fails, a regular unix error
- * code will be returned.
- */
-static int
-sprom_opcode_next_binding(struct sprom_opcode_state *state)
-{
- uint8_t opcode;
- int error;
-
- if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN)
- return (EINVAL);
-
- /* Step until we hit a bind opcode, or a new variable */
- while ((error = sprom_opcode_step(state, &opcode)) == 0) {
- switch (SPROM_OPCODE_OP(opcode)) {
- case SPROM_OPCODE_DO_BIND:
- case SPROM_OPCODE_DO_BINDN:
- case SPROM_OPCODE_DO_BINDN_IMM:
- /* Found next bind */
- BHND_NV_ASSERT(
- state->var_state == SPROM_OPCODE_VAR_STATE_OPEN,
- ("missing variable definition"));
- BHND_NV_ASSERT(state->var.have_bind, ("missing bind"));
-
- return (0);
-
- case SPROM_OPCODE_VAR_END:
- /* No further binding opcodes */
- BHND_NV_ASSERT(
- state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
- ("variable definition still available"));
- return (ENOENT);
- }
- }
-
- /* Not found, or evaluation failed */
- return (error);
+ return (false);
}
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom_subr.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom_subr.c
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom_subr.c
@@ -0,0 +1,1366 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/endian.h>
+
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <machine/_inttypes.h>
+#else /* !_KERNEL */
+#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#endif /* _KERNEL */
+
+#include "bhnd_nvram_private.h"
+#include "bhnd_nvram_data_spromvar.h"
+
+static int bhnd_sprom_opcode_sort_idx(const void *lhs, const void *rhs);
+static int bhnd_nvram_opcode_idx_vid_compare(const void *key,
+ const void *rhs);
+
+static int bhnd_sprom_opcode_reset(bhnd_sprom_opcode_state *state);
+static int bhnd_sprom_opcode_next_var(bhnd_sprom_opcode_state *state);
+
+static int bhnd_sprom_opcode_set_type(bhnd_sprom_opcode_state *state,
+ bhnd_nvram_type type);
+
+static int bhnd_sprom_opcode_set_var(bhnd_sprom_opcode_state *state,
+ size_t vid);
+static int bhnd_sprom_opcode_clear_var(bhnd_sprom_opcode_state *state);
+
+static int bhnd_sprom_opcode_flush_bind(bhnd_sprom_opcode_state *state);
+
+static int bhnd_sprom_opcode_read_opval32(bhnd_sprom_opcode_state *state,
+ uint8_t type, uint32_t *opval);
+
+static int bhnd_sprom_opcode_step(bhnd_sprom_opcode_state *state,
+ uint8_t *opcode);
+
+#define SPROM_OP_BAD(_state, _fmt, ...) \
+ BHND_NV_LOG("bad encoding at %td: " _fmt, \
+ (_state)->input - (_state)->layout->bindings, ##__VA_ARGS__)
+
+/**
+ * Initialize SPROM opcode evaluation state.
+ *
+ * @param state The opcode state to be initialized.
+ * @param layout The SPROM layout to be parsed by this instance.
+ *
+ *
+ * @retval 0 success
+ * @retval non-zero If initialization fails, a regular unix error code will be
+ * returned.
+ */
+int
+bhnd_sprom_opcode_init(bhnd_sprom_opcode_state *state,
+ const struct bhnd_sprom_layout *layout)
+{
+ bhnd_sprom_opcode_idx_entry *idx;
+ size_t num_vars, num_idx;
+ int error;
+
+ idx = NULL;
+
+ state->layout = layout;
+ state->idx = NULL;
+ state->num_idx = 0;
+
+ /* Initialize interpretation state */
+ if ((error = bhnd_sprom_opcode_reset(state)))
+ return (error);
+
+ /* Allocate and populate our opcode index */
+ num_idx = state->layout->num_vars;
+ idx = bhnd_nv_calloc(num_idx, sizeof(*idx));
+ if (idx == NULL)
+ return (ENOMEM);
+
+ for (num_vars = 0; num_vars < num_idx; num_vars++) {
+ size_t opcodes;
+
+ /* Seek to next entry */
+ if ((error = bhnd_sprom_opcode_next_var(state))) {
+ SPROM_OP_BAD(state, "error reading expected variable "
+ "entry: %d\n", error);
+ bhnd_nv_free(idx);
+ return (error);
+ }
+
+ /* We limit the SPROM index representations to the minimal
+ * type widths capable of covering all known layouts */
+
+ /* Save SPROM image offset */
+ if (state->offset > UINT16_MAX) {
+ SPROM_OP_BAD(state, "cannot index large offset %u\n",
+ state->offset);
+ bhnd_nv_free(idx);
+ return (ENXIO);
+ }
+ idx[num_vars].offset = state->offset;
+
+ /* Save current variable ID */
+ if (state->vid > UINT16_MAX) {
+ SPROM_OP_BAD(state, "cannot index large vid %zu\n",
+ state->vid);
+ bhnd_nv_free(idx);
+ return (ENXIO);
+ }
+ idx[num_vars].vid = state->vid;
+
+ /* Save opcode position */
+ opcodes = (state->input - state->layout->bindings);
+ if (opcodes > UINT16_MAX) {
+ SPROM_OP_BAD(state, "cannot index large opcode offset "
+ "%zu\n", opcodes);
+ bhnd_nv_free(idx);
+ return (ENXIO);
+ }
+ idx[num_vars].opcodes = opcodes;
+ }
+
+ /* Should have reached end of binding table; next read must return
+ * ENOENT */
+ if ((error = bhnd_sprom_opcode_next_var(state)) != ENOENT) {
+ BHND_NV_LOG("expected EOF parsing binding table: %d\n", error);
+ bhnd_nv_free(idx);
+ return (ENXIO);
+ }
+
+ /* Reset interpretation state */
+ if ((error = bhnd_sprom_opcode_reset(state))) {
+ bhnd_nv_free(idx);
+ return (error);
+ }
+
+ /* Make index available to opcode state evaluation */
+ qsort(idx, num_idx, sizeof(idx[0]), bhnd_sprom_opcode_sort_idx);
+
+ state->idx = idx;
+ state->num_idx = num_idx;
+
+ return (0);
+}
+
+/**
+ * Reset SPROM opcode evaluation state; future evaluation will be performed
+ * starting at the first opcode.
+ *
+ * @param state The opcode state to be reset.
+ *
+ * @retval 0 success
+ * @retval non-zero If reset fails, a regular unix error code will be returned.
+ */
+static int
+bhnd_sprom_opcode_reset(bhnd_sprom_opcode_state *state)
+{
+ memset(&state->var, 0, sizeof(state->var));
+
+ state->input = state->layout->bindings;
+ state->offset = 0;
+ state->vid = 0;
+ state->var_state = SPROM_OPCODE_VAR_STATE_NONE;
+ bit_set(state->revs, state->layout->rev);
+
+ return (0);
+}
+
+/**
+ * Free any resources associated with @p state.
+ *
+ * @param state An opcode state previously successfully initialized with
+ * bhnd_sprom_opcode_init().
+ */
+void
+bhnd_sprom_opcode_fini(bhnd_sprom_opcode_state *state)
+{
+ bhnd_nv_free(state->idx);
+}
+
+
+/**
+ * Sort function used to prepare our index for querying; sorts
+ * bhnd_sprom_opcode_idx_entry values by variable ID, ascending.
+ */
+static int
+bhnd_sprom_opcode_sort_idx(const void *lhs, const void *rhs)
+{
+ const bhnd_sprom_opcode_idx_entry *l, *r;
+
+ l = lhs;
+ r = rhs;
+
+ if (l->vid < r->vid)
+ return (-1);
+ if (l->vid > r->vid)
+ return (1);
+ return (0);
+}
+
+/**
+ * Binary search comparison function used by bhnd_sprom_opcode_index_find();
+ * searches bhnd_sprom_opcode_idx_entry values by variable ID, ascending.
+ */
+static int
+bhnd_nvram_opcode_idx_vid_compare(const void *key, const void *rhs)
+{
+ const bhnd_sprom_opcode_idx_entry *entry;
+ size_t vid;
+
+ vid = *(const size_t *)key;
+ entry = rhs;
+
+ if (vid < entry->vid)
+ return (-1);
+ if (vid > entry->vid)
+ return (1);
+
+ return (0);
+}
+
+/**
+ * Locate an index entry for the variable with @p name, or NULL if not found.
+ *
+ * @param state The opcode state to be queried.
+ * @param name The name to search for.
+ *
+ * @retval non-NULL If @p name is found, its index entry value will be
+ * returned.
+ * @retval NULL If @p name is not found.
+ */
+bhnd_sprom_opcode_idx_entry *
+bhnd_sprom_opcode_index_find(bhnd_sprom_opcode_state *state, const char *name)
+{
+ const struct bhnd_nvram_vardefn *var;
+ size_t vid;
+
+ /* Determine the variable ID for the given name */
+ if ((var = bhnd_nvram_find_vardefn(name)) == NULL)
+ return (NULL);
+
+ vid = bhnd_nvram_get_vardefn_id(var);
+
+ /* Search our index for the variable ID */
+ return (bsearch(&vid, state->idx, state->num_idx, sizeof(state->idx[0]),
+ bhnd_nvram_opcode_idx_vid_compare));
+}
+
+
+/**
+ * Iterate over all index entries in @p state.
+ *
+ * @param state The opcode state to be iterated.
+ * @param[in,out] prev An entry previously returned by
+ * bhnd_sprom_opcode_index_next(), or a NULL value
+ * to begin iteration.
+ *
+ * @return Returns the next index entry name, or NULL if all entries have
+ * been iterated.
+ */
+bhnd_sprom_opcode_idx_entry *
+bhnd_sprom_opcode_index_next(bhnd_sprom_opcode_state *state,
+ bhnd_sprom_opcode_idx_entry *prev)
+{
+ size_t idxpos;
+
+ /* Get next index position */
+ if (prev == NULL) {
+ idxpos = 0;
+ } else {
+ /* Determine current position */
+ idxpos = (size_t)(prev - state->idx);
+ BHND_NV_ASSERT(idxpos < state->num_idx,
+ ("invalid index %zu", idxpos));
+
+ /* Advance to next entry */
+ idxpos++;
+ }
+
+ /* Check for EOF */
+ if (idxpos == state->num_idx)
+ return (NULL);
+
+ return (&state->idx[idxpos]);
+}
+
+/**
+ * Reset SPROM opcode evaluation state and seek to the @p entry's position.
+ *
+ * @param state The opcode state to be reset.
+ * @param entry The indexed entry to which we'll seek the opcode state.
+ */
+int
+bhnd_sprom_opcode_seek(bhnd_sprom_opcode_state *state,
+ bhnd_sprom_opcode_idx_entry *entry)
+{
+ int error;
+
+ BHND_NV_ASSERT(entry->opcodes < state->layout->bindings_size,
+ ("index entry references invalid opcode position"));
+
+ /* Reset state */
+ if ((error = bhnd_sprom_opcode_reset(state)))
+ return (error);
+
+ /* Seek to the indexed sprom opcode offset */
+ state->input = state->layout->bindings + entry->opcodes;
+
+ /* Restore the indexed sprom data offset and VID */
+ state->offset = entry->offset;
+
+ /* Restore the indexed sprom variable ID */
+ if ((error = bhnd_sprom_opcode_set_var(state, entry->vid)))
+ return (error);
+
+ return (0);
+}
+
+/**
+ * Set the current revision range for @p state. This also resets
+ * variable state.
+ *
+ * @param state The opcode state to update
+ * @param start The first revision in the range.
+ * @param end The last revision in the range.
+ *
+ * @retval 0 success
+ * @retval non-zero If updating @p state fails, a regular unix error code will
+ * be returned.
+ */
+static inline int
+bhnd_sprom_opcode_set_revs(bhnd_sprom_opcode_state *state, uint8_t start,
+ uint8_t end)
+{
+ int error;
+
+ /* Validate the revision range */
+ if (start > SPROM_OP_REV_MAX ||
+ end > SPROM_OP_REV_MAX ||
+ end < start)
+ {
+ SPROM_OP_BAD(state, "invalid revision range: %hhu-%hhu\n",
+ start, end);
+ return (EINVAL);
+ }
+
+ /* Clear variable state */
+ if ((error = bhnd_sprom_opcode_clear_var(state)))
+ return (error);
+
+ /* Reset revision mask */
+ memset(state->revs, 0x0, sizeof(state->revs));
+ bit_nset(state->revs, start, end);
+
+ return (0);
+}
+
+/**
+ * Set the current variable's value mask for @p state.
+ *
+ * @param state The opcode state to update
+ * @param mask The mask to be set
+ *
+ * @retval 0 success
+ * @retval non-zero If updating @p state fails, a regular unix error code will
+ * be returned.
+ */
+static inline int
+bhnd_sprom_opcode_set_mask(bhnd_sprom_opcode_state *state, uint32_t mask)
+{
+ if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
+ SPROM_OP_BAD(state, "no open variable definition\n");
+ return (EINVAL);
+ }
+
+ state->var.mask = mask;
+ return (0);
+}
+
+/**
+ * Set the current variable's value shift for @p state.
+ *
+ * @param state The opcode state to update
+ * @param shift The shift to be set
+ *
+ * @retval 0 success
+ * @retval non-zero If updating @p state fails, a regular unix error code will
+ * be returned.
+ */
+static inline int
+bhnd_sprom_opcode_set_shift(bhnd_sprom_opcode_state *state, int8_t shift)
+{
+ if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
+ SPROM_OP_BAD(state, "no open variable definition\n");
+ return (EINVAL);
+ }
+
+ state->var.shift = shift;
+ return (0);
+}
+
+/**
+ * Register a new BIND/BINDN operation with @p state.
+ *
+ * @param state The opcode state to update.
+ * @param count The number of elements to be bound.
+ * @param skip_in The number of input elements to skip after each bind.
+ * @param skip_in_negative If true, the input skip should be subtracted from
+ * the current offset after each bind. If false, the input skip should be
+ * added.
+ * @param skip_out The number of output elements to skip after each bind.
+ *
+ * @retval 0 success
+ * @retval EINVAL if a variable definition is not open.
+ * @retval EINVAL if @p skip_in and @p count would trigger an overflow or
+ * underflow when applied to the current input offset.
+ * @retval ERANGE if @p skip_in would overflow uint32_t when multiplied by
+ * @p count and the scale value.
+ * @retval ERANGE if @p skip_out would overflow uint32_t when multiplied by
+ * @p count and the scale value.
+ * @retval non-zero If updating @p state otherwise fails, a regular unix error
+ * code will be returned.
+ */
+static inline int
+bhnd_sprom_opcode_set_bind(bhnd_sprom_opcode_state *state, uint8_t count,
+ uint8_t skip_in, bool skip_in_negative, uint8_t skip_out)
+{
+ uint32_t iskip_total;
+ uint32_t iskip_scaled;
+ int error;
+
+ /* Must have an open variable */
+ if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
+ SPROM_OP_BAD(state, "no open variable definition\n");
+ SPROM_OP_BAD(state, "BIND outside of variable definition\n");
+ return (EINVAL);
+ }
+
+ /* Cannot overwite an existing bind definition */
+ if (state->var.have_bind) {
+ SPROM_OP_BAD(state, "BIND overwrites existing definition\n");
+ return (EINVAL);
+ }
+
+ /* Must have a count of at least 1 */
+ if (count == 0) {
+ SPROM_OP_BAD(state, "BIND with zero count\n");
+ return (EINVAL);
+ }
+
+ /* Scale skip_in by the current type width */
+ iskip_scaled = skip_in;
+ if ((error = bhnd_sprom_opcode_apply_scale(state, &iskip_scaled)))
+ return (error);
+
+ /* Calculate total input bytes skipped: iskip_scaled * count) */
+ if (iskip_scaled > 0 && UINT32_MAX / iskip_scaled < count) {
+ SPROM_OP_BAD(state, "skip_in %hhu would overflow", skip_in);
+ return (EINVAL);
+ }
+
+ iskip_total = iskip_scaled * count;
+
+ /* Verify that the skip_in value won't under/overflow the current
+ * input offset. */
+ if (skip_in_negative) {
+ if (iskip_total > state->offset) {
+ SPROM_OP_BAD(state, "skip_in %hhu would underflow "
+ "offset %u\n", skip_in, state->offset);
+ return (EINVAL);
+ }
+ } else {
+ if (UINT32_MAX - iskip_total < state->offset) {
+ SPROM_OP_BAD(state, "skip_in %hhu would overflow "
+ "offset %u\n", skip_in, state->offset);
+ return (EINVAL);
+ }
+ }
+
+ /* Set the actual count and skip values */
+ state->var.have_bind = true;
+ state->var.bind.count = count;
+ state->var.bind.skip_in = skip_in;
+ state->var.bind.skip_out = skip_out;
+
+ state->var.bind.skip_in_negative = skip_in_negative;
+
+ /* Update total bind count for the current variable */
+ state->var.bind_total++;
+
+ return (0);
+}
+
+
+/**
+ * Apply and clear the current opcode bind state, if any.
+ *
+ * @param state The opcode state to update.
+ *
+ * @retval 0 success
+ * @retval non-zero If updating @p state otherwise fails, a regular unix error
+ * code will be returned.
+ */
+static int
+bhnd_sprom_opcode_flush_bind(bhnd_sprom_opcode_state *state)
+{
+ int error;
+ uint32_t skip;
+
+ /* Nothing to do? */
+ if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN ||
+ !state->var.have_bind)
+ return (0);
+
+ /* Apply SPROM offset adjustment */
+ if (state->var.bind.count > 0) {
+ skip = state->var.bind.skip_in * state->var.bind.count;
+ if ((error = bhnd_sprom_opcode_apply_scale(state, &skip)))
+ return (error);
+
+ if (state->var.bind.skip_in_negative) {
+ state->offset -= skip;
+ } else {
+ state->offset += skip;
+ }
+ }
+
+ /* Clear bind state */
+ memset(&state->var.bind, 0, sizeof(state->var.bind));
+ state->var.have_bind = false;
+
+ return (0);
+}
+
+/**
+ * Set the current type to @p type, and reset type-specific
+ * stream state.
+ *
+ * @param state The opcode state to update.
+ * @param type The new type.
+ *
+ * @retval 0 success
+ * @retval EINVAL if @p vid is not a valid variable ID.
+ */
+static int
+bhnd_sprom_opcode_set_type(bhnd_sprom_opcode_state *state, bhnd_nvram_type type)
+{
+ bhnd_nvram_type base_type;
+ size_t width;
+ uint32_t mask;
+
+ /* Must have an open variable definition */
+ if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
+ SPROM_OP_BAD(state, "type set outside variable definition\n");
+ return (EINVAL);
+ }
+
+ /* Fetch type width for use as our scale value */
+ width = bhnd_nvram_type_width(type);
+ if (width == 0) {
+ SPROM_OP_BAD(state, "unsupported variable-width type: %d\n",
+ type);
+ return (EINVAL);
+ } else if (width > UINT32_MAX) {
+ SPROM_OP_BAD(state, "invalid type width %zu for type: %d\n",
+ width, type);
+ return (EINVAL);
+ }
+
+ /* Determine default mask value for the element type */
+ base_type = bhnd_nvram_base_type(type);
+ switch (base_type) {
+ case BHND_NVRAM_TYPE_UINT8:
+ case BHND_NVRAM_TYPE_INT8:
+ case BHND_NVRAM_TYPE_CHAR:
+ mask = UINT8_MAX;
+ break;
+ case BHND_NVRAM_TYPE_UINT16:
+ case BHND_NVRAM_TYPE_INT16:
+ mask = UINT16_MAX;
+ break;
+ case BHND_NVRAM_TYPE_UINT32:
+ case BHND_NVRAM_TYPE_INT32:
+ mask = UINT32_MAX;
+ break;
+ case BHND_NVRAM_TYPE_STRING:
+ /* fallthrough (unused by SPROM) */
+ default:
+ SPROM_OP_BAD(state, "unsupported type: %d\n", type);
+ return (EINVAL);
+ }
+
+ /* Update state */
+ state->var.base_type = base_type;
+ state->var.mask = mask;
+ state->var.scale = (uint32_t)width;
+
+ return (0);
+}
+
+/**
+ * Clear current variable state, if any.
+ *
+ * @param state The opcode state to update.
+ */
+static int
+bhnd_sprom_opcode_clear_var(bhnd_sprom_opcode_state *state)
+{
+ if (state->var_state == SPROM_OPCODE_VAR_STATE_NONE)
+ return (0);
+
+ BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
+ ("incomplete variable definition"));
+ BHND_NV_ASSERT(!state->var.have_bind, ("stale bind state"));
+
+ memset(&state->var, 0, sizeof(state->var));
+ state->var_state = SPROM_OPCODE_VAR_STATE_NONE;
+
+ return (0);
+}
+
+/**
+ * Set the current variable's array element count to @p nelem.
+ *
+ * @param state The opcode state to update.
+ * @param nelem The new array length.
+ *
+ * @retval 0 success
+ * @retval EINVAL if no open variable definition exists.
+ * @retval EINVAL if @p nelem is zero.
+ * @retval ENXIO if @p nelem is greater than one, and the current variable does
+ * not have an array type.
+ * @retval ENXIO if @p nelem exceeds the array length of the NVRAM variable
+ * definition.
+ */
+static int
+bhnd_sprom_opcode_set_nelem(bhnd_sprom_opcode_state *state, uint8_t nelem)
+{
+ const struct bhnd_nvram_vardefn *var;
+
+ /* Must have a defined variable */
+ if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
+ SPROM_OP_BAD(state, "array length set without open variable "
+ "state");
+ return (EINVAL);
+ }
+
+ /* Locate the actual variable definition */
+ if ((var = bhnd_nvram_get_vardefn(state->vid)) == NULL) {
+ SPROM_OP_BAD(state, "unknown variable ID: %zu\n", state->vid);
+ return (EINVAL);
+ }
+
+ /* Must be greater than zero */
+ if (nelem == 0) {
+ SPROM_OP_BAD(state, "invalid nelem: %hhu\n", nelem);
+ return (EINVAL);
+ }
+
+ /* If the variable is not an array-typed value, the array length
+ * must be 1 */
+ if (!bhnd_nvram_is_array_type(var->type) && nelem != 1) {
+ SPROM_OP_BAD(state, "nelem %hhu on non-array %zu\n", nelem,
+ state->vid);
+ return (ENXIO);
+ }
+
+ /* Cannot exceed the variable's defined array length */
+ if (nelem > var->nelem) {
+ SPROM_OP_BAD(state, "nelem %hhu exceeds %zu length %hhu\n",
+ nelem, state->vid, var->nelem);
+ return (ENXIO);
+ }
+
+ /* Valid length; update state */
+ state->var.nelem = nelem;
+
+ return (0);
+}
+
+/**
+ * Set the current variable ID to @p vid, and reset variable-specific
+ * stream state.
+ *
+ * @param state The opcode state to update.
+ * @param vid The new variable ID.
+ *
+ * @retval 0 success
+ * @retval EINVAL if @p vid is not a valid variable ID.
+ */
+static int
+bhnd_sprom_opcode_set_var(bhnd_sprom_opcode_state *state, size_t vid)
+{
+ const struct bhnd_nvram_vardefn *var;
+ int error;
+
+ BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_NONE,
+ ("overwrite of open variable definition"));
+
+ /* Locate the variable definition */
+ if ((var = bhnd_nvram_get_vardefn(vid)) == NULL) {
+ SPROM_OP_BAD(state, "unknown variable ID: %zu\n", vid);
+ return (EINVAL);
+ }
+
+ /* Update vid and var state */
+ state->vid = vid;
+ state->var_state = SPROM_OPCODE_VAR_STATE_OPEN;
+
+ /* Initialize default variable record values */
+ memset(&state->var, 0x0, sizeof(state->var));
+
+ /* Set initial base type */
+ if ((error = bhnd_sprom_opcode_set_type(state, var->type)))
+ return (error);
+
+ /* Set default array length */
+ if ((error = bhnd_sprom_opcode_set_nelem(state, var->nelem)))
+ return (error);
+
+ return (0);
+}
+
+/**
+ * Mark the currently open variable definition as complete.
+ *
+ * @param state The opcode state to update.
+ *
+ * @retval 0 success
+ * @retval EINVAL if no incomplete open variable definition exists.
+ */
+static int
+bhnd_sprom_opcode_end_var(bhnd_sprom_opcode_state *state)
+{
+ if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
+ SPROM_OP_BAD(state, "no open variable definition\n");
+ return (EINVAL);
+ }
+
+ state->var_state = SPROM_OPCODE_VAR_STATE_DONE;
+ return (0);
+}
+
+/**
+ * Apply the current scale to @p value.
+ *
+ * @param state The SPROM opcode state.
+ * @param[in,out] value The value to scale
+ *
+ * @retval 0 success
+ * @retval EINVAL if no open variable definition exists.
+ * @retval EINVAL if applying the current scale would overflow.
+ */
+int
+bhnd_sprom_opcode_apply_scale(bhnd_sprom_opcode_state *state, uint32_t *value)
+{
+ /* Must have a defined variable (and thus, scale) */
+ if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) {
+ SPROM_OP_BAD(state, "scaled value encoded without open "
+ "variable state");
+ return (EINVAL);
+ }
+
+ /* Applying the scale value must not overflow */
+ if (UINT32_MAX / state->var.scale < *value) {
+ SPROM_OP_BAD(state, "cannot represent %" PRIu32 " * %" PRIu32
+ "\n", *value, state->var.scale);
+ return (EINVAL);
+ }
+
+ *value = (*value) * state->var.scale;
+ return (0);
+}
+
+/**
+ * Read a SPROM_OP_DATA_* value from @p opcodes.
+ *
+ * @param state The SPROM opcode state.
+ * @param type The SROM_OP_DATA_* type to be read.
+ * @param opval On success, the 32bit data representation. If @p type is signed,
+ * the value will be appropriately sign extended and may be directly cast to
+ * int32_t.
+ *
+ * @retval 0 success
+ * @retval non-zero If reading the value otherwise fails, a regular unix error
+ * code will be returned.
+ */
+static int
+bhnd_sprom_opcode_read_opval32(bhnd_sprom_opcode_state *state, uint8_t type,
+ uint32_t *opval)
+{
+ const uint8_t *p;
+ int error;
+
+ p = state->input;
+ switch (type) {
+ case SPROM_OP_DATA_I8:
+ /* Convert to signed value first, then sign extend */
+ *opval = (int32_t)(int8_t)(*p);
+ p += 1;
+ break;
+ case SPROM_OP_DATA_U8:
+ *opval = *p;
+ p += 1;
+ break;
+ case SPROM_OP_DATA_U8_SCALED:
+ *opval = *p;
+
+ if ((error = bhnd_sprom_opcode_apply_scale(state, opval)))
+ return (error);
+
+ p += 1;
+ break;
+ case SPROM_OP_DATA_U16:
+ *opval = le16dec(p);
+ p += 2;
+ break;
+ case SPROM_OP_DATA_U32:
+ *opval = le32dec(p);
+ p += 4;
+ break;
+ default:
+ SPROM_OP_BAD(state, "unsupported data type: %hhu\n", type);
+ return (EINVAL);
+ }
+
+ /* Update read address */
+ state->input = p;
+
+ return (0);
+}
+
+/**
+ * Return true if our layout revision is currently defined by the SPROM
+ * opcode state.
+ *
+ * This may be used to test whether the current opcode stream state applies
+ * to the layout that we are actually parsing.
+ *
+ * A given opcode stream may cover multiple layout revisions, switching
+ * between them prior to defining a set of variables.
+ */
+static inline bool
+bhnd_sprom_opcode_matches_layout_rev(bhnd_sprom_opcode_state *state)
+{
+ return (bit_test(state->revs, state->layout->rev));
+}
+
+/**
+ * When evaluating @p state and @p opcode, rewrite @p opcode based on the
+ * current evaluation state.
+ *
+ * This allows the insertion of implicit opcodes into interpretation of the
+ * opcode stream.
+ *
+ * If @p opcode is rewritten, it should be returned from
+ * bhnd_sprom_opcode_step() instead of the opcode parsed from @p state's opcode
+ * stream.
+ *
+ * If @p opcode remains unmodified, then bhnd_sprom_opcode_step() should
+ * proceed to standard evaluation.
+ */
+static int
+bhnd_sprom_opcode_rewrite_opcode(bhnd_sprom_opcode_state *state,
+ uint8_t *opcode)
+{
+ uint8_t op;
+ int error;
+
+ op = SPROM_OPCODE_OP(*opcode);
+ switch (state->var_state) {
+ case SPROM_OPCODE_VAR_STATE_NONE:
+ /* No open variable definition */
+ return (0);
+
+ case SPROM_OPCODE_VAR_STATE_OPEN:
+ /* Open variable definition; check for implicit closure. */
+
+ /*
+ * If a variable definition contains no explicit bind
+ * instructions prior to closure, we must generate a DO_BIND
+ * instruction with count and skip values of 1.
+ */
+ if (SPROM_OP_IS_VAR_END(op) &&
+ state->var.bind_total == 0)
+ {
+ uint8_t count, skip_in, skip_out;
+ bool skip_in_negative;
+
+ /* Create bind with skip_in/skip_out of 1, count of 1 */
+ count = 1;
+ skip_in = 1;
+ skip_out = 1;
+ skip_in_negative = false;
+
+ error = bhnd_sprom_opcode_set_bind(state, count,
+ skip_in, skip_in_negative, skip_out);
+ if (error)
+ return (error);
+
+ /* Return DO_BIND */
+ *opcode = SPROM_OPCODE_DO_BIND |
+ (0 << SPROM_OP_BIND_SKIP_IN_SIGN) |
+ (1 << SPROM_OP_BIND_SKIP_IN_SHIFT) |
+ (1 << SPROM_OP_BIND_SKIP_OUT_SHIFT);
+
+ return (0);
+ }
+
+ /*
+ * If a variable is implicitly closed (e.g. by a new variable
+ * definition), we must generate a VAR_END instruction.
+ */
+ if (SPROM_OP_IS_IMPLICIT_VAR_END(op)) {
+ /* Mark as complete */
+ if ((error = bhnd_sprom_opcode_end_var(state)))
+ return (error);
+
+ /* Return VAR_END */
+ *opcode = SPROM_OPCODE_VAR_END;
+ return (0);
+ }
+ break;
+
+
+ case SPROM_OPCODE_VAR_STATE_DONE:
+ /* Previously completed variable definition. Discard variable
+ * state */
+ return (bhnd_sprom_opcode_clear_var(state));
+ }
+
+ /* Nothing to do */
+ return (0);
+}
+
+/**
+ * Evaluate one opcode from @p state.
+ *
+ * @param state The opcode state to be evaluated.
+ * @param[out] opcode On success, the evaluated opcode
+ *
+ * @retval 0 success
+ * @retval ENOENT if EOF is reached
+ * @retval non-zero if evaluation otherwise fails, a regular unix error
+ * code will be returned.
+ */
+static int
+bhnd_sprom_opcode_step(bhnd_sprom_opcode_state *state, uint8_t *opcode)
+{
+ int error;
+
+ while (*state->input != SPROM_OPCODE_EOF) {
+ uint32_t val;
+ uint8_t op, rewrite, immd;
+
+ /* Fetch opcode */
+ *opcode = *state->input;
+ op = SPROM_OPCODE_OP(*opcode);
+ immd = SPROM_OPCODE_IMM(*opcode);
+
+ /* Clear any existing bind state */
+ if ((error = bhnd_sprom_opcode_flush_bind(state)))
+ return (error);
+
+ /* Insert local opcode based on current state? */
+ rewrite = *opcode;
+ if ((error = bhnd_sprom_opcode_rewrite_opcode(state, &rewrite)))
+ return (error);
+
+ if (rewrite != *opcode) {
+ /* Provide rewritten opcode */
+ *opcode = rewrite;
+
+ /* We must keep evaluating until we hit a state
+ * applicable to the SPROM revision we're parsing */
+ if (!bhnd_sprom_opcode_matches_layout_rev(state))
+ continue;
+
+ return (0);
+ }
+
+ /* Advance input */
+ state->input++;
+
+ switch (op) {
+ case SPROM_OPCODE_VAR_IMM:
+ if ((error = bhnd_sprom_opcode_set_var(state, immd)))
+ return (error);
+ break;
+
+ case SPROM_OPCODE_VAR_REL_IMM:
+ error = bhnd_sprom_opcode_set_var(state,
+ state->vid + immd);
+ if (error)
+ return (error);
+ break;
+
+ case SPROM_OPCODE_VAR:
+ error = bhnd_sprom_opcode_read_opval32(state, immd,
+ &val);
+ if (error)
+ return (error);
+
+ if ((error = bhnd_sprom_opcode_set_var(state, val)))
+ return (error);
+
+ break;
+
+ case SPROM_OPCODE_VAR_END:
+ if ((error = bhnd_sprom_opcode_end_var(state)))
+ return (error);
+ break;
+
+ case SPROM_OPCODE_NELEM:
+ immd = *state->input;
+ if ((error = bhnd_sprom_opcode_set_nelem(state, immd)))
+ return (error);
+
+ state->input++;
+ break;
+
+ case SPROM_OPCODE_DO_BIND:
+ case SPROM_OPCODE_DO_BINDN: {
+ uint8_t count, skip_in, skip_out;
+ bool skip_in_negative;
+
+ /* Fetch skip arguments */
+ skip_in = (immd & SPROM_OP_BIND_SKIP_IN_MASK) >>
+ SPROM_OP_BIND_SKIP_IN_SHIFT;
+
+ skip_in_negative =
+ ((immd & SPROM_OP_BIND_SKIP_IN_SIGN) != 0);
+
+ skip_out = (immd & SPROM_OP_BIND_SKIP_OUT_MASK) >>
+ SPROM_OP_BIND_SKIP_OUT_SHIFT;
+
+ /* Fetch count argument (if any) */
+ if (op == SPROM_OPCODE_DO_BINDN) {
+ /* Count is provided as trailing U8 */
+ count = *state->input;
+ state->input++;
+ } else {
+ count = 1;
+ }
+
+ /* Set BIND state */
+ error = bhnd_sprom_opcode_set_bind(state, count,
+ skip_in, skip_in_negative, skip_out);
+ if (error)
+ return (error);
+
+ break;
+ }
+ case SPROM_OPCODE_DO_BINDN_IMM: {
+ uint8_t count, skip_in, skip_out;
+ bool skip_in_negative;
+
+ /* Implicit skip_in/skip_out of 1, count encoded as immd
+ * value */
+ count = immd;
+ skip_in = 1;
+ skip_out = 1;
+ skip_in_negative = false;
+
+ error = bhnd_sprom_opcode_set_bind(state, count,
+ skip_in, skip_in_negative, skip_out);
+ if (error)
+ return (error);
+ break;
+ }
+
+ case SPROM_OPCODE_REV_IMM:
+ error = bhnd_sprom_opcode_set_revs(state, immd, immd);
+ if (error)
+ return (error);
+ break;
+
+ case SPROM_OPCODE_REV_RANGE: {
+ uint8_t range;
+ uint8_t rstart, rend;
+
+ /* Revision range is encoded in next byte, as
+ * { uint8_t start:4, uint8_t end:4 } */
+ range = *state->input;
+ rstart = (range & SPROM_OP_REV_START_MASK) >>
+ SPROM_OP_REV_START_SHIFT;
+ rend = (range & SPROM_OP_REV_END_MASK) >>
+ SPROM_OP_REV_END_SHIFT;
+
+ /* Update revision bitmask */
+ error = bhnd_sprom_opcode_set_revs(state, rstart, rend);
+ if (error)
+ return (error);
+
+ /* Advance input */
+ state->input++;
+ break;
+ }
+ case SPROM_OPCODE_MASK_IMM:
+ if ((error = bhnd_sprom_opcode_set_mask(state, immd)))
+ return (error);
+ break;
+
+ case SPROM_OPCODE_MASK:
+ error = bhnd_sprom_opcode_read_opval32(state, immd,
+ &val);
+ if (error)
+ return (error);
+
+ if ((error = bhnd_sprom_opcode_set_mask(state, val)))
+ return (error);
+ break;
+
+ case SPROM_OPCODE_SHIFT_IMM:
+ error = bhnd_sprom_opcode_set_shift(state, immd * 2);
+ if (error)
+ return (error);
+ break;
+
+ case SPROM_OPCODE_SHIFT: {
+ int8_t shift;
+
+ if (immd == SPROM_OP_DATA_I8) {
+ shift = (int8_t)(*state->input);
+ } else if (immd == SPROM_OP_DATA_U8) {
+ val = *state->input;
+ if (val > INT8_MAX) {
+ SPROM_OP_BAD(state, "invalid shift "
+ "value: %#x\n", val);
+ }
+
+ shift = val;
+ } else {
+ SPROM_OP_BAD(state, "unsupported shift data "
+ "type: %#hhx\n", immd);
+ return (EINVAL);
+ }
+
+ if ((error = bhnd_sprom_opcode_set_shift(state, shift)))
+ return (error);
+
+ state->input++;
+ break;
+ }
+ case SPROM_OPCODE_OFFSET_REL_IMM:
+ /* Fetch unscaled relative offset */
+ val = immd;
+
+ /* Apply scale */
+ error = bhnd_sprom_opcode_apply_scale(state, &val);
+ if (error)
+ return (error);
+
+ /* Adding val must not overflow our offset */
+ if (UINT32_MAX - state->offset < val) {
+ BHND_NV_LOG("offset out of range\n");
+ return (EINVAL);
+ }
+
+ /* Adjust offset */
+ state->offset += val;
+ break;
+ case SPROM_OPCODE_OFFSET:
+ error = bhnd_sprom_opcode_read_opval32(state, immd,
+ &val);
+ if (error)
+ return (error);
+
+ state->offset = val;
+ break;
+
+ case SPROM_OPCODE_TYPE:
+ /* Type follows as U8 */
+ immd = *state->input;
+ state->input++;
+
+ /* fall through */
+ case SPROM_OPCODE_TYPE_IMM:
+ switch (immd) {
+ case BHND_NVRAM_TYPE_UINT8:
+ case BHND_NVRAM_TYPE_UINT16:
+ case BHND_NVRAM_TYPE_UINT32:
+ case BHND_NVRAM_TYPE_UINT64:
+ case BHND_NVRAM_TYPE_INT8:
+ case BHND_NVRAM_TYPE_INT16:
+ case BHND_NVRAM_TYPE_INT32:
+ case BHND_NVRAM_TYPE_INT64:
+ case BHND_NVRAM_TYPE_CHAR:
+ case BHND_NVRAM_TYPE_STRING:
+ error = bhnd_sprom_opcode_set_type(state,
+ (bhnd_nvram_type)immd);
+ if (error)
+ return (error);
+ break;
+ default:
+ BHND_NV_LOG("unrecognized type %#hhx\n", immd);
+ return (EINVAL);
+ }
+ break;
+
+ default:
+ BHND_NV_LOG("unrecognized opcode %#hhx\n", *opcode);
+ return (EINVAL);
+ }
+
+ /* We must keep evaluating until we hit a state applicable to
+ * the SPROM revision we're parsing */
+ if (bhnd_sprom_opcode_matches_layout_rev(state))
+ return (0);
+ }
+
+ /* End of opcode stream */
+ return (ENOENT);
+}
+
+/**
+ * Reset SPROM opcode evaluation state, seek to the @p entry's position,
+ * and perform complete evaluation of the variable's opcodes.
+ *
+ * @param state The opcode state to be to be evaluated.
+ * @param entry The indexed variable location.
+ *
+ * @retval 0 success
+ * @retval non-zero If evaluation fails, a regular unix error code will be
+ * returned.
+ */
+int
+bhnd_sprom_opcode_parse_var(bhnd_sprom_opcode_state *state,
+ bhnd_sprom_opcode_idx_entry *entry)
+{
+ uint8_t opcode;
+ int error;
+
+ /* Seek to entry */
+ if ((error = bhnd_sprom_opcode_seek(state, entry)))
+ return (error);
+
+ /* Parse full variable definition */
+ while ((error = bhnd_sprom_opcode_step(state, &opcode)) == 0) {
+ /* Iterate until VAR_END */
+ if (SPROM_OPCODE_OP(opcode) != SPROM_OPCODE_VAR_END)
+ continue;
+
+ BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
+ ("incomplete variable definition"));
+
+ return (0);
+ }
+
+ /* Error parsing definition */
+ return (error);
+}
+
+/**
+ * Evaluate @p state until the next variable definition is found.
+ *
+ * @param state The opcode state to be evaluated.
+ *
+ * @retval 0 success
+ * @retval ENOENT if no additional variable definitions are available.
+ * @retval non-zero if evaluation otherwise fails, a regular unix error
+ * code will be returned.
+ */
+static int
+bhnd_sprom_opcode_next_var(bhnd_sprom_opcode_state *state)
+{
+ uint8_t opcode;
+ int error;
+
+ /* Step until we hit a variable opcode */
+ while ((error = bhnd_sprom_opcode_step(state, &opcode)) == 0) {
+ switch (SPROM_OPCODE_OP(opcode)) {
+ case SPROM_OPCODE_VAR:
+ case SPROM_OPCODE_VAR_IMM:
+ case SPROM_OPCODE_VAR_REL_IMM:
+ BHND_NV_ASSERT(
+ state->var_state == SPROM_OPCODE_VAR_STATE_OPEN,
+ ("missing variable definition"));
+
+ return (0);
+ default:
+ continue;
+ }
+ }
+
+ /* Reached EOF, or evaluation failed */
+ return (error);
+}
+
+/**
+ * Evaluate @p state until the next binding for the current variable definition
+ * is found.
+ *
+ * @param state The opcode state to be evaluated.
+ *
+ * @retval 0 success
+ * @retval ENOENT if no additional binding opcodes are found prior to reaching
+ * a new variable definition, or the end of @p state's binding opcodes.
+ * @retval non-zero if evaluation otherwise fails, a regular unix error
+ * code will be returned.
+ */
+int
+bhnd_sprom_opcode_next_binding(bhnd_sprom_opcode_state *state)
+{
+ uint8_t opcode;
+ int error;
+
+ if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN)
+ return (EINVAL);
+
+ /* Step until we hit a bind opcode, or a new variable */
+ while ((error = bhnd_sprom_opcode_step(state, &opcode)) == 0) {
+ switch (SPROM_OPCODE_OP(opcode)) {
+ case SPROM_OPCODE_DO_BIND:
+ case SPROM_OPCODE_DO_BINDN:
+ case SPROM_OPCODE_DO_BINDN_IMM:
+ /* Found next bind */
+ BHND_NV_ASSERT(
+ state->var_state == SPROM_OPCODE_VAR_STATE_OPEN,
+ ("missing variable definition"));
+ BHND_NV_ASSERT(state->var.have_bind, ("missing bind"));
+
+ return (0);
+
+ case SPROM_OPCODE_VAR_END:
+ /* No further binding opcodes */
+ BHND_NV_ASSERT(
+ state->var_state == SPROM_OPCODE_VAR_STATE_DONE,
+ ("variable definition still available"));
+ return (ENOENT);
+ }
+ }
+
+ /* Not found, or evaluation failed */
+ return (error);
+}
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h
@@ -44,12 +44,43 @@
#include "bhnd_nvram_io.h"
/** The maximum number of array elements encoded in a single SPROM variable */
-#define SPROM_ARRAY_MAXLEN 12
+#define BHND_SPROM_ARRAY_MAXLEN 12
+
+typedef struct bhnd_sprom_opcode_state bhnd_sprom_opcode_state;
+typedef struct bhnd_sprom_opcode_bind bhnd_sprom_opcode_bind;
+typedef struct bhnd_sprom_opcode_var bhnd_sprom_opcode_var;
+typedef struct bhnd_sprom_opcode_idx_entry bhnd_sprom_opcode_idx_entry;
+
+int bhnd_sprom_opcode_init(
+ bhnd_sprom_opcode_state *state,
+ const bhnd_sprom_layout *layout);
+void bhnd_sprom_opcode_fini(
+ bhnd_sprom_opcode_state *state);
+
+bhnd_sprom_opcode_idx_entry *bhnd_sprom_opcode_index_find(
+ bhnd_sprom_opcode_state *state,
+ const char *name);
+bhnd_sprom_opcode_idx_entry *bhnd_sprom_opcode_index_next(
+ bhnd_sprom_opcode_state *state,
+ bhnd_sprom_opcode_idx_entry *prev);
+
+int bhnd_sprom_opcode_parse_var(
+ bhnd_sprom_opcode_state *state,
+ bhnd_sprom_opcode_idx_entry *entry);
+
+int bhnd_sprom_opcode_seek(
+ bhnd_sprom_opcode_state *state,
+ bhnd_sprom_opcode_idx_entry *entry);
+int bhnd_sprom_opcode_next_binding(
+ bhnd_sprom_opcode_state *state);
+int bhnd_sprom_opcode_apply_scale(
+ bhnd_sprom_opcode_state *state,
+ uint32_t *value);
/**
* SPROM opcode per-bind evaluation state.
*/
-struct sprom_opcode_bind {
+struct bhnd_sprom_opcode_bind {
uint8_t count;
uint32_t skip_in; /**< input element skips */
bool skip_in_negative; /**< skip_in should be subtracted */
@@ -59,15 +90,15 @@
/**
* SPROM opcode per-variable evaluation state.
*/
-struct sprom_opcode_var {
- uint8_t nelem; /**< variable array length */
- uint32_t mask; /**< current bind input mask */
- int8_t shift; /**< current bind input shift */
- bhnd_nvram_type base_type; /**< current bind input type */
- uint32_t scale; /**< current scale to apply to scaled encodings */
- struct sprom_opcode_bind bind; /**< current bind state */
- bool have_bind; /**< if bind state is defined */
- size_t bind_total; /**< total count of bind operations performed */
+struct bhnd_sprom_opcode_var {
+ uint8_t nelem; /**< variable array length */
+ uint32_t mask; /**< current bind input mask */
+ int8_t shift; /**< current bind input shift */
+ bhnd_nvram_type base_type; /**< current bind input type */
+ uint32_t scale; /**< current scale to apply to scaled encodings */
+ bhnd_sprom_opcode_bind bind; /**< current bind state */
+ bool have_bind; /**< if bind state is defined */
+ size_t bind_total; /**< total count of bind operations performed */
};
/**
@@ -80,13 +111,16 @@
SPROM_OPCODE_VAR_STATE_NONE = 1, /**< no variable entry available */
SPROM_OPCODE_VAR_STATE_OPEN = 2, /**< currently parsing a variable entry */
SPROM_OPCODE_VAR_STATE_DONE = 3 /**< full variable entry has been parsed */
-} sprom_opcode_var_state;
+} bhnd_sprom_opcode_var_state;
/**
* SPROM opcode evaluation state
*/
-struct sprom_opcode_state {
- const struct bhnd_sprom_layout *layout; /**< SPROM layout */
+struct bhnd_sprom_opcode_state {
+ const bhnd_sprom_layout *layout; /**< SPROM layout */
+
+ bhnd_sprom_opcode_idx_entry *idx; /**< variable index (NULL during initialization) */
+ size_t num_idx; /**< variable index entry count */
/** Current SPROM revision range */
bitstr_t bit_decl(revs, SPROM_OP_REV_MAX);
@@ -98,14 +132,14 @@
size_t vid; /**< Variable ID */
/* State reset after end of each variable definition */
- struct sprom_opcode_var var; /**< variable record (if any) */
- sprom_opcode_var_state var_state; /**< variable record state */
+ bhnd_sprom_opcode_var var; /**< variable record (if any) */
+ bhnd_sprom_opcode_var_state var_state; /**< variable record state */
};
/**
* SPROM opcode variable index entry
*/
-struct sprom_opcode_idx {
+struct bhnd_sprom_opcode_idx_entry {
uint16_t vid; /**< SPROM variable ID */
uint16_t offset; /**< SPROM input offset */
uint16_t opcodes; /**< SPROM opcode offset */
@@ -118,33 +152,23 @@
* variable.
*/
union bhnd_nvram_sprom_storage {
- uint8_t u8[SPROM_ARRAY_MAXLEN];
- uint16_t u16[SPROM_ARRAY_MAXLEN];
- uint32_t u32[SPROM_ARRAY_MAXLEN];
- int8_t i8[SPROM_ARRAY_MAXLEN];
- int16_t i16[SPROM_ARRAY_MAXLEN];
- int32_t i32[SPROM_ARRAY_MAXLEN];
- char ch[SPROM_ARRAY_MAXLEN];
-};
-
-/**
- * SPROM common integer value representation.
- */
-union bhnd_nvram_sprom_intv {
- uint32_t u32;
- int32_t s32;
+ uint8_t u8[BHND_SPROM_ARRAY_MAXLEN];
+ uint16_t u16[BHND_SPROM_ARRAY_MAXLEN];
+ uint32_t u32[BHND_SPROM_ARRAY_MAXLEN];
+ int8_t i8[BHND_SPROM_ARRAY_MAXLEN];
+ int16_t i16[BHND_SPROM_ARRAY_MAXLEN];
+ int32_t i32[BHND_SPROM_ARRAY_MAXLEN];
+ char ch[BHND_SPROM_ARRAY_MAXLEN];
};
/**
* SPROM data class instance state.
*/
struct bhnd_nvram_sprom {
- struct bhnd_nvram_data nv; /**< common instance state */
- struct bhnd_nvram_io *data; /**< backing SPROM image */
- const struct bhnd_sprom_layout *layout; /**< layout definition */
- struct sprom_opcode_state state; /**< opcode eval state */
- struct sprom_opcode_idx *idx; /**< opcode index entries */
- size_t num_idx; /**< opcode index entry count */
+ struct bhnd_nvram_data nv; /**< common instance state */
+ struct bhnd_nvram_io *data; /**< backing SPROM image */
+ const bhnd_sprom_layout *layout; /**< layout definition */
+ bhnd_sprom_opcode_state state; /**< opcode eval state */
};
#endif /* _BHND_NVRAM_BHND_NVRAM_SPROMVAR_H_ */
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c
@@ -62,7 +62,8 @@
size_t count; /**< variable count */
};
-BHND_NVRAM_DATA_CLASS_DEFN(tlv, "WGT634U", sizeof(struct bhnd_nvram_tlv))
+BHND_NVRAM_DATA_CLASS_DEFN(tlv, "WGT634U", BHND_NVRAM_DATA_CAP_DEVPATHS,
+ sizeof(struct bhnd_nvram_tlv))
/** Minimal TLV_ENV record header */
struct bhnd_nvram_tlv_env_hdr {
@@ -163,6 +164,123 @@
return (BHND_NVRAM_DATA_PROBE_DEFAULT);
}
+static int
+bhnd_nvram_tlv_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
+ bhnd_nvram_plist *options, void *outp, size_t *olen)
+{
+ bhnd_nvram_prop *prop;
+ size_t limit, nbytes;
+ int error;
+
+ /* Determine output byte limit */
+ if (outp != NULL)
+ limit = *olen;
+ else
+ limit = 0;
+
+ nbytes = 0;
+
+ /* Write all properties */
+ prop = NULL;
+ while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
+ struct bhnd_nvram_tlv_env env;
+ const char *name;
+ uint8_t *p;
+ size_t name_len, value_len;
+ size_t rec_size;
+
+ env.hdr.tag = NVRAM_TLV_TYPE_ENV;
+ env.hdr.size = sizeof(env.flags);
+ env.flags = 0x0;
+
+ /* Fetch name value and add to record length */
+ name = bhnd_nvram_prop_name(prop);
+ name_len = strlen(name) + 1 /* '=' */;
+
+ if (UINT8_MAX - env.hdr.size < name_len) {
+ BHND_NV_LOG("%s name exceeds maximum TLV record "
+ "length\n", name);
+ return (EFTYPE); /* would overflow TLV size */
+ }
+
+ env.hdr.size += name_len;
+
+ /* Add string value to record length */
+ error = bhnd_nvram_prop_encode(prop, NULL, &value_len,
+ BHND_NVRAM_TYPE_STRING);
+ if (error) {
+ BHND_NV_LOG("error serializing %s to required type "
+ "%s: %d\n", name,
+ bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
+ error);
+ return (error);
+ }
+
+ if (UINT8_MAX - env.hdr.size < value_len) {
+ BHND_NV_LOG("%s value exceeds maximum TLV record "
+ "length\n", name);
+ return (EFTYPE); /* would overflow TLV size */
+ }
+
+ env.hdr.size += value_len;
+
+ /* Calculate total record size */
+ rec_size = sizeof(env.hdr) + env.hdr.size;
+ if (SIZE_MAX - nbytes < rec_size)
+ return (EFTYPE); /* would overflow size_t */
+
+ /* Calculate our output pointer */
+ if (nbytes > limit || limit - nbytes < rec_size) {
+ /* buffer is full; cannot write */
+ p = NULL;
+ } else {
+ p = (uint8_t *)outp + nbytes;
+ }
+
+ /* Write to output */
+ if (p != NULL) {
+ memcpy(p, &env, sizeof(env));
+ p += sizeof(env);
+
+ memcpy(p, name, name_len - 1);
+ p[name_len - 1] = '=';
+ p += name_len;
+
+ error = bhnd_nvram_prop_encode(prop, p, &value_len,
+ BHND_NVRAM_TYPE_STRING);
+ if (error) {
+ BHND_NV_LOG("error serializing %s to required "
+ "type %s: %d\n", name,
+ bhnd_nvram_type_name(
+ BHND_NVRAM_TYPE_STRING),
+ error);
+ return (error);
+ }
+ }
+
+ nbytes += rec_size;
+ }
+
+ /* Write terminating END record */
+ if (limit > nbytes)
+ *((uint8_t *)outp + nbytes) = NVRAM_TLV_TYPE_END;
+
+ if (nbytes == SIZE_MAX)
+ return (EFTYPE); /* would overflow size_t */
+ nbytes++;
+
+ /* Provide required length */
+ *olen = nbytes;
+ if (limit < *olen) {
+ if (outp == NULL)
+ return (0);
+
+ return (ENOMEM);
+ }
+
+ return (0);
+}
+
/**
* Initialize @p tlv with the provided NVRAM TLV data mapped by @p src.
*
@@ -256,91 +374,13 @@
return (tlv->count);
}
+
static bhnd_nvram_plist *
bhnd_nvram_tlv_options(struct bhnd_nvram_data *nv)
{
return (NULL);
}
-static int
-bhnd_nvram_tlv_size(struct bhnd_nvram_data *nv, size_t *size)
-{
- /* Let the serialization implementation calculate the length */
- return (bhnd_nvram_data_serialize(nv, NULL, size));
-}
-
-static int
-bhnd_nvram_tlv_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len)
-{
- struct bhnd_nvram_tlv *tlv;
- size_t limit;
- size_t next;
- uint8_t tag;
- int error;
-
- tlv = (struct bhnd_nvram_tlv *)nv;
-
- /* Save the buffer capacity */
- if (buf == NULL)
- limit = 0;
- else
- limit = *len;
-
- /* Write all of our TLV records to the output buffer (or just
- * calculate the buffer size that would be required) */
- next = 0;
- do {
- struct bhnd_nvram_tlv_env *env;
- uint8_t *p;
- size_t name_len;
- size_t rec_offset, rec_size;
-
- /* Parse the TLV record */
- error = bhnd_nvram_tlv_next_record(tlv->data, &next,
- &rec_offset, &tag);
- if (error)
- return (error);
-
- rec_size = next - rec_offset;
-
- /* Calculate our output pointer */
- if (rec_offset > limit || limit - rec_offset < rec_size) {
- /* buffer is full; cannot write */
- p = NULL;
- } else {
- p = (uint8_t *)buf + rec_offset;
- }
-
- /* If not writing, nothing further to do for this record */
- if (p == NULL)
- continue;
-
- /* Copy to the output buffer */
- error = bhnd_nvram_io_read(tlv->data, rec_offset, p, rec_size);
- if (error)
- return (error);
-
- /* All further processing is TLV_ENV-specific */
- if (tag != NVRAM_TLV_TYPE_ENV)
- continue;
-
- /* Restore the original key=value format, rewriting '\0'
- * delimiter back to '=' */
- env = (struct bhnd_nvram_tlv_env *)p;
- name_len = strlen(env->envp); /* skip variable name */
- *(env->envp + name_len) = '='; /* set '=' */
- } while (tag != NVRAM_TLV_TYPE_END);
-
- /* The 'next' offset should now point at EOF, and represents
- * the total length of the serialized output. */
- *len = next;
-
- if (buf != NULL && limit < *len)
- return (ENOMEM);
-
- return (0);
-}
-
static uint32_t
bhnd_nvram_tlv_caps(struct bhnd_nvram_data *nv)
{
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h
@@ -55,6 +55,13 @@
/** @see bhnd_nvram_data_probe() */
typedef int (bhnd_nvram_data_op_probe)(struct bhnd_nvram_io *io);
+/** @see bhnd_nvram_data_serialize() */
+typedef int (bhnd_nvram_data_op_serialize)(
+ bhnd_nvram_data_class *cls,
+ bhnd_nvram_plist *props,
+ bhnd_nvram_plist *options, void *outp,
+ size_t *olen);
+
/** @see bhnd_nvram_data_new() */
typedef int (bhnd_nvram_data_op_new)(struct bhnd_nvram_data *nv,
struct bhnd_nvram_io *io);
@@ -66,15 +73,6 @@
/** @see bhnd_nvram_data_count() */
typedef size_t (bhnd_nvram_data_op_count)(struct bhnd_nvram_data *nv);
-/** @see bhnd_nvram_data_size() */
-typedef int (bhnd_nvram_data_op_size)(struct bhnd_nvram_data *nv,
- size_t *len);
-
-/** @see bhnd_nvram_data_serialize() */
-typedef int (bhnd_nvram_data_op_serialize)(
- struct bhnd_nvram_data *nv, void *buf,
- size_t *len);
-
/** @see bhnd_nvram_data_options() */
typedef bhnd_nvram_plist*(bhnd_nvram_data_op_options)(
struct bhnd_nvram_data *nv);
@@ -129,14 +127,14 @@
*/
struct bhnd_nvram_data_class {
const char *desc; /**< description */
+ uint32_t caps; /**< capabilities (BHND_NVRAM_DATA_CAP_*) */
size_t size; /**< instance size */
bhnd_nvram_data_op_probe *op_probe;
+ bhnd_nvram_data_op_serialize *op_serialize;
bhnd_nvram_data_op_new *op_new;
bhnd_nvram_data_op_free *op_free;
bhnd_nvram_data_op_count *op_count;
- bhnd_nvram_data_op_size *op_size;
- bhnd_nvram_data_op_serialize *op_serialize;
bhnd_nvram_data_op_options *op_options;
bhnd_nvram_data_op_caps *op_caps;
bhnd_nvram_data_op_next *op_next;
@@ -186,11 +184,10 @@
*/
#define BHND_NVRAM_DATA_CLASS_ITER_METHODS(_cname, _macro) \
_macro(_cname, probe) \
+ _macro(_cname, serialize) \
_macro(_cname, new) \
_macro(_cname, free) \
_macro(_cname, count) \
- _macro(_cname, size) \
- _macro(_cname, serialize) \
_macro(_cname, options) \
_macro(_cname, caps) \
_macro(_cname, next) \
@@ -207,12 +204,13 @@
* Define a bhnd_nvram_data_class with class name @p _n and description
* @p _desc, and register with bhnd_nvram_data_class_set.
*/
-#define BHND_NVRAM_DATA_CLASS_DEFN(_cname, _desc, _size) \
+#define BHND_NVRAM_DATA_CLASS_DEFN(_cname, _desc, _caps, _size) \
BHND_NVRAM_DATA_CLASS_ITER_METHODS(_cname, \
BHND_NVRAM_DATA_CLASS_DECL_METHOD) \
\
struct bhnd_nvram_data_class bhnd_nvram_## _cname ## _class = { \
.desc = (_desc), \
+ .caps = (_caps), \
.size = (_size), \
BHND_NVRAM_DATA_CLASS_ITER_METHODS(_cname, \
BHND_NVRAM_DATA_CLASS_ASSIGN_METHOD) \
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_private.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_private.h
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_private.h
@@ -314,7 +314,7 @@
bhnd_nvram_type type; /**< variable type */
uint8_t nelem; /**< element count, or 1 if not
an array-typed variable */
- const bhnd_nvram_val_fmt *fmt; /**< value format, or NULL */
+ const bhnd_nvram_val_fmt *fmt; /**< value format */
uint32_t flags; /**< flags (BHND_NVRAM_VF_*) */
};
@@ -327,19 +327,20 @@
/**
* SPROM layout descriptor.
*/
-struct bhnd_sprom_layout {
+typedef struct bhnd_sprom_layout {
size_t size; /**< SPROM image size, in bytes */
uint8_t rev; /**< SPROM revision */
uint8_t flags; /**< layout flags (SPROM_LAYOUT_*) */
size_t srev_offset; /**< offset to SROM revision */
size_t magic_offset; /**< offset to magic value */
uint16_t magic_value; /**< expected magic value */
+ size_t crc_offset; /**< offset to crc8 value */
const uint8_t *bindings; /**< SPROM binding opcode table */
size_t bindings_size; /**< SPROM binding opcode table size */
uint16_t num_vars; /**< total number of variables defined
for this layout by the binding
table */
-};
+} bhnd_sprom_layout;
/*
* SPROM layout descriptions generated from nvram_map.
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_store.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_store.h
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_store.h
@@ -76,6 +76,9 @@
bhnd_nvram_plist **props, bhnd_nvram_plist **options,
uint32_t flags);
+int bhnd_nvram_store_serialize(struct bhnd_nvram_store *store,
+ const char *path, struct bhnd_nvram_io **data, uint32_t flags);
+
int bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,
void *outp, size_t *olen, bhnd_nvram_type otype);
int bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name,
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_store.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_store.c
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_store.c
@@ -998,6 +998,90 @@
}
/**
+ * Encode all NVRAM properties at @p path, using the @p store's current NVRAM
+ * data format.
+ *
+ * @param sc The NVRAM store instance.
+ * @param path The NVRAM path to export, or NULL to select the root
+ * path.
+ * @param[out] data On success, will be set to the newly serialized value.
+ * The caller is responsible for freeing this value
+ * via bhnd_nvram_io_free().
+ * @param flags Export flags. See BHND_NVSTORE_EXPORT_*.
+ *
+ * @retval 0 success
+ * @retval EINVAL If @p flags is invalid.
+ * @retval ENOENT The requested path was not found.
+ * @retval ENOMEM If allocation fails.
+ * @retval non-zero If serialization of @p path otherwise fails, a regular
+ * unix error code will be returned.
+ */
+int
+bhnd_nvram_store_serialize(struct bhnd_nvram_store *sc, const char *path,
+ struct bhnd_nvram_io **data, uint32_t flags)
+{
+ bhnd_nvram_plist *props;
+ bhnd_nvram_plist *options;
+ bhnd_nvram_data_class *cls;
+ struct bhnd_nvram_io *io;
+ void *outp;
+ size_t olen;
+ int error;
+
+ props = NULL;
+ options = NULL;
+ io = NULL;
+
+ /* Perform requested export */
+ error = bhnd_nvram_store_export(sc, path, &cls, &props, &options,
+ flags);
+ if (error)
+ return (error);
+
+ /* Determine serialized size */
+ error = bhnd_nvram_data_serialize(cls, props, options, NULL, &olen);
+ if (error)
+ goto failed;
+
+ /* Allocate output buffer */
+ if ((io = bhnd_nvram_iobuf_empty(olen, olen)) == NULL) {
+ error = ENOMEM;
+ goto failed;
+ }
+
+ /* Fetch write pointer */
+ if ((error = bhnd_nvram_io_write_ptr(io, 0, &outp, olen, NULL)))
+ goto failed;
+
+ /* Perform serialization */
+ error = bhnd_nvram_data_serialize(cls, props, options, outp, &olen);
+ if (error)
+ goto failed;
+
+ if ((error = bhnd_nvram_io_setsize(io, olen)))
+ goto failed;
+
+ /* Success */
+ bhnd_nvram_plist_release(props);
+ bhnd_nvram_plist_release(options);
+
+ *data = io;
+ return (0);
+
+failed:
+ if (props != NULL)
+ bhnd_nvram_plist_release(props);
+
+ if (options != NULL)
+ bhnd_nvram_plist_release(options);
+
+ if (io != NULL)
+ bhnd_nvram_io_free(io);
+
+ return (error);
+}
+
+/**
* Read an NVRAM variable.
*
* @param sc The NVRAM parser state.
Index: head/sys/dev/bhnd/tools/nvram_map_gen.awk
===================================================================
--- head/sys/dev/bhnd/tools/nvram_map_gen.awk
+++ head/sys/dev/bhnd/tools/nvram_map_gen.awk
@@ -253,14 +253,19 @@
# Value Formats
Fmt = class_new("Fmt")
class_add_prop(Fmt, p_name, "name")
- class_add_prop(Fmt, p_symbol, "const")
+ class_add_prop(Fmt, p_symbol, "symbol")
+ class_add_prop(Fmt, p_array_fmt, "array_fmt")
FmtHex = fmt_new("hex", "bhnd_nvram_val_bcm_hex_fmt")
FmtDec = fmt_new("decimal", "bhnd_nvram_val_bcm_decimal_fmt")
FmtMAC = fmt_new("macaddr", "bhnd_nvram_val_bcm_macaddr_fmt")
FmtLEDDC = fmt_new("leddc", "bhnd_nvram_val_bcm_leddc_fmt")
+ FmtCharArray = fmt_new("char_array", "bhnd_nvram_val_char_array_fmt")
+ FmtChar = fmt_new("char", "bhnd_nvram_val_char_array_fmt",
+ FmtCharArray)
FmtStr = fmt_new("string", "bhnd_nvram_val_bcm_string_fmt")
+ # User-specifiable value formats
ValueFormats = map_new()
map_set(ValueFormats, get(FmtHex, p_name), FmtHex)
map_set(ValueFormats, get(FmtDec, p_name), FmtDec)
@@ -315,7 +320,7 @@
"BHND_NVRAM_TYPE_INT32_ARRAY", FmtDec, UInt32Max, 6, 22)
Char = type_new("char", 1, 1, "BHND_NVRAM_TYPE_CHAR",
- "BHND_NVRAM_TYPE_CHAR_ARRAY", FmtStr, UInt8Max, 8, 24)
+ "BHND_NVRAM_TYPE_CHAR_ARRAY", FmtChar, UInt8Max, 8, 24)
BaseTypes = map_new()
map_set(BaseTypes, get(UInt8, p_name), UInt8)
@@ -634,7 +639,7 @@
# Write a top-level bhnd_sprom_layout entry for the given revision
# and layout definition
function write_data_srom_layout(layout, revision, _flags, _size,
- _sromcrc, _crc_seg,
+ _sromcrc, _crc_seg, _crc_off,
_sromsig, _sig_seg, _sig_offset, _sig_value,
_sromrev, _rev_seg, _rev_off,
_num_vars)
@@ -648,7 +653,8 @@
"cannot compute total size")
} else {
_crc_seg = srom_entry_get_single_segment(_sromcrc)
- _size = get(_crc_seg, p_offset)
+ _crc_off = get(_crc_seg, p_offset)
+ _size = _crc_off
_size += get(get(_crc_seg, p_type), p_width)
}
@@ -703,6 +709,8 @@
emit(".magic_value = 0,\n")
}
+ emit(".crc_offset = " _crc_off ",\n")
+
emit(".bindings = " srom_layout_get_variable_name(layout) ",\n")
emit(".bindings_size = nitems(" \
srom_layout_get_variable_name(layout) "),\n")
@@ -1741,6 +1749,9 @@
if (class == null)
return (0)
+ if (prop_id == null)
+ return (0)
+
# Check class<->prop cache
if ((class, prop_id) in _g_class_prop_cache)
return (1)
@@ -2538,9 +2549,17 @@
}
# Return the default fmt for a given type instance
-function type_get_default_fmt(type, _base) {
+function type_get_default_fmt(type, _base, _fmt, _array_fmt) {
_base = type_get_base(type)
- return (get(_base, p_default_fmt))
+ _fmt = get(_base, p_default_fmt)
+
+ if (obj_is_instanceof(type, ArrayType)) {
+ _array_fmt = get(_fmt, p_array_fmt)
+ if (_array_fmt != null)
+ _fmt = _array_fmt
+ }
+
+ return (_fmt)
}
# Return a string representation of the given type
@@ -2641,11 +2660,14 @@
}
# Create a new Fmt instance
-function fmt_new(name, symbol, _obj) {
+function fmt_new(name, symbol, array_fmt, _obj) {
_obj = obj_new(Fmt)
set(_obj, p_name, name)
set(_obj, p_symbol, symbol)
+ if (array_fmt != null)
+ set(_obj, p_array_fmt, array_fmt)
+
return (_obj)
}
Index: head/sys/modules/bhnd/Makefile
===================================================================
--- head/sys/modules/bhnd/Makefile
+++ head/sys/modules/bhnd/Makefile
@@ -32,6 +32,7 @@
bhnd_nvram_data_bcmraw.c \
bhnd_nvram_data_btxt.c \
bhnd_nvram_data_sprom.c \
+ bhnd_nvram_data_sprom_subr.c \
bhnd_nvram_data_tlv.c \
bhnd_nvram_io.c \
bhnd_nvram_iobuf.c \
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Mar 11, 12:18 PM (4 h, 32 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29520400
Default Alt Text
D8762.id23114.diff (147 KB)
Attached To
Mode
D8762: bhnd(4): NVRAM serialization support.
Attached
Detach File
Event Timeline
Log In to Comment