Page MenuHomeFreeBSD

D8762.id23114.diff
No OneTemporary

D8762.id23114.diff

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

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)

Event Timeline