Page MenuHomeFreeBSD

D7489.id.diff
No OneTemporary

D7489.id.diff

Index: head/sys/conf/files
===================================================================
--- head/sys/conf/files
+++ head/sys/conf/files
@@ -1163,10 +1163,14 @@
dev/bhnd/cores/pcie2/bhnd_pcie2.c optional bhnd pci
dev/bhnd/cores/pcie2/bhnd_pcie2_hostb.c optional bhndb bhnd pci
dev/bhnd/cores/pcie2/bhnd_pcie2b.c optional bhnd_pcie2b bhnd pci
+dev/bhnd/nvram/bhnd_nvram.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_common.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_cfe.c optional bhnd siba_nexus cfe | \
+ bhnd bcma_nexus cfe
dev/bhnd/nvram/bhnd_nvram_if.m optional bhnd
+dev/bhnd/nvram/bhnd_nvram_parser.c optional bhnd
dev/bhnd/nvram/bhnd_sprom.c optional bhnd
-dev/bhnd/nvram/bhnd_sprom_subr.c optional bhnd
-dev/bhnd/nvram/nvram_subr.c optional bhnd
+dev/bhnd/nvram/bhnd_sprom_parser.c optional bhnd
dev/bhnd/siba/siba.c optional siba bhnd
dev/bhnd/siba/siba_bhndb.c optional siba bhnd bhndb
dev/bhnd/siba/siba_nexus.c optional siba_nexus siba bhnd
Index: head/sys/dev/bhnd/bhnd.h
===================================================================
--- head/sys/dev/bhnd/bhnd.h
+++ head/sys/dev/bhnd/bhnd.h
@@ -321,6 +321,31 @@
const char *name);
void bhnd_set_default_core_desc(device_t dev);
+int bhnd_nvram_getvar_str(device_t dev,
+ const char *name, char *buf, size_t len,
+ size_t *rlen);
+
+int bhnd_nvram_getvar_uint(device_t dev,
+ const char *name, void *value, int width);
+int bhnd_nvram_getvar_uint8(device_t dev,
+ const char *name, uint8_t *value);
+int bhnd_nvram_getvar_uint16(device_t dev,
+ const char *name, uint16_t *value);
+int bhnd_nvram_getvar_uint32(device_t dev,
+ const char *name, uint32_t *value);
+
+int bhnd_nvram_getvar_int(device_t dev,
+ const char *name, void *value, int width);
+int bhnd_nvram_getvar_int8(device_t dev,
+ const char *name, int8_t *value);
+int bhnd_nvram_getvar_int16(device_t dev,
+ const char *name, int16_t *value);
+int bhnd_nvram_getvar_int32(device_t dev,
+ const char *name, int32_t *value);
+
+int bhnd_nvram_getvar_array(device_t dev,
+ const char *name, void *buf, size_t count,
+ bhnd_nvram_type type);
bool bhnd_bus_generic_is_hw_disabled(device_t dev,
device_t child);
@@ -329,7 +354,8 @@
u_int port, u_int region);
int bhnd_bus_generic_get_nvram_var(device_t dev,
device_t child, const char *name,
- void *buf, size_t *size);
+ void *buf, size_t *size,
+ bhnd_nvram_type type);
const struct bhnd_chipid *bhnd_bus_generic_get_chipid(device_t dev,
device_t child);
int bhnd_bus_generic_read_board_info(device_t dev,
@@ -428,55 +454,36 @@
}
/**
- * Determine an NVRAM variable's expected size.
- *
- * @param dev A bhnd bus child device.
- * @param name The variable name.
- * @param[out] len On success, the variable's size, in bytes.
- *
- * @retval 0 success
- * @retval ENOENT The requested variable was not found.
- * @retval ENODEV No valid NVRAM source could be found.
- * @retval non-zero If reading @p name otherwise fails, a regular unix
- * error code will be returned.
- */
-static inline int
-bhnd_nvram_getvarlen(device_t dev, const char *name, size_t *len)
-{
- return (BHND_BUS_GET_NVRAM_VAR(device_get_parent(dev), dev, name, NULL,
- len));
-}
-
-/**
- * Read an NVRAM variable.
- *
- * @param dev A bhnd bus child device.
- * @param name The NVRAM variable name.
- * @param buf A buffer large enough to hold @p len bytes. On success,
- * the requested value will be written to this buffer.
- * @param len The required variable length.
+ * Read an NVRAM variable, coerced to the requested @p type.
*
+ * @param dev A bhnd bus child device.
+ * @param name The NVRAM variable name.
+ * @param[out] buf A buffer large enough to hold @p len bytes. On
+ * success, the requested value will be written to
+ * this buffer. This argment may be NULL if
+ * the value is not desired.
+ * @param[in,out] len The maximum capacity of @p buf. On success,
+ * will be set to the actual size of the requested
+ * value.
+ * @param type The desired data representation to be written
+ * to @p buf.
+ *
* @retval 0 success
* @retval ENOENT The requested variable was not found.
- * @retval EINVAL If @p len does not match the actual variable size.
* @retval ENODEV No valid NVRAM source could be found.
+ * @retval ENOMEM If a buffer of @p size is too small to hold the
+ * requested value.
+ * @retval EOPNOTSUPP If the value cannot be coerced to @p type.
+ * @retval ERANGE If value coercion would overflow @p type.
* @retval non-zero If reading @p name otherwise fails, a regular unix
* error code will be returned.
*/
static inline int
-bhnd_nvram_getvar(device_t dev, const char *name, void *buf, size_t len)
+bhnd_nvram_getvar(device_t dev, const char *name, void *buf, size_t *len,
+ bhnd_nvram_type type)
{
- size_t var_len;
- int error;
-
- if ((error = bhnd_nvram_getvarlen(dev, name, &var_len)))
- return (error);
-
- if (len != var_len)
- return (EINVAL);
-
return (BHND_BUS_GET_NVRAM_VAR(device_get_parent(dev), dev, name, buf,
- &len));
+ len, type));
}
/**
Index: head/sys/dev/bhnd/bhnd.c
===================================================================
--- head/sys/dev/bhnd/bhnd.c
+++ head/sys/dev/bhnd/bhnd.c
@@ -361,7 +361,7 @@
if (ccaps->nvram_src != BHND_NVRAM_SRC_UNKNOWN) {
if ((sc->nvram_dev = bhnd_find_nvram(sc)) == NULL) {
device_printf(sc->dev,
- "warning: %s NVRAM device not found\n",
+ "warning: NVRAM %s device not found\n",
bhnd_nvram_src_name(ccaps->nvram_src));
}
}
@@ -459,6 +459,11 @@
}
child = device_find_child(chipc, classname, -1);
+ if (child != NULL)
+ goto found;
+
+ /* Look for a parent-attached device (e.g. nexus0 -> bhnd_nvram) */
+ child = device_find_child(device_get_parent(sc->dev), classname, -1);
if (child == NULL)
return (NULL);
@@ -651,7 +656,7 @@
*/
int
bhnd_generic_get_nvram_var(device_t dev, device_t child, const char *name,
- void *buf, size_t *size)
+ void *buf, size_t *size, bhnd_nvram_type type)
{
struct bhnd_softc *sc;
device_t nvram, parent;
@@ -660,14 +665,14 @@
/* If a NVRAM device is available, consult it first */
if ((nvram = bhnd_find_nvram(sc)) != NULL)
- return BHND_NVRAM_GETVAR(nvram, name, buf, size);
+ return BHND_NVRAM_GETVAR(nvram, name, buf, size, type);
/* Otherwise, try to delegate to parent */
if ((parent = device_get_parent(dev)) == NULL)
return (ENODEV);
return (BHND_BUS_GET_NVRAM_VAR(device_get_parent(dev), child,
- name, buf, size));
+ name, buf, size, type));
}
/**
Index: head/sys/dev/bhnd/bhnd_bus_if.m
===================================================================
--- head/sys/dev/bhnd/bhnd_bus_if.m
+++ head/sys/dev/bhnd/bhnd_bus_if.m
@@ -116,7 +116,7 @@
static int
bhnd_bus_null_get_nvram_var(device_t dev, device_t child,
- const char *name, void *buf, size_t *size)
+ const char *name, void *buf, size_t *size, bhnd_nvram_type type)
{
return (ENODEV);
}
@@ -492,12 +492,15 @@
* the value is not desired.
* @param[in,out] size The capacity of @p buf. On success, will be set
* to the actual size of the requested value.
+ * @param type The data type to be written to @p buf.
*
* @retval 0 success
* @retval ENOENT The requested variable was not found.
* @retval ENOMEM If @p buf is non-NULL and a buffer of @p size is too
* small to hold the requested value.
* @retval ENODEV No valid NVRAM source could be found.
+ * @retval EFTYPE If the @p name's data type cannot be coerced to @p type.
+ * @retval ERANGE If value coercion would overflow @p type.
* @retval non-zero If reading @p name otherwise fails, a regular unix
* error code will be returned.
*/
@@ -507,6 +510,7 @@
const char *name;
void *buf;
size_t *size;
+ bhnd_nvram_type type;
} DEFAULT bhnd_bus_null_get_nvram_var;
Index: head/sys/dev/bhnd/bhnd_subr.c
===================================================================
--- head/sys/dev/bhnd/bhnd_subr.c
+++ head/sys/dev/bhnd/bhnd_subr.c
@@ -30,7 +30,6 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include <sys/types.h>
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/systm.h>
@@ -881,6 +880,325 @@
}
/**
+ * Read an NVRAM variable's NUL-terminated string value.
+ *
+ * @param dev A bhnd bus child device.
+ * @param name The NVRAM variable name.
+ * @param[out] buf A buffer large enough to hold @p len bytes. On
+ * success, the NUL-terminated string value will be
+ * written to this buffer. This argment may be NULL if
+ * the value is not desired.
+ * @param len The maximum capacity of @p buf.
+ * @param[out] rlen On success, will be set to the actual size of
+ * the requested value (including NUL termination). This
+ * argment may be NULL if the size is not desired.
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval ENODEV No valid NVRAM source could be found.
+ * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too
+ * small to hold the requested value.
+ * @retval EFTYPE If the variable data cannot be coerced to a valid
+ * string representation.
+ * @retval ERANGE If value coercion would overflow @p type.
+ * @retval non-zero If reading @p name otherwise fails, a regular unix
+ * error code will be returned.
+ */
+int
+bhnd_nvram_getvar_str(device_t dev, const char *name, char *buf, size_t len,
+ size_t *rlen)
+{
+ size_t larg;
+ int error;
+
+ larg = len;
+ error = bhnd_nvram_getvar(dev, name, buf, &larg, BHND_NVRAM_TYPE_CSTR);
+ if (rlen != NULL)
+ *rlen = larg;
+
+ return (error);
+}
+
+/**
+ * Read an NVRAM variable's unsigned integer value.
+ *
+ * @param dev A bhnd bus child device.
+ * @param name The NVRAM variable name.
+ * @param[out] value On success, the requested value will be written
+ * to this pointer.
+ * @param width The output integer type width (1, 2, or
+ * 4 bytes).
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval ENODEV No valid NVRAM source could be found.
+ * @retval EFTYPE If the variable data cannot be coerced to a
+ * a valid unsigned integer representation.
+ * @retval ERANGE If value coercion would overflow (or underflow) an
+ * unsigned representation of the given @p width.
+ * @retval non-zero If reading @p name otherwise fails, a regular unix
+ * error code will be returned.
+ */
+int
+bhnd_nvram_getvar_uint(device_t dev, const char *name, void *value, int width)
+{
+ bhnd_nvram_type type;
+ size_t len;
+
+ switch (width) {
+ case 1:
+ type = BHND_NVRAM_TYPE_UINT8;
+ break;
+ case 2:
+ type = BHND_NVRAM_TYPE_UINT16;
+ break;
+ case 4:
+ type = BHND_NVRAM_TYPE_UINT32;
+ break;
+ default:
+ device_printf(dev, "unsupported NVRAM integer width: %d\n",
+ width);
+ return (EINVAL);
+ }
+
+ len = width;
+ return (bhnd_nvram_getvar(dev, name, value, &len, type));
+}
+
+/**
+ * Read an NVRAM variable's unsigned 8-bit integer value.
+ *
+ * @param dev A bhnd bus child device.
+ * @param name The NVRAM variable name.
+ * @param[out] value On success, the requested value will be written
+ * to this pointer.
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval ENODEV No valid NVRAM source could be found.
+ * @retval EFTYPE If the variable data cannot be coerced to a
+ * a valid unsigned integer representation.
+ * @retval ERANGE If value coercion would overflow (or underflow) uint8_t.
+ * @retval non-zero If reading @p name otherwise fails, a regular unix
+ * error code will be returned.
+ */
+int
+bhnd_nvram_getvar_uint8(device_t dev, const char *name, uint8_t *value)
+{
+ return (bhnd_nvram_getvar_uint(dev, name, value, sizeof(*value)));
+}
+
+/**
+ * Read an NVRAM variable's unsigned 16-bit integer value.
+ *
+ * @param dev A bhnd bus child device.
+ * @param name The NVRAM variable name.
+ * @param[out] value On success, the requested value will be written
+ * to this pointer.
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval ENODEV No valid NVRAM source could be found.
+ * @retval EFTYPE If the variable data cannot be coerced to a
+ * a valid unsigned integer representation.
+ * @retval ERANGE If value coercion would overflow (or underflow)
+ * uint16_t.
+ * @retval non-zero If reading @p name otherwise fails, a regular unix
+ * error code will be returned.
+ */
+int
+bhnd_nvram_getvar_uint16(device_t dev, const char *name, uint16_t *value)
+{
+ return (bhnd_nvram_getvar_uint(dev, name, value, sizeof(*value)));
+}
+
+/**
+ * Read an NVRAM variable's unsigned 32-bit integer value.
+ *
+ * @param dev A bhnd bus child device.
+ * @param name The NVRAM variable name.
+ * @param[out] value On success, the requested value will be written
+ * to this pointer.
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval ENODEV No valid NVRAM source could be found.
+ * @retval EFTYPE If the variable data cannot be coerced to a
+ * a valid unsigned integer representation.
+ * @retval ERANGE If value coercion would overflow (or underflow)
+ * uint32_t.
+ * @retval non-zero If reading @p name otherwise fails, a regular unix
+ * error code will be returned.
+ */
+int
+bhnd_nvram_getvar_uint32(device_t dev, const char *name, uint32_t *value)
+{
+ return (bhnd_nvram_getvar_uint(dev, name, value, sizeof(*value)));
+}
+
+/**
+ * Read an NVRAM variable's signed integer value.
+ *
+ * @param dev A bhnd bus child device.
+ * @param name The NVRAM variable name.
+ * @param[out] value On success, the requested value will be written
+ * to this pointer.
+ * @param width The output integer type width (1, 2, or
+ * 4 bytes).
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval ENODEV No valid NVRAM source could be found.
+ * @retval EFTYPE If the variable data cannot be coerced to a
+ * a valid integer representation.
+ * @retval ERANGE If value coercion would overflow (or underflow) an
+ * signed representation of the given @p width.
+ * @retval non-zero If reading @p name otherwise fails, a regular unix
+ * error code will be returned.
+ */
+int
+bhnd_nvram_getvar_int(device_t dev, const char *name, void *value, int width)
+{
+ bhnd_nvram_type type;
+ size_t len;
+
+ switch (width) {
+ case 1:
+ type = BHND_NVRAM_TYPE_INT8;
+ break;
+ case 2:
+ type = BHND_NVRAM_TYPE_INT16;
+ break;
+ case 4:
+ type = BHND_NVRAM_TYPE_INT32;
+ break;
+ default:
+ device_printf(dev, "unsupported NVRAM integer width: %d\n",
+ width);
+ return (EINVAL);
+ }
+
+ len = width;
+ return (bhnd_nvram_getvar(dev, name, value, &len, type));
+}
+
+/**
+ * Read an NVRAM variable's signed 8-bit integer value.
+ *
+ * @param dev A bhnd bus child device.
+ * @param name The NVRAM variable name.
+ * @param[out] value On success, the requested value will be written
+ * to this pointer.
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval ENODEV No valid NVRAM source could be found.
+ * @retval EFTYPE If the variable data cannot be coerced to a
+ * a valid integer representation.
+ * @retval ERANGE If value coercion would overflow (or underflow) int8_t.
+ * @retval non-zero If reading @p name otherwise fails, a regular unix
+ * error code will be returned.
+ */
+int
+bhnd_nvram_getvar_int8(device_t dev, const char *name, int8_t *value)
+{
+ return (bhnd_nvram_getvar_int(dev, name, value, sizeof(*value)));
+}
+
+/**
+ * Read an NVRAM variable's signed 16-bit integer value.
+ *
+ * @param dev A bhnd bus child device.
+ * @param name The NVRAM variable name.
+ * @param[out] value On success, the requested value will be written
+ * to this pointer.
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval ENODEV No valid NVRAM source could be found.
+ * @retval EFTYPE If the variable data cannot be coerced to a
+ * a valid integer representation.
+ * @retval ERANGE If value coercion would overflow (or underflow)
+ * int16_t.
+ * @retval non-zero If reading @p name otherwise fails, a regular unix
+ * error code will be returned.
+ */
+int
+bhnd_nvram_getvar_int16(device_t dev, const char *name, int16_t *value)
+{
+ return (bhnd_nvram_getvar_int(dev, name, value, sizeof(*value)));
+}
+
+/**
+ * Read an NVRAM variable's signed 32-bit integer value.
+ *
+ * @param dev A bhnd bus child device.
+ * @param name The NVRAM variable name.
+ * @param[out] value On success, the requested value will be written
+ * to this pointer.
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval ENODEV No valid NVRAM source could be found.
+ * @retval EFTYPE If the variable data cannot be coerced to a
+ * a valid integer representation.
+ * @retval ERANGE If value coercion would overflow (or underflow)
+ * int32_t.
+ * @retval non-zero If reading @p name otherwise fails, a regular unix
+ * error code will be returned.
+ */
+int
+bhnd_nvram_getvar_int32(device_t dev, const char *name, int32_t *value)
+{
+ return (bhnd_nvram_getvar_int(dev, name, value, sizeof(*value)));
+}
+
+
+/**
+ * Read an NVRAM variable's array value.
+ *
+ * @param dev A bhnd bus child device.
+ * @param name The NVRAM variable name.
+ * @param[out] buf A buffer large enough to hold @p size bytes.
+ * On success, the requested value will be written
+ * to this buffer.
+ * @param[in,out] size The required number of bytes to write to
+ * @p buf.
+ * @param type The desired array element data representation.
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval ENODEV No valid NVRAM source could be found.
+ * @retval ENXIO If less than @p size bytes are available.
+ * @retval ENOMEM If a buffer of @p size is too small to hold the
+ * requested value.
+ * @retval EFTYPE If the variable data cannot be coerced to a
+ * a valid instance of @p type.
+ * @retval ERANGE If value coercion would overflow (or underflow) a
+ * representation of @p type.
+ * @retval non-zero If reading @p name otherwise fails, a regular unix
+ * error code will be returned.
+ */
+int
+bhnd_nvram_getvar_array(device_t dev, const char *name, void *buf, size_t size,
+ bhnd_nvram_type type)
+{
+ size_t nbytes;
+ int error;
+
+ /* Attempt read */
+ nbytes = size;
+ if ((error = bhnd_nvram_getvar(dev, name, buf, &nbytes, type)))
+ return (error);
+
+ /* Verify that the expected number of bytes were fetched */
+ if (nbytes < size)
+ return (ENXIO);
+
+ return (0);
+}
+
+/**
* Using the bhnd(4) bus-level core information and a custom core name,
* populate @p dev's device description.
*
@@ -953,7 +1271,8 @@
/* nvram board_info population macros for bhnd_bus_generic_read_board_info() */
#define BHND_GV(_dest, _name) \
- bhnd_nvram_getvar(child, BHND_NVAR_ ## _name, &_dest, sizeof(_dest))
+ bhnd_nvram_getvar_uint(child, BHND_NVAR_ ## _name, &_dest, \
+ sizeof(_dest))
#define REQ_BHND_GV(_dest, _name) do { \
if ((error = BHND_GV(_dest, _name))) { \
@@ -1017,7 +1336,7 @@
*/
int
bhnd_bus_generic_get_nvram_var(device_t dev, device_t child, const char *name,
- void *buf, size_t *size)
+ void *buf, size_t *size, bhnd_nvram_type type)
{
device_t nvram;
device_t parent;
@@ -1027,14 +1346,14 @@
/* Look for a directly-attached NVRAM child */
if ((nvram = device_find_child(dev, "bhnd_nvram", -1)) != NULL)
- return BHND_NVRAM_GETVAR(nvram, name, buf, size);
+ return BHND_NVRAM_GETVAR(nvram, name, buf, size, type);
/* Try to delegate to parent */
if ((parent = device_get_parent(dev)) == NULL)
return (ENODEV);
return (BHND_BUS_GET_NVRAM_VAR(device_get_parent(dev), child,
- name, buf, size));
+ name, buf, size, type));
}
/**
Index: head/sys/dev/bhnd/bhnd_types.h
===================================================================
--- head/sys/dev/bhnd/bhnd_types.h
+++ head/sys/dev/bhnd/bhnd_types.h
@@ -34,6 +34,8 @@
#include <sys/types.h>
+#include "nvram/bhnd_nvram.h"
+
/** bhnd(4) device classes. */
typedef enum {
BHND_DEVCLASS_CC, /**< chipcommon i/o controller */
Index: head/sys/dev/bhnd/bhndvar.h
===================================================================
--- head/sys/dev/bhnd/bhndvar.h
+++ head/sys/dev/bhnd/bhndvar.h
@@ -92,6 +92,6 @@
int bhnd_generic_get_nvram_var(device_t dev,
device_t child, const char *name, void *buf,
- size_t *size);
+ size_t *size, bhnd_nvram_type type);
#endif /* _BHND_BHNDVAR_H_ */
Index: head/sys/dev/bhnd/cores/chipc/chipc.c
===================================================================
--- head/sys/dev/bhnd/cores/chipc/chipc.c
+++ head/sys/dev/bhnd/cores/chipc/chipc.c
@@ -369,19 +369,11 @@
{
uint32_t otp_st, srom_ctrl;
- /* Very early devices vend SPROM/OTP/CIS (if at all) via the
- * host bridge interface instead of ChipCommon. */
- if (!CHIPC_QUIRK(sc, SUPPORTS_SPROM))
- return (BHND_NVRAM_SRC_UNKNOWN);
-
/*
- * Later chipset revisions standardized the SPROM capability flags and
- * register interfaces.
- *
* We check for hardware presence in order of precedence. For example,
* SPROM is is always used in preference to internal OTP if found.
*/
- if (caps->sprom) {
+ if (CHIPC_QUIRK(sc, SUPPORTS_SPROM) && caps->sprom) {
srom_ctrl = bhnd_bus_read_4(sc->core, CHIPC_SPROM_CTRL);
if (srom_ctrl & CHIPC_SRC_PRESENT)
return (BHND_NVRAM_SRC_SPROM);
Index: head/sys/dev/bhnd/nvram/bhnd_nvram.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram.h
+++ head/sys/dev/bhnd/nvram/bhnd_nvram.h
@@ -67,4 +67,50 @@
*/
} bhnd_nvram_src;
+/** Supported NVRAM formats. */
+typedef enum {
+ BHND_NVRAM_FMT_BCM = 0, /**< Broadcom NUL-delimited key=value pairs */
+ BHND_NVRAM_FMT_TLV = 1, /**< CFE TLV encoding, as used on WGT634U */
+ BHND_NVRAM_FMT_BTXT = 2, /**< Broadcom board text file. This is used
+ to provide external NVRAM data for some
+ fullmac WiFi devices. */
+ BHND_NVRAM_FMT_SPROM = 3, /**< SPROM/OTP-specific encoding used by
+ Broadcom network adapters */
+ BHND_NVRAM_FMT_CIS = 4, /**< A mostly CIS-compatible encoding used
+ on some Broadcom network adapters */
+ BHND_NVRAM_FMT_UNKNOWN = 5 /**< Unknown or unrecognized format */
+} bhnd_nvram_format;
+
+
+/** bhnd_nvram_type bit flags */
+enum {
+ BHND_NVRAM_TF_SIGNED = (1<<7),
+};
+
+#define BHND_NVRAM_TYPE_ID_MASK 0xF
+#define BHND_NVRAM_TYPE_FLAGS_MASK 0x70
+
+#define BHND_NVRAM_TYPE_ID(_id, _flags) \
+ (((_id) & BHND_NVRAM_TYPE_ID_MASK) | \
+ ((_flags) & BHND_NVRAM_TYPE_FLAGS_MASK))
+
+/** Supported NVRAM data types */
+typedef enum {
+ BHND_NVRAM_TYPE_UINT8 = BHND_NVRAM_TYPE_ID(0, 0), /**< unsigned 8-bit integer */
+ BHND_NVRAM_TYPE_UINT16 = BHND_NVRAM_TYPE_ID(1, 0), /**< unsigned 16-bit integer */
+ BHND_NVRAM_TYPE_UINT32 = BHND_NVRAM_TYPE_ID(2, 0), /**< unsigned 32-bit integer */
+ BHND_NVRAM_TYPE_INT8 = BHND_NVRAM_TYPE_ID(4, BHND_NVRAM_TF_SIGNED), /**< signed 8-bit integer */
+ BHND_NVRAM_TYPE_INT16 = BHND_NVRAM_TYPE_ID(5, BHND_NVRAM_TF_SIGNED), /**< signed 16-bit integer */
+ BHND_NVRAM_TYPE_INT32 = BHND_NVRAM_TYPE_ID(6, BHND_NVRAM_TF_SIGNED), /**< signed 32-bit integer */
+ BHND_NVRAM_TYPE_CHAR = BHND_NVRAM_TYPE_ID(7, BHND_NVRAM_TF_SIGNED), /**< ASCII character */
+ BHND_NVRAM_TYPE_CSTR = BHND_NVRAM_TYPE_ID(8, 0), /**< NUL-terminated C string */
+} bhnd_nvram_type;
+
+#undef BHND_NVRAM_TYPE_ID_MASK
+#undef BHND_NVRAM_TYPE_FLAGS_MASK
+#undef BHND_NVRAM_TYPE_ID
+
+#define BHND_NVRAM_SIGNED_TYPE(_type) \
+ (((_type) & BHND_NVRAM_TF_SIGNED) == BHND_NVRAM_TF_SIGNED)
+
#endif /* _BHND_NVRAM_BHND_NVRAM_H_ */
Index: head/sys/dev/bhnd/nvram/bhnd_nvram.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram.c
+++ head/sys/dev/bhnd/nvram/bhnd_nvram.c
@@ -0,0 +1,189 @@
+/*-
+ * Copyright (c) 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$");
+
+/*
+ * BHND CFE NVRAM driver.
+ *
+ * Provides access to device NVRAM via CFE.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/limits.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/bhnd/bhnd.h>
+
+#include <dev/cfe/cfe_api.h>
+#include <dev/cfe/cfe_error.h>
+#include <dev/cfe/cfe_ioctl.h>
+
+#include "bhnd_nvram_if.h"
+
+#include "bhnd_nvramvar.h"
+
+/**
+ * Default bhnd_nvram driver implementation of DEVICE_PROBE().
+ */
+int
+bhnd_nvram_probe(device_t dev)
+{
+ device_set_desc(dev, "Broadcom NVRAM");
+
+ /* Refuse wildcard attachments */
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+/**
+ * Call from subclass DEVICE_ATTACH() implementations to handle
+ * device attachment.
+ *
+ * @param dev BHND NVRAM device.
+ * @param data NVRAM data to be copied and parsed. No reference to data
+ * will be held after return.
+ * @param size Size of @p data, in bytes.
+ * @param fmt NVRAM format.
+ */
+int
+bhnd_nvram_attach(device_t dev, void *data, size_t size, bhnd_nvram_format fmt)
+{
+ struct bhnd_nvram_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ /* Initialize NVRAM parser */
+ error = bhnd_nvram_parser_init(&sc->nvram, dev, data, size, fmt);
+ if (error)
+ return (error);
+
+ /* Initialize mutex */
+ BHND_NVRAM_LOCK_INIT(sc);
+
+ return (0);
+}
+
+/**
+ * Default bhnd_nvram driver implementation of DEVICE_RESUME().
+ */
+int
+bhnd_nvram_resume(device_t dev)
+{
+ return (0);
+}
+
+/**
+ * Default bhnd_nvram driver implementation of DEVICE_SUSPEND().
+ */
+int
+bhnd_nvram_suspend(device_t dev)
+{
+ return (0);
+}
+
+/**
+ * Default bhnd_nvram driver implementation of DEVICE_DETACH().
+ */
+int
+bhnd_nvram_detach(device_t dev)
+{
+ struct bhnd_nvram_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bhnd_nvram_parser_fini(&sc->nvram);
+ BHND_NVRAM_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+/**
+ * Default bhnd_nvram driver implementation of BHND_NVRAM_GETVAR().
+ */
+static int
+bhnd_nvram_getvar_method(device_t dev, const char *name, void *buf, size_t *len,
+ bhnd_nvram_type type)
+{
+ struct bhnd_nvram_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ BHND_NVRAM_LOCK(sc);
+ error = bhnd_nvram_parser_getvar(&sc->nvram, name, buf, len, type);
+ BHND_NVRAM_UNLOCK(sc);
+
+ return (error);
+}
+
+/**
+ * Default bhnd_nvram driver implementation of BHND_NVRAM_SETVAR().
+ */
+static int
+bhnd_nvram_setvar_method(device_t dev, const char *name, const void *buf,
+ size_t len, bhnd_nvram_type type)
+{
+ struct bhnd_nvram_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ BHND_NVRAM_LOCK(sc);
+ error = bhnd_nvram_parser_setvar(&sc->nvram, name, buf, len, type);
+ BHND_NVRAM_UNLOCK(sc);
+
+ return (error);
+}
+
+static device_method_t bhnd_nvram_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bhnd_nvram_probe),
+ DEVMETHOD(device_resume, bhnd_nvram_resume),
+ DEVMETHOD(device_suspend, bhnd_nvram_suspend),
+ DEVMETHOD(device_detach, bhnd_nvram_detach),
+
+ /* NVRAM interface */
+ DEVMETHOD(bhnd_nvram_getvar, bhnd_nvram_getvar_method),
+ DEVMETHOD(bhnd_nvram_setvar, bhnd_nvram_setvar_method),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(bhnd_nvram, bhnd_nvram_driver, bhnd_nvram_methods, sizeof(struct bhnd_nvram_softc));
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_cfe.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_cfe.c
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_cfe.c
@@ -0,0 +1,373 @@
+/*-
+ * Copyright (c) 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$");
+
+/*
+ * BHND CFE NVRAM driver.
+ *
+ * Provides access to device NVRAM via CFE.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/limits.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/bhnd/bhnd.h>
+
+#include <dev/cfe/cfe_api.h>
+#include <dev/cfe/cfe_error.h>
+#include <dev/cfe/cfe_ioctl.h>
+
+#include "bhnd_nvram_if.h"
+
+#include "bhnd_nvramvar.h"
+
+static int nvram_open_cfedev(device_t dev, char *devname, int fd,
+ int64_t *offset, uint32_t *size, bhnd_nvram_format fmt);
+static char *nvram_find_cfedev(device_t dev, int *fd, int64_t *offset,
+ uint32_t *size, bhnd_nvram_format *fmt);
+
+/** Known CFE NVRAM device names, in probe order. */
+static char *nvram_cfe_devs[] = {
+ "nflash0.nvram", /* NAND */
+ "nflash1.nvram",
+ "flash0.nvram",
+ "flash1.nvram",
+};
+
+/** Supported CFE NVRAM formats, in probe order. */
+bhnd_nvram_format nvram_cfe_fmts[] = {
+ BHND_NVRAM_FMT_BCM,
+ BHND_NVRAM_FMT_TLV
+};
+
+
+static int
+bhnd_nvram_cfe_probe(device_t dev)
+{
+ char *devname;
+ bhnd_nvram_format fmt;
+ int64_t offset;
+ uint32_t size;
+ int error;
+ int fd;
+
+ /* Defer to default driver implementation */
+ if ((error = bhnd_nvram_probe(dev)) > 0)
+ return (error);
+
+ /* Locate a usable CFE device */
+ devname = nvram_find_cfedev(dev, &fd, &offset, &size, &fmt);
+ if (devname == NULL)
+ return (ENXIO);
+ cfe_close(fd);
+
+ switch (fmt) {
+ case BHND_NVRAM_FMT_BCM:
+ device_set_desc(dev, "Broadcom NVRAM");
+ break;
+ case BHND_NVRAM_FMT_TLV:
+ device_set_desc(dev, "Broadcom WGT634U NVRAM");
+ break;
+ default:
+ device_printf(dev, "unknown NVRAM format: %d\n", fmt);
+ return (ENXIO);
+ }
+
+ /* Refuse wildcard attachments */
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+
+static int
+bhnd_nvram_cfe_attach(device_t dev)
+{
+ char *devname;
+ unsigned char *buffer;
+ bhnd_nvram_format fmt;
+ int64_t offset;
+ uint32_t size;
+ int error;
+ int fd;
+
+ error = 0;
+ buffer = NULL;
+ fd = CFE_ERR;
+
+ /* Locate NVRAM device via CFE */
+ devname = nvram_find_cfedev(dev, &fd, &offset, &size, &fmt);
+ if (devname == NULL) {
+ device_printf(dev, "CFE NVRAM device not found\n");
+ return (ENXIO);
+ }
+
+ /* Copy out NVRAM buffer */
+ buffer = malloc(size, M_TEMP, M_NOWAIT);
+ if (buffer == NULL)
+ return (ENOMEM);
+
+ for (size_t remain = size; remain > 0;) {
+ int nr, req;
+
+ req = ulmin(INT_MAX, remain);
+ nr = cfe_readblk(fd, size-remain, buffer+(size-remain),
+ req);
+ if (nr < 0) {
+ device_printf(dev, "%s: cfe_readblk() failed: %d\n",
+ devname, fd);
+
+ error = ENXIO;
+ goto cleanup;
+ }
+
+ remain -= nr;
+
+ if (nr == 0 && remain > 0) {
+ device_printf(dev, "%s: cfe_readblk() unexpected EOF: "
+ "%zu of %zu pending\n", devname, remain, size);
+
+ error = ENXIO;
+ goto cleanup;
+ }
+ }
+
+ device_printf(dev, "CFE %s (%#jx+%#jx)\n", devname, (uintmax_t)offset,
+ (uintmax_t)size);
+
+ /* Delegate to default driver implementation */
+ error = bhnd_nvram_attach(dev, buffer, size, fmt);
+
+cleanup:
+ if (buffer != NULL)
+ free(buffer, M_TEMP);
+
+ if (fd >= 0)
+ cfe_close(fd);
+
+ return (error);
+}
+
+/**
+ * Identify and open a CFE NVRAM device.
+ *
+ * @param dev bhnd_nvram_cfe device.
+ * @param devname The name of the CFE device to be probed.
+ * @param fd An open CFE file descriptor for @p devname.
+ * @param[out] offset On success, the NVRAM data offset within @p @fd.
+ * @param[out] size On success, maximum the NVRAM data size within @p fd.
+ * @param fmt The expected NVRAM data format for this device.
+ *
+ * @retval 0 success
+ * @retval non-zero If probing @p devname fails, a regular unix
+ * error code will be returned.
+ */
+static int
+nvram_open_cfedev(device_t dev, char *devname, int fd, int64_t *offset,
+ uint32_t *size, bhnd_nvram_format fmt)
+{
+ union bhnd_nvram_ident ident;
+ nvram_info_t nvram_info;
+ int cerr, devinfo, dtype, rlen;
+ int error;
+
+ /* Try to fetch device info */
+ if ((devinfo = cfe_getdevinfo(devname)) == CFE_ERR_DEVNOTFOUND)
+ return (ENODEV);
+
+ if (devinfo < 0) {
+ device_printf(dev, "cfe_getdevinfo() failed: %d",
+ devinfo);
+ return (ENXIO);
+ }
+
+ /* Verify device type */
+ dtype = devinfo & CFE_DEV_MASK;
+ switch (dtype) {
+ case CFE_DEV_FLASH:
+ case CFE_DEV_NVRAM:
+ /* Valid device type */
+ break;
+ default:
+ device_printf(dev, "%s: unknown device type %d\n",
+ devname, dtype);
+ return (ENXIO);
+ }
+
+ /* Try to fetch nvram info from CFE */
+ cerr = cfe_ioctl(fd, IOCTL_NVRAM_GETINFO, (unsigned char *)&nvram_info,
+ sizeof(nvram_info), &rlen, 0);
+ if (cerr != CFE_OK && cerr != CFE_ERR_INV_COMMAND) {
+ device_printf(dev, "%s: IOCTL_NVRAM_GETINFO failed: %d\n",
+ devname, cerr);
+ return (ENXIO);
+ }
+
+ /* Fall back on flash info.
+ *
+ * This is known to be required on the Asus RT-N53 (CFE 5.70.55.33,
+ * BBP 1.0.37, BCM5358UB0), where IOCTL_NVRAM_GETINFO returns
+ * CFE_ERR_INV_COMMAND.
+ */
+ if (cerr == CFE_ERR_INV_COMMAND) {
+ flash_info_t fi;
+
+ cerr = cfe_ioctl(fd, IOCTL_FLASH_GETINFO, (unsigned char *)&fi,
+ sizeof(fi), &rlen, 0);
+
+ if (cerr != CFE_OK) {
+ device_printf(dev, "%s: IOCTL_FLASH_GETINFO failed: "
+ "%d\n", devname, cerr);
+ return (ENXIO);
+ }
+
+ nvram_info.nvram_eraseflg =
+ !(fi.flash_flags & FLASH_FLAG_NOERASE);
+ nvram_info.nvram_offset = 0x0;
+ nvram_info.nvram_size = fi.flash_size;
+ }
+
+ /* Try to read NVRAM header/format identification */
+ cerr = cfe_readblk(fd, 0, (unsigned char *)&ident, sizeof(ident));
+ if (cerr < 0) {
+ device_printf(dev, "%s: cfe_readblk() failed: %d\n",
+ devname, cerr);
+ return (ENXIO);
+ } else if (cerr == 0) {
+ /* EOF */
+ return (ENODEV);
+ } else if (cerr != sizeof(ident)) {
+ device_printf(dev, "%s: cfe_readblk() short read: %d\n",
+ devname, cerr);
+ return (ENXIO);
+ }
+
+ /* Verify expected format */
+ if ((error = bhnd_nvram_parser_identify(&ident, fmt)))
+ return (error);
+
+ /* Provide offset and size */
+ switch (fmt) {
+ case BHND_NVRAM_FMT_TLV:
+ /* No size field is available; must assume the NVRAM data
+ * consumes up to the full CFE NVRAM range */
+ *offset = nvram_info.nvram_offset;
+ *size = nvram_info.nvram_size;
+ break;
+ case BHND_NVRAM_FMT_BCM:
+ if (ident.bcm.size > nvram_info.nvram_size) {
+ device_printf(dev, "%s: NVRAM size %#x overruns %#x "
+ "device limit\n", devname, ident.bcm.size,
+ nvram_info.nvram_size);
+ return (ENODEV);
+ }
+
+ *offset = nvram_info.nvram_offset;
+ *size = ident.bcm.size;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+/**
+ * Find (and open) a CFE NVRAM device.
+ *
+ * @param dev bhnd_nvram_cfe device.
+ * @param[out] fd On success, a valid CFE file descriptor. The callee
+ * is responsible for closing this file descriptor via
+ * cfe_close().
+ * @param[out] offset On success, the NVRAM data offset within @p @fd.
+ * @param[out] size On success, maximum the NVRAM data size within @p fd.
+ * @param fmt The expected NVRAM data format for this device.
+ *
+ * @return On success, the opened CFE device's name will be returned. On
+ * error, returns NULL.
+ */
+static char *
+nvram_find_cfedev(device_t dev, int *fd, int64_t *offset,
+ uint32_t *size, bhnd_nvram_format *fmt)
+{
+ char *devname;
+ int error;
+
+ for (u_int i = 0; i < nitems(nvram_cfe_fmts); i++) {
+ *fmt = nvram_cfe_fmts[i];
+
+ for (u_int j = 0; j < nitems(nvram_cfe_devs); j++) {
+ devname = nvram_cfe_devs[j];
+
+ /* Open for reading */
+ *fd = cfe_open(devname);
+ if (*fd == CFE_ERR_DEVNOTFOUND) {
+ continue;
+ } else if (*fd < 0) {
+ device_printf(dev, "%s: cfe_open() failed: "
+ "%d\n", devname, *fd);
+ continue;
+ }
+
+ /* Probe */
+ error = nvram_open_cfedev(dev, devname, *fd, offset,
+ size, *fmt);
+ if (error == 0)
+ return (devname);
+
+ /* Keep searching */
+ devname = NULL;
+ cfe_close(*fd);
+ }
+ }
+
+ return (NULL);
+}
+
+static device_method_t bhnd_nvram_cfe_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bhnd_nvram_cfe_probe),
+ DEVMETHOD(device_attach, bhnd_nvram_cfe_attach),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(bhnd_nvram, bhnd_nvram_cfe, bhnd_nvram_cfe_methods,
+ sizeof(struct bhnd_nvram_softc), bhnd_nvram_driver);
+EARLY_DRIVER_MODULE(bhnd_nvram_cfe, nexus, bhnd_nvram_cfe,
+ bhnd_nvram_devclass, NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_common.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_common.h
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_common.h
@@ -0,0 +1,180 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BHND_NVRAM_BHND_NVRAM_COMMON_H_
+#define _BHND_NVRAM_BHND_NVRAM_COMMON_H_
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+
+#include "bhnd_nvram.h"
+
+struct bhnd_nvram_tuple;
+struct bhnd_nvram_varmap;
+
+struct bhnd_nvram_vardefn;
+
+MALLOC_DECLARE(M_BHND_NVRAM);
+
+extern const uint8_t bhnd_nvram_crc8_tab[];
+
+#define BHND_NVRAM_CRC8_INITIAL 0xFF /**< Initial bhnd_nvram_crc8 value */
+#define BHND_NVRAM_CRC8_VALID 0x9F /**< Valid CRC-8 checksum */
+#define BHND_SPROMREV_MAX UINT8_MAX /**< maximum supported SPROM revision */
+
+
+/** NVRAM data type string representations */
+typedef enum {
+ BHND_NVRAM_SFMT_HEX = 1, /**< hex format */
+ BHND_NVRAM_SFMT_DEC = 2, /**< decimal format */
+ BHND_NVRAM_SFMT_MACADDR = 3, /**< mac address (canonical form, hex octets,
+ separated with ':') */
+ BHND_NVRAM_SFMT_LEDDC = 4, /**< LED PWM duty-cycle (2 bytes -- on/off) */
+ BHND_NVRAM_SFMT_CCODE = 5 /**< count code format (2-3 ASCII chars, or hex string) */
+} bhnd_nvram_sfmt;
+
+size_t bhnd_nvram_type_width(bhnd_nvram_type type);
+const char *bhnd_nvram_type_fmt(bhnd_nvram_type type,
+ bhnd_nvram_sfmt sfmt, size_t elem_num);
+
+const struct bhnd_nvram_vardefn *bhnd_nvram_find_vardefn(const char *varname);
+
+bool bhnd_nvram_validate_name(const char *name,
+ size_t name_len);
+int bhnd_nvram_parse_octet_string(
+ const char *value, size_t value_len,
+ void *buf, size_t *len,
+ bhnd_nvram_type type);
+
+int bhnd_nvram_varmap_init(
+ struct bhnd_nvram_varmap *map,
+ size_t nelements, int flags);
+void bhnd_nvram_varmap_free(
+ struct bhnd_nvram_varmap *map);
+int bhnd_nvram_varmap_add(
+ struct bhnd_nvram_varmap *map,
+ const char *name, const char *value,
+ size_t value_len);
+int bhnd_nvram_varmap_remove(
+ struct bhnd_nvram_varmap *map,
+ const char *name);
+struct bhnd_nvram_tuple *bhnd_nvram_varmap_find(
+ struct bhnd_nvram_varmap *map,
+ const char *name, size_t name_len);
+bool bhnd_nvram_varmap_contains(
+ struct bhnd_nvram_varmap *map,
+ const char *name, size_t name_len);
+
+struct bhnd_nvram_tuple *bhnd_nvram_tuple_alloc(const char *name,
+ const char *value);
+void bhnd_nvram_tuple_free(
+ struct bhnd_nvram_tuple *tuple);
+
+/** NVRAM variable flags */
+enum {
+ BHND_NVRAM_VF_ARRAY = (1<<0), /**< variable is an array */
+ BHND_NVRAM_VF_MFGINT = (1<<1), /**< mfg-internal variable; should not be externally visible */
+ BHND_NVRAM_VF_IGNALL1 = (1<<2) /**< hide variable if its value has all bits set. */
+};
+
+/** SPROM revision compatibility declaration */
+struct bhnd_sprom_compat {
+ uint8_t first; /**< first compatible SPROM revision */
+ uint8_t last; /**< last compatible SPROM revision, or BHND_SPROMREV_MAX */
+};
+
+/** SPROM value descriptor */
+struct bhnd_sprom_offset {
+ uint16_t offset; /**< byte offset within SPROM */
+ bool cont:1; /**< value should be bitwise OR'd with the
+ * previous offset descriptor */
+ bhnd_nvram_type type:7; /**< data type */
+ int8_t shift; /**< shift to be applied to the value */
+ uint32_t mask; /**< mask to be applied to the value(s) */
+};
+
+/** SPROM-specific variable definition */
+struct bhnd_sprom_vardefn {
+ struct bhnd_sprom_compat compat; /**< sprom compatibility declaration */
+ const struct bhnd_sprom_offset *offsets; /**< offset descriptors */
+ size_t num_offsets; /**< number of offset descriptors */
+};
+
+/** NVRAM variable definition */
+struct bhnd_nvram_vardefn {
+ const char *name; /**< variable name */
+ bhnd_nvram_type type; /**< base data type */
+ bhnd_nvram_sfmt sfmt; /**< string format */
+ uint32_t flags; /**< BHND_NVRAM_VF_* flags */
+
+ const struct bhnd_sprom_vardefn *sp_defs; /**< SPROM-specific variable definitions */
+ size_t num_sp_defs; /**< number of sprom definitions */
+};
+
+/**
+ * NVRAM value tuple.
+ */
+struct bhnd_nvram_tuple {
+ char *name; /**< variable name. */
+ size_t name_len; /**< variable length. */
+ char *value; /**< value, or NULL if this tuple represents variable
+ deletion */
+ size_t value_len; /**< value length. */
+
+ LIST_ENTRY(bhnd_nvram_tuple) t_link;
+};
+
+LIST_HEAD(bhnd_nvram_tuples, bhnd_nvram_tuple);
+
+/** NVRAM tuple hash table */
+struct bhnd_nvram_varmap {
+ struct bhnd_nvram_tuples *table; /**< hash buckets */
+ u_long mask; /**< hash index mask */
+};
+
+/**
+ * Calculate CRC-8 over @p buf.
+ *
+ * @param buf input buffer
+ * @param size buffer size
+ * @param crc last computed crc, or BHND_NVRAM_CRC8_INITIAL
+ */
+static inline uint8_t
+bhnd_nvram_crc8(const void *buf, size_t size, uint8_t crc)
+{
+ const uint8_t *p = (const uint8_t *)buf;
+ while (size--)
+ crc = bhnd_nvram_crc8_tab[(crc ^ *p++)];
+
+ return (crc);
+}
+
+#endif /* _BHND_NVRAM_BHND_NVRAM_COMMON_H_ */
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_common.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_common.c
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_common.c
@@ -0,0 +1,661 @@
+/*-
+ * Copyright (c) 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/ctype.h>
+#include <sys/hash.h>
+
+#include <sys/param.h>
+#include <sys/limits.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+
+#include <machine/_inttypes.h>
+
+#include "bhnd_nvram_common.h"
+
+#include "bhnd_nvram_map_data.h"
+
+/*
+ * Common NVRAM/SPROM support, including NVRAM variable map
+ * lookup.
+ */
+
+MALLOC_DEFINE(M_BHND_NVRAM, "bhnd_nvram", "bhnd nvram data");
+
+/*
+ * CRC-8 lookup table used to checksum SPROM and NVRAM data via
+ * bhnd_nvram_crc8().
+ *
+ * Generated with following parameters:
+ * polynomial: CRC-8 (x^8 + x^7 + x^6 + x^4 + x^2 + 1)
+ * reflected bits: false
+ * reversed: true
+ */
+const uint8_t bhnd_nvram_crc8_tab[] = {
+ 0x00, 0xf7, 0xb9, 0x4e, 0x25, 0xd2, 0x9c, 0x6b, 0x4a, 0xbd, 0xf3,
+ 0x04, 0x6f, 0x98, 0xd6, 0x21, 0x94, 0x63, 0x2d, 0xda, 0xb1, 0x46,
+ 0x08, 0xff, 0xde, 0x29, 0x67, 0x90, 0xfb, 0x0c, 0x42, 0xb5, 0x7f,
+ 0x88, 0xc6, 0x31, 0x5a, 0xad, 0xe3, 0x14, 0x35, 0xc2, 0x8c, 0x7b,
+ 0x10, 0xe7, 0xa9, 0x5e, 0xeb, 0x1c, 0x52, 0xa5, 0xce, 0x39, 0x77,
+ 0x80, 0xa1, 0x56, 0x18, 0xef, 0x84, 0x73, 0x3d, 0xca, 0xfe, 0x09,
+ 0x47, 0xb0, 0xdb, 0x2c, 0x62, 0x95, 0xb4, 0x43, 0x0d, 0xfa, 0x91,
+ 0x66, 0x28, 0xdf, 0x6a, 0x9d, 0xd3, 0x24, 0x4f, 0xb8, 0xf6, 0x01,
+ 0x20, 0xd7, 0x99, 0x6e, 0x05, 0xf2, 0xbc, 0x4b, 0x81, 0x76, 0x38,
+ 0xcf, 0xa4, 0x53, 0x1d, 0xea, 0xcb, 0x3c, 0x72, 0x85, 0xee, 0x19,
+ 0x57, 0xa0, 0x15, 0xe2, 0xac, 0x5b, 0x30, 0xc7, 0x89, 0x7e, 0x5f,
+ 0xa8, 0xe6, 0x11, 0x7a, 0x8d, 0xc3, 0x34, 0xab, 0x5c, 0x12, 0xe5,
+ 0x8e, 0x79, 0x37, 0xc0, 0xe1, 0x16, 0x58, 0xaf, 0xc4, 0x33, 0x7d,
+ 0x8a, 0x3f, 0xc8, 0x86, 0x71, 0x1a, 0xed, 0xa3, 0x54, 0x75, 0x82,
+ 0xcc, 0x3b, 0x50, 0xa7, 0xe9, 0x1e, 0xd4, 0x23, 0x6d, 0x9a, 0xf1,
+ 0x06, 0x48, 0xbf, 0x9e, 0x69, 0x27, 0xd0, 0xbb, 0x4c, 0x02, 0xf5,
+ 0x40, 0xb7, 0xf9, 0x0e, 0x65, 0x92, 0xdc, 0x2b, 0x0a, 0xfd, 0xb3,
+ 0x44, 0x2f, 0xd8, 0x96, 0x61, 0x55, 0xa2, 0xec, 0x1b, 0x70, 0x87,
+ 0xc9, 0x3e, 0x1f, 0xe8, 0xa6, 0x51, 0x3a, 0xcd, 0x83, 0x74, 0xc1,
+ 0x36, 0x78, 0x8f, 0xe4, 0x13, 0x5d, 0xaa, 0x8b, 0x7c, 0x32, 0xc5,
+ 0xae, 0x59, 0x17, 0xe0, 0x2a, 0xdd, 0x93, 0x64, 0x0f, 0xf8, 0xb6,
+ 0x41, 0x60, 0x97, 0xd9, 0x2e, 0x45, 0xb2, 0xfc, 0x0b, 0xbe, 0x49,
+ 0x07, 0xf0, 0x9b, 0x6c, 0x22, 0xd5, 0xf4, 0x03, 0x4d, 0xba, 0xd1,
+ 0x26, 0x68, 0x9f
+};
+
+/**
+ * Return the size of type @p type, or 0 if @p type has a variable width
+ * (e.g. a C string).
+ *
+ * @param type NVRAM data type.
+ * @result the byte width of @p type.
+ */
+size_t
+bhnd_nvram_type_width(bhnd_nvram_type type)
+{
+ switch (type) {
+ case BHND_NVRAM_TYPE_INT8:
+ case BHND_NVRAM_TYPE_UINT8:
+ case BHND_NVRAM_TYPE_CHAR:
+ return (sizeof(uint8_t));
+
+ case BHND_NVRAM_TYPE_INT16:
+ case BHND_NVRAM_TYPE_UINT16:
+ return (sizeof(uint16_t));
+
+ case BHND_NVRAM_TYPE_INT32:
+ case BHND_NVRAM_TYPE_UINT32:
+ return (sizeof(uint32_t));
+
+ case BHND_NVRAM_TYPE_CSTR:
+ return (0);
+ }
+
+ /* Quiesce gcc4.2 */
+ panic("bhnd nvram type %u unknown", type);
+}
+
+/**
+ * Return the format string to use when printing @p type with @p sfmt
+ *
+ * @param type The value type being printed.
+ * @param sfmt The string format required for @p type.
+ * @param elem_num The element index being printed. If this is the first
+ * value in an array of elements, the index would be 0, the next would be 1,
+ * and so on.
+ *
+ * @retval non-NULL A valid printf format string.
+ * @retval NULL If no format string is available for @p type and @p sfmt.
+ */
+const char *
+bhnd_nvram_type_fmt(bhnd_nvram_type type, bhnd_nvram_sfmt sfmt,
+ size_t elem_num)
+{
+ size_t width;
+
+ width = bhnd_nvram_type_width(type);
+
+ /* Sanity-check the type width */
+ switch (width) {
+ case 1:
+ case 2:
+ case 4:
+ break;
+ default:
+ return (NULL);
+ }
+
+ /* Special-cased string formats */
+ switch (sfmt) {
+ case BHND_NVRAM_SFMT_LEDDC:
+ /* If this is the first element, use the 0x-prefixed
+ * SFMT_HEX */
+ if (elem_num == 0)
+ sfmt = BHND_NVRAM_SFMT_HEX;
+ break;
+ default:
+ break;
+ }
+
+ /* Return the format string */
+ switch (sfmt) {
+ case BHND_NVRAM_SFMT_MACADDR:
+ switch (width) {
+ case 1: return ("%02" PRIx8);
+ }
+ break;
+
+ case BHND_NVRAM_SFMT_HEX:
+ switch (width) {
+ case 1: return ("0x%02" PRIx8);
+ case 2: return ("0x%04" PRIx16);
+ case 4: return ("0x%08" PRIx32);
+ }
+ break;
+ case BHND_NVRAM_SFMT_DEC:
+ if (BHND_NVRAM_SIGNED_TYPE(type)) {
+ switch (width) {
+ case 1: return ("%" PRId8);
+ case 2: return ("%" PRId16);
+ case 4: return ("%" PRId32);
+ }
+ } else {
+ switch (width) {
+ case 1: return ("%" PRIu8);
+ case 2: return ("%" PRIu16);
+ case 4: return ("%" PRIu32);
+ }
+ }
+ break;
+ case BHND_NVRAM_SFMT_LEDDC:
+ switch (width) {
+ case 1: return ("%02" PRIx8);
+ case 2: return ("%04" PRIx16);
+ case 4: return ("%08" PRIx32);
+ }
+ break;
+
+ case BHND_NVRAM_SFMT_CCODE:
+ switch (width) {
+ case 1: return ("%c");
+ }
+ break;
+ }
+
+ return (NULL);
+}
+
+/**
+ * Find and return the variable definition for @p varname, if any.
+ *
+ * @param varname variable name
+ *
+ * @retval bhnd_nvram_vardefn If a valid definition for @p varname is found.
+ * @retval NULL If no definition for @p varname is found.
+ */
+const struct bhnd_nvram_vardefn *
+bhnd_nvram_find_vardefn(const char *varname)
+{
+ size_t min, mid, max;
+ int order;
+
+ /*
+ * Locate the requested variable using a binary search.
+ *
+ * The variable table is guaranteed to be sorted in lexicographical
+ * order (using the 'C' locale for collation rules)
+ */
+ min = 0;
+ mid = 0;
+ max = nitems(bhnd_nvram_vardefs) - 1;
+
+ while (max >= min) {
+ /* Select midpoint */
+ mid = (min + max) / 2;
+
+ /* Determine which side of the partition to search */
+ order = strcmp(bhnd_nvram_vardefs[mid].name, varname);
+ if (order < 0) {
+ /* Search upper partition */
+ min = mid + 1;
+ } else if (order > 0) {
+ /* Search lower partition */
+ max = mid - 1;
+ } else if (order == 0) {
+ /* Match found */
+ return (&bhnd_nvram_vardefs[mid]);
+ }
+ }
+
+ /* Not found */
+ return (NULL);
+}
+
+/**
+ * Validate an NVRAM variable name.
+ *
+ * Scans for special characters (path delimiters, value delimiters, path
+ * alias prefixes), returning false if the given name cannot be used
+ * as a relative NVRAM key.
+ *
+ * @param name A relative NVRAM variable name to validate.
+ * @param name_len The length of @p name, in bytes.
+ *
+ * @retval true If @p name is a valid relative NVRAM key.
+ * @retval false If @p name should not be used as a relative NVRAM key.
+ */
+bool
+bhnd_nvram_validate_name(const char *name, size_t name_len)
+{
+ size_t limit;
+
+ limit = strnlen(name, name_len);
+ if (limit == 0)
+ return (false);
+
+ /* Disallow path alias prefixes ([0-9]+:.*) */
+ if (limit >= 2 && isdigit(*name)) {
+ for (const char *p = name; p - name < limit; p++) {
+ if (isdigit(*p))
+ continue;
+ else if (*p == ':')
+ return (false);
+ else
+ break;
+ }
+ }
+
+ /* Scan for special characters */
+ for (const char *p = name; p - name < limit; p++) {
+ switch (*p) {
+ case '/': /* path delimiter */
+ case '=': /* key=value delimiter */
+ return (false);
+
+ default:
+ if (isspace(*p) || !isascii(*p))
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
+/**
+ * Parse an octet string, such as a MAC address, consisting of hex octets
+ * separated with ':' or '-'.
+ *
+ * @param value The octet string to parse.
+ * @param value_len The length of @p value, in bytes.
+ * @param buf The output buffer to which parsed octets will be written. May be
+ * NULL.
+ * @param[in,out] len The capacity of @p buf. On success, will be set
+ * to the actual size of the requested value.
+ * @param type
+ */
+int
+bhnd_nvram_parse_octet_string(const char *value, size_t value_len, void *buf,
+ size_t *len, bhnd_nvram_type type)
+{
+ size_t limit, nbytes, width;
+ size_t slen;
+ uint8_t octet;
+ char delim;
+
+ slen = strnlen(value, value_len);
+
+ nbytes = 0;
+ if (buf != NULL)
+ limit = *len;
+ else
+ limit = 0;
+
+ /* Type must have a fixed width */
+ if ((width = bhnd_nvram_type_width(type)) == 0)
+ return (EINVAL);
+
+ /* String length (not including NUL) must be aligned on an octet
+ * boundary ('AA:BB', not 'AA:B', etc), and must be large enough
+ * to contain at least two octet entries. */
+ if (slen % 3 != 2 || slen < sizeof("AA:BB") - 1)
+ return (EINVAL);
+
+ /* Identify the delimiter used. The standard delimiter for
+ * MAC addresses is ':', but some earlier NVRAM formats may use
+ * '-' */
+ switch ((delim = value[2])) {
+ case ':':
+ case '-':
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ /* Parse octets */
+ for (const char *p = value; p - value < value_len; p++) {
+ void *outp;
+ size_t pos;
+ unsigned char c;
+
+ pos = (p - value);
+
+ /* Skip delimiter after each octet */
+ if (pos % 3 == 2) {
+ if (*p == delim)
+ continue;
+
+ if (*p == '\0')
+ return (0);
+
+ /* No delimiter? */
+ return (EINVAL);
+ }
+
+ c = *(const unsigned char *)p;
+
+ if (isdigit(c))
+ c -= '0';
+ else if (isxdigit(c))
+ c -= islower(c) ? 'a' - 10 : 'A' - 10;
+ else
+ return (EINVAL);
+
+ if (pos % 3 == 0) {
+ /* MSB */
+ octet = (c << 4);
+ continue;
+ } else if (pos % 3 == 1) {
+ /* LSB */
+ octet |= (c & 0xF);
+ }
+
+ /* Skip writing? */
+ if (limit < width || limit - width < nbytes) {
+ nbytes += width;
+ continue;
+ }
+
+ /* Write output */
+ outp = ((uint8_t *)buf) + nbytes;
+ switch (type) {
+ case BHND_NVRAM_TYPE_UINT8:
+ *(uint8_t *)outp = octet;
+ break;
+
+ case BHND_NVRAM_TYPE_UINT16:
+ *(uint16_t *)outp = octet;
+ break;
+
+ case BHND_NVRAM_TYPE_UINT32:
+ *(uint32_t *)outp = octet;
+ break;
+
+ case BHND_NVRAM_TYPE_INT8:
+ if (octet > INT8_MAX)
+ return (ERANGE);
+ *(int8_t *)outp = (int8_t)octet;
+ break;
+
+ case BHND_NVRAM_TYPE_INT16:
+ *(int16_t *)outp = (int8_t)octet;
+ break;
+
+ case BHND_NVRAM_TYPE_INT32:
+ *(int32_t *)outp = (int8_t)octet;
+ break;
+
+ case BHND_NVRAM_TYPE_CHAR:
+#if (CHAR_MAX < UINT8_MAX)
+ if (octet > CHAR_MAX)
+ return (ERANGE);
+#endif
+ *(char *)outp = (char)octet;
+ break;
+ default:
+ printf("unknown type %d\n", type);
+ return (EINVAL);
+ }
+
+ nbytes += width;
+ }
+
+ return (0);
+}
+
+
+/**
+ * Initialize a new variable hash table with @p nelements.
+ *
+ * @param map Hash table instance to be initialized.
+ * @param nelements The number of hash table buckets to allocate.
+ * @param flags Hash table flags (HASH_*).
+ */
+int
+bhnd_nvram_varmap_init(struct bhnd_nvram_varmap *map, size_t nelements,
+ int flags)
+{
+ map->table = hashinit_flags(nelements, M_BHND_NVRAM, &map->mask,
+ flags);
+ if (map->table == NULL)
+ return (ENOMEM);
+
+ return (0);
+}
+
+/**
+ * Deallocate all resources associated with @p map.
+ *
+ * @param map Hash table to be deallocated.
+ */
+void
+bhnd_nvram_varmap_free(struct bhnd_nvram_varmap *map)
+{
+ struct bhnd_nvram_tuple *t, *tnext;
+
+ /* Free all elements */
+ for (size_t i = 0; i <= map->mask; i++) {
+ LIST_FOREACH_SAFE(t, &map->table[i], t_link, tnext) {
+ LIST_REMOVE(t, t_link);
+ bhnd_nvram_tuple_free(t);
+ }
+ }
+
+ /* Free hash table */
+ hashdestroy(map->table, M_BHND_NVRAM, map->mask);
+}
+
+/**
+ * Add a variable entry to @p map.
+ *
+ * @param map Hash table to modify.
+ * @param name Variable name.
+ * @param value Variable value.
+ * @param value_len The length of @p value, in bytes.
+ *
+ * @retval 0 success
+ * @retval ENOMEM unable to allocate new entry
+ */
+int
+bhnd_nvram_varmap_add(struct bhnd_nvram_varmap *map, const char *name,
+ const char *value, size_t value_len)
+{
+ struct bhnd_nvram_tuples *head;
+ struct bhnd_nvram_tuple *t;
+
+ /* Locate target bucket */
+ head = &map->table[hash32_str(name, HASHINIT) & map->mask];
+
+ /* Allocate new entry */
+ if ((t = bhnd_nvram_tuple_alloc(name, value)) == NULL)
+ return (ENOMEM);
+
+ /* Remove any existing entry */
+ bhnd_nvram_varmap_remove(map, name);
+
+ /* Insert new entry */
+ LIST_INSERT_HEAD(head, t, t_link);
+ return (0);
+}
+
+/**
+ * Remove @p map in @p tuples, if it exists.
+ *
+ * @param map Hash table to modify.
+ * @param key Key to remove.
+ *
+ * @retval 0 success
+ * @retval ENOENT If @p name is not found in @p map.
+ */
+int
+bhnd_nvram_varmap_remove(struct bhnd_nvram_varmap *map, const char *name)
+{
+ struct bhnd_nvram_tuples *head;
+ struct bhnd_nvram_tuple *t;
+ size_t name_len;
+
+ /* Locate target bucket */
+ head = &map->table[hash32_str(name, HASHINIT) & map->mask];
+ name_len = strlen(name);
+
+ LIST_FOREACH(t, head, t_link) {
+ if (t->name_len != name_len)
+ continue;
+
+ if (strncmp(t->name, name, name_len) != 0)
+ continue;
+
+ LIST_REMOVE(t, t_link);
+ bhnd_nvram_tuple_free(t);
+ return (0);
+ }
+
+ /* Not found */
+ return (ENOENT);
+}
+
+/**
+ * Search for @p name in @p map.
+ *
+ * @param map Hash table to modify.
+ * @param name Variable name.
+ * @param name_len Length of @p name, not including trailing NUL.
+ *
+ * @retval bhnd_nvram_tuple If @p name is found in @p map.
+ * @retval NULL If @p name is not found.
+ */
+struct bhnd_nvram_tuple *
+bhnd_nvram_varmap_find(struct bhnd_nvram_varmap *map, const char *name,
+ size_t name_len)
+{
+ struct bhnd_nvram_tuples *head;
+ struct bhnd_nvram_tuple *t;
+
+ head = &map->table[hash32_str(name, HASHINIT) & map->mask];
+
+ LIST_FOREACH(t, head, t_link) {
+ if (t->name_len != name_len)
+ continue;
+
+ if (strncmp(t->name, name, name_len) != 0)
+ continue;
+
+ /* Match */
+ return (t);
+ }
+
+ /* not found */
+ return (NULL);
+}
+
+/**
+ * Check for @p name in @p map.
+ *
+ * @param map Hash table to modify.
+ * @param name Variable name.
+ * @param name_len Length of @p name, not including trailing NUL.
+ *
+ * @retval true If @p name is found in @p tuples.
+ * @retval false If @p name is not found.
+ */
+bool bhnd_nvram_varmap_contains(struct bhnd_nvram_varmap *map,
+ const char *name, size_t name_len)
+{
+ return (bhnd_nvram_varmap_find(map, name, name_len) != NULL);
+}
+
+/**
+ * Allocate a new tuple with @p name and @p value.
+ *
+ * @param name Variable name.
+ * @param value Variable value.
+ *
+ * @retval bhnd_nvram_tuple success.
+ * @retval NULL if allocation fails.
+ */
+struct bhnd_nvram_tuple *
+bhnd_nvram_tuple_alloc(const char *name, const char *value)
+{
+ struct bhnd_nvram_tuple *t;
+
+ t = malloc(sizeof(*t), M_BHND_NVRAM, M_NOWAIT);
+ if (t == NULL)
+ return (NULL);
+
+ t->name_len = strlen(name);
+ t->name = malloc(t->name_len+1, M_BHND_NVRAM, M_NOWAIT);
+
+ t->value_len = strlen(value);
+ t->value = malloc(t->value_len+1, M_BHND_NVRAM, M_NOWAIT);
+
+ if (t->name == NULL || t->value == NULL)
+ goto failed;
+
+ strcpy(t->name, name);
+ strcpy(t->value, value);
+
+ return (t);
+
+failed:
+ if (t->name != NULL)
+ free(t->name, M_BHND_NVRAM);
+
+ if (t->value != NULL)
+ free(t->value, M_BHND_NVRAM);
+
+ free(t, M_BHND_NVRAM);
+
+ return (NULL);
+}
+
+void
+bhnd_nvram_tuple_free(struct bhnd_nvram_tuple *tuple)
+{
+ free(tuple->name, M_BHND_NVRAM);
+ free(tuple->value, M_BHND_NVRAM);
+ free(tuple, M_BHND_NVRAM);
+}
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_if.m
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_if.m
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_if.m
@@ -49,6 +49,7 @@
* @param[in,out] len The maximum capacity of @p buf. On success,
* will be set to the actual size of the requested
* value.
+ * @param type The data type to be written to @p buf.
*
* @retval 0 success
* @retval ENOENT The requested variable was not found.
@@ -56,6 +57,9 @@
* small to hold the requested value.
* @retval ENODEV If no supported NVRAM hardware is accessible via this
* device.
+ * @retval EOPNOTSUPP If any coercion to @p type is unsupported.
+ * @retval EFTYPE If the @p name's data type cannot be coerced to @p type.
+ * @retval ERANGE If value coercion would overflow @p type.
* @retval non-zero If reading @p name otherwise fails, a regular unix
* error code will be returned.
*/
@@ -64,29 +68,35 @@
const char *name;
void *buf;
size_t *len;
+ bhnd_nvram_type type;
};
/**
- * Set an NVRAM variable's local value.
+ * Set an NVRAM variable's value.
*
- * No changes should be written to non-volatile storage.
+ * No changes will be written to non-volatile storage until explicitly
+ * committed.
*
* @param dev The NVRAM device.
* @param name The NVRAM variable name.
- * @param buf The new value.
- * @param len The size of @p buf.
+ * @param value The new value.
+ * @param len The size of @p value.
+ * @param type The data type of @p value.
*
* @retval 0 success
* @retval ENOENT The specified variable name is not recognized.
- * @retval EINVAL If @p len does not match the expected variable size.
* @retval ENODEV If no supported NVRAM hardware is accessible via this
* device.
+ * @retval EOPNOTSUPP If any coercion to @p type is unsupported.
+ * @retval EFTYPE If the @p name's data type cannot be coerced to @p type.
+ * @retval ERANGE If value coercion from @p type would overflow.
* @retval non-zero If reading @p name otherwise fails, a regular unix
* error code will be returned.
*/
METHOD int setvar {
device_t dev;
const char *name;
- const void *buf;
+ const void *value;
size_t len;
+ bhnd_nvram_type type;
};
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_parser.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_parser.h
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_parser.h
@@ -0,0 +1,101 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BHND_NVRAM_BHND_NVRAM_PARSER_H_
+#define _BHND_NVRAM_BHND_NVRAM_PARSER_H_
+
+#include <sys/param.h>
+#include <sys/bus.h>
+
+#include "bhnd_nvram_common.h"
+
+union bhnd_nvram_ident;
+
+struct bhnd_nvram_idx;
+struct bhnd_nvram_ops;
+struct bhnd_nvram_devpath;
+
+struct bhnd_nvram;
+
+LIST_HEAD(bhnd_nvram_devpaths, bhnd_nvram_devpath);
+
+int bhnd_nvram_parser_identify(const union bhnd_nvram_ident *ident,
+ bhnd_nvram_format expected);
+int bhnd_nvram_parser_init(struct bhnd_nvram *sc, device_t owner,
+ const void *data, size_t len, bhnd_nvram_format fmt);
+void bhnd_nvram_parser_fini(struct bhnd_nvram *sc);
+
+int bhnd_nvram_parser_getvar(struct bhnd_nvram *sc, const char *name,
+ void *buf, size_t *len, bhnd_nvram_type type);
+int bhnd_nvram_parser_setvar(struct bhnd_nvram *sc, const char *name,
+ const void *buf, size_t len, bhnd_nvram_type type);
+
+/** BCM NVRAM header */
+struct bhnd_nvram_header {
+ uint32_t magic;
+ uint32_t size;
+ uint32_t cfg0; /**< crc:8, version:8, sdram_init:16 */
+ uint32_t cfg1; /**< sdram_config:16, sdram_refresh:16 */
+ uint32_t sdram_ncdl; /**< sdram_ncdl */
+} __packed;
+
+/**
+ * NVRAM format identification.
+ *
+ * To perform identification of the NVRAM format using bhnd_nvram_identify(),
+ * read `sizeof(bhnd_nvram_indent)` bytes from the head of the NVRAM data.
+ */
+union bhnd_nvram_ident {
+ struct bhnd_nvram_header bcm;
+ char btxt[4];
+ struct bhnd_tlv_ident {
+ uint8_t tag;
+ uint8_t size[2];
+ uint8_t flags;
+ } __packed tlv;
+};
+
+/** bhnd nvram parser instance state */
+struct bhnd_nvram {
+ device_t dev; /**< parent device, or NULL */
+ const struct bhnd_nvram_ops *ops;
+ uint8_t *buf; /**< nvram data */
+ size_t buf_size;
+ size_t num_buf_vars; /**< number of records in @p buf (0 if not yet calculated) */
+
+ struct bhnd_nvram_idx *idx; /**< key index */
+
+ struct bhnd_nvram_devpaths devpaths; /**< device paths */
+ struct bhnd_nvram_varmap defaults; /**< default values */
+ struct bhnd_nvram_varmap pending; /**< uncommitted writes */
+};
+
+#endif /* _BHND_NVRAM_BHND_NVRAM_PARSER_H_ */
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_parser.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_parser.c
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_parser.c
@@ -0,0 +1,1578 @@
+/*-
+ * 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/bus.h>
+#include <sys/ctype.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include "bhnd_nvram_parserreg.h"
+#include "bhnd_nvram_parservar.h"
+
+/*
+ * BHND NVRAM Parser
+ *
+ * Provides identification, decoding, and encoding of BHND NVRAM data.
+ */
+
+static const struct bhnd_nvram_ops *bhnd_nvram_find_ops(bhnd_nvram_format fmt);
+
+static int bhnd_nvram_find_var(struct bhnd_nvram *sc, const char *name,
+ const char **value, size_t *value_len);
+
+static int bhnd_nvram_keycmp(const char *lhs, size_t lhs_len,
+ const char *rhs, size_t rhs_len);
+static int bhnd_nvram_sort_idx(void *ctx, const void *lhs,
+ const void *rhs);
+static int bhnd_nvram_generate_index(struct bhnd_nvram *sc);
+
+static int bhnd_nvram_index_lookup(struct bhnd_nvram *sc,
+ struct bhnd_nvram_idx *idx, const char *name,
+ const char **env, size_t *len, const char **value,
+ size_t *value_len);
+static int bhnd_nvram_buffer_lookup(struct bhnd_nvram *sc,
+ const char *name, const char **env, size_t *env_len,
+ const char **value, size_t *value_len);
+
+static bool bhnd_nvram_bufptr_valid(struct bhnd_nvram *sc, const void *ptr,
+ size_t nbytes, bool log_error);
+
+static int bhnd_nvram_parse_env(struct bhnd_nvram *sc, const char *env,
+ size_t len, const char **key, size_t *key_len,
+ const char **val, size_t *val_len);
+
+/**
+ * Calculate the size of the NVRAM data in @p data.
+ *
+ * @param data Pointer to NVRAM data to be parsed.
+ * @param[in,out] size On input, the total size of @p data. On
+ * successful parsing of @p data, will be set to
+ * the parsed size (which may be larger).
+ */
+typedef int (*bhnd_nvram_op_getsize)(const void *data, size_t *size);
+
+/** Perform format-specific initialization. */
+typedef int (*bhnd_nvram_op_init)(struct bhnd_nvram *sc);
+
+/** Initialize any format-specific default values. */
+typedef int (*bhnd_nvram_op_init_defaults)(struct bhnd_nvram *sc);
+typedef int (*bhnd_nvram_op_enum_buf)(struct bhnd_nvram *sc,
+ const char **env, size_t *len, const uint8_t *p,
+ uint8_t const **next);
+
+/* FMT_BCM ops */
+static int bhnd_nvram_bcm_getsize(const void *data, size_t *size);
+static int bhnd_nvram_bcm_init(struct bhnd_nvram *sc);
+static int bhnd_nvram_bcm_init_defaults(struct bhnd_nvram *sc);
+static int bhnd_nvram_bcm_enum_buf(struct bhnd_nvram *sc, const char **env,
+ size_t *len, const uint8_t *p, uint8_t const **next);
+
+/* FMT_TLV ops */
+static int bhnd_nvram_tlv_getsize(const void *data, size_t *size);
+static int bhnd_nvram_tlv_init(struct bhnd_nvram *sc);
+static int bhnd_nvram_tlv_enum_buf(struct bhnd_nvram *sc, const char **env,
+ size_t *len, const uint8_t *p, uint8_t const **next);
+/* FMT_TXT ops */
+static int bhnd_nvram_txt_getsize(const void *data, size_t *size);
+static int bhnd_nvram_txt_init(struct bhnd_nvram *sc);
+static int bhnd_nvram_txt_enum_buf(struct bhnd_nvram *sc, const char **env,
+ size_t *len, const uint8_t *p, uint8_t const **next);
+
+/**
+ * Format-specific operations.
+ */
+struct bhnd_nvram_ops {
+ bhnd_nvram_format fmt; /**< nvram format */
+ bhnd_nvram_op_getsize getsize; /**< determine actual NVRAM size */
+ bhnd_nvram_op_init init; /**< format-specific initialization */
+ bhnd_nvram_op_enum_buf enum_buf; /**< enumerate backing buffer */
+ bhnd_nvram_op_init_defaults init_defaults; /**< populate any default values */
+};
+
+static const struct bhnd_nvram_ops bhnd_nvram_ops_table[] = {
+ {
+ BHND_NVRAM_FMT_BCM,
+ bhnd_nvram_bcm_getsize,
+ bhnd_nvram_bcm_init,
+ bhnd_nvram_bcm_enum_buf,
+ bhnd_nvram_bcm_init_defaults
+ },
+ {
+ BHND_NVRAM_FMT_TLV,
+ bhnd_nvram_tlv_getsize,
+ bhnd_nvram_tlv_init,
+ bhnd_nvram_tlv_enum_buf,
+ NULL
+ },
+ {
+ BHND_NVRAM_FMT_BTXT,
+ bhnd_nvram_txt_getsize,
+ bhnd_nvram_txt_init,
+ bhnd_nvram_txt_enum_buf,
+ NULL
+ },
+};
+
+#define NVRAM_LOG(sc, fmt, ...) do { \
+ if (sc->dev != NULL) \
+ device_printf(sc->dev, fmt, ##__VA_ARGS__); \
+ else \
+ printf("bhnd_nvram: " fmt, ##__VA_ARGS__); \
+} while (0)
+
+/* Limit a size_t value to a suitable range for use as a printf string field
+ * width */
+#define NVRAM_PRINT_WIDTH(_len) \
+ ((_len) > NVRAM_VAL_MAX ? NVRAM_VAL_MAX : (int)(_len))
+
+/* Is _c a field terminating/delimiting character? */
+#define nvram_is_ftermc(_c) ((_c) == '\0' || nvram_is_fdelim(_c))
+
+/* Is _c a field delimiting character? */
+#define nvram_is_fdelim(_c) ((_c) == ',')
+
+/**
+ * Identify @p ident.
+ *
+ * @param ident Initial header data to be used for identification.
+ * @param expected Expected format against which @p ident will be tested.
+ *
+ * @retval 0 If @p ident has the @p expected format.
+ * @retval ENODEV If @p ident does not match @p expected.
+ */
+int
+bhnd_nvram_parser_identify(const union bhnd_nvram_ident *ident,
+ bhnd_nvram_format expected)
+{
+ uint32_t bcm_magic = le32toh(ident->bcm.magic);
+
+ switch (expected) {
+ case BHND_NVRAM_FMT_BCM:
+ if (bcm_magic == NVRAM_MAGIC)
+ return (0);
+
+ return (ENODEV);
+ case BHND_NVRAM_FMT_TLV:
+ if (bcm_magic == NVRAM_MAGIC)
+ return (ENODEV);
+
+ if (ident->tlv.tag != NVRAM_TLV_TYPE_ENV)
+ return (ENODEV);
+
+ return (0);
+ case BHND_NVRAM_FMT_BTXT:
+ for (size_t i = 0; i < nitems(ident->btxt); i++) {
+ char c = ident->btxt[i];
+ if (!isprint(c) && !isspace(c))
+ return (ENODEV);
+ }
+ return (0);
+ break;
+ default:
+ printf("%s: unknown format: %d\n", __FUNCTION__, expected);
+ return (ENODEV);
+ }
+}
+
+/** Return the set of operations for @p fmt, if any */
+static const struct bhnd_nvram_ops *
+bhnd_nvram_find_ops(bhnd_nvram_format fmt)
+{
+ const struct bhnd_nvram_ops *ops;
+
+ /* Fetch format-specific operation callbacks */
+ for (size_t i = 0; i < nitems(bhnd_nvram_ops_table); i++) {
+ ops = &bhnd_nvram_ops_table[i];
+
+ if (ops->fmt != fmt)
+ continue;
+
+ /* found */
+ return (ops);
+ }
+
+ return (NULL);
+}
+
+/**
+ * Identify the NVRAM format at @p offset within @p r, verify the
+ * CRC (if applicable), and allocate a local shadow copy of the NVRAM data.
+ *
+ * After initialization, no reference to @p input will be held by the
+ * NVRAM parser, and @p input may be safely deallocated.
+ *
+ * @param[out] sc The NVRAM parser state to be initialized.
+ * @param dev The parser's parent device, or NULL if none.
+ * @param data NVRAM data to be parsed.
+ * @param size Size of @p data.
+ * @param fmt Required format of @p input.
+ *
+ * @retval 0 success
+ * @retval ENOMEM If internal allocation of NVRAM state fails.
+ * @retval EINVAL If @p input parsing fails.
+ */
+int
+bhnd_nvram_parser_init(struct bhnd_nvram *sc, device_t dev, const void *data,
+ size_t size, bhnd_nvram_format fmt)
+{
+ int error;
+
+ /* Initialize NVRAM state */
+ memset(sc, 0, sizeof(*sc));
+
+ sc->dev = dev;
+ LIST_INIT(&sc->devpaths);
+
+ /* Verify data format and init operation callbacks */
+ if (size < sizeof(union bhnd_nvram_ident))
+ return (EINVAL);
+
+ error = bhnd_nvram_parser_identify(
+ (const union bhnd_nvram_ident *)data, fmt);
+ if (error)
+ return (error);
+
+ if ((sc->ops = bhnd_nvram_find_ops(fmt)) == NULL) {
+ NVRAM_LOG(sc, "unsupported format: %d\n", fmt);
+ return (error);
+ }
+
+ /* Determine appropriate size for backing buffer */
+ sc->buf_size = size;
+ if ((error = sc->ops->getsize(data, &sc->buf_size)))
+ return (error);
+
+ if (sc->buf_size > size) {
+ NVRAM_LOG(sc, "cannot parse %zu NVRAM bytes, would overrun "
+ "%zu byte input buffer\n", sc->buf_size, size);
+ return (EINVAL);
+ }
+
+ /* Allocate and populate backing buffer */
+ sc->buf = malloc(sc->buf_size, M_BHND_NVRAM, M_NOWAIT);
+ if (sc->buf == NULL)
+ return (ENOMEM);
+ memcpy(sc->buf, data, sc->buf_size);
+
+ /* Allocate default/pending variable hash tables */
+ error = bhnd_nvram_varmap_init(&sc->defaults, NVRAM_SMALL_HASH_SIZE,
+ M_NOWAIT);
+ if (error)
+ goto cleanup;
+
+ error = bhnd_nvram_varmap_init(&sc->pending, NVRAM_SMALL_HASH_SIZE,
+ M_NOWAIT);
+ if (error)
+ goto cleanup;
+
+ /* Perform format-specific initialization */
+ if ((error = sc->ops->init(sc)))
+ goto cleanup;
+
+ /* Generate all indices */
+ if ((error = bhnd_nvram_generate_index(sc)))
+ goto cleanup;
+
+ /* Add any format-specific default values */
+ if (sc->ops->init_defaults != NULL) {
+ if ((error = sc->ops->init_defaults(sc)))
+ goto cleanup;
+ }
+
+ return (0);
+
+cleanup:
+ bhnd_nvram_parser_fini(sc);
+ return (error);
+}
+
+
+/**
+ * Release all resources held by @p sc.
+ *
+ * @param sc A NVRAM instance previously initialized via
+ * bhnd_nvram_parser_init().
+ */
+void
+bhnd_nvram_parser_fini(struct bhnd_nvram *sc)
+{
+ struct bhnd_nvram_devpath *dpath, *dnext;
+
+ LIST_FOREACH_SAFE(dpath, &sc->devpaths, dp_link, dnext) {
+ free(dpath->path, M_BHND_NVRAM);
+ free(dpath, M_BHND_NVRAM);
+ }
+
+ if (sc->defaults.table != NULL)
+ bhnd_nvram_varmap_free(&sc->defaults);
+
+ if (sc->pending.table != NULL)
+ bhnd_nvram_varmap_free(&sc->pending);
+
+ if (sc->idx != NULL)
+ free(sc->idx, M_BHND_NVRAM);
+
+ if (sc->buf != NULL)
+ free(sc->buf, M_BHND_NVRAM);
+
+}
+
+/**
+ * Identify the integer format of @p field.
+ *
+ * @param field Field to be identified.
+ * @param field_len Length of @p field.
+ * @param[out] base Integer base, or 0 if integer format unrecognized.
+ * @param[out] negated True if integer is prefixed with negation sign.
+ *
+ * @retval true if parsed successfully
+ * @retval false if the format of @p field cannot be determined.
+ */
+static bool
+bhnd_nvram_identify_intfmt(const char *field, size_t field_len, int *base,
+ bool *negated)
+{
+ const char *p;
+
+ /* Hex? */
+ p = field;
+ if (field_len > 2 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
+ bool valid;
+
+ /* Check all field characters */
+ valid = true;
+ for (p = field + 2; p - field < field_len; p++) {
+ if (isxdigit(*p))
+ continue;
+
+ valid = false;
+ break;
+ }
+
+ if (valid) {
+ *base = 16;
+ *negated = false;
+ return (true);
+ }
+ }
+
+ /* Decimal? */
+ p = field;
+ if (field_len >= 1 && (*p == '-' || isdigit(*p))) {
+ bool valid;
+
+ valid = true;
+ *negated = false;
+ for (p = field; p - field < field_len; p++) {
+ if (p == field && *p == '-') {
+ *negated = true;
+ continue;
+ }
+
+ if (isdigit(*p))
+ continue;
+
+ valid = false;
+ break;
+ }
+
+ if (valid) {
+ *base = 10;
+ return (true);
+ }
+ }
+
+ /* No match */
+ *base = 0;
+ *negated = false;
+ return (false);
+}
+
+/**
+ * Search for a field delimiter or '\0' in @p value, returning the
+ * size of the first field (not including its terminating character).
+ *
+ * If no terminating character is found, @p value_len is returned.
+ *
+ * @param value The value to be searched.
+ * @param value_size The size of @p value.
+ */
+static size_t
+bhnd_nvram_parse_field_len(const char *value, size_t value_size)
+{
+ for (const char *p = value; p - value < value_size; p++) {
+ if (nvram_is_ftermc(*p))
+ return (p - value);
+ }
+
+ return (value_size);
+}
+
+/* Parse a string NVRAM variable, writing the NUL-terminated result
+ * to buf (if non-NULL). */
+static int
+bhnd_nvram_parse_strvar(const char *value, size_t value_len, char *buf,
+ size_t *size)
+{
+ size_t str_len;
+ size_t req_size;
+ size_t max_size;
+
+ if (buf != NULL)
+ max_size = *size;
+ else
+ max_size = 0;
+
+
+ /* Determine input and output sizes, including whether additional space
+ * is required for a trailing NUL */
+ str_len = strnlen(value, value_len);
+ if (str_len == value_len)
+ req_size = str_len + 1;
+ else
+ req_size = value_len;
+
+ /* Provide actual size to caller */
+ *size = req_size;
+ if (max_size < req_size) {
+ if (buf != NULL)
+ return (ENOMEM);
+ else
+ return (0);
+ }
+
+ /* Copy and NUL terminate output */
+ memcpy(buf, value, str_len);
+ buf[str_len] = '\0';
+ return (0);
+}
+
+/**
+ * Read an NVRAM variable.
+ *
+ * @param sc The NVRAM parser state.
+ * @param name The NVRAM variable name.
+ * @param[out] buf On success, the requested value 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 size of the requested value.
+ * @param type The requested data type to be written to @p buf.
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too
+ * small to hold the requested value.
+ * @retval non-zero If reading @p name otherwise fails, a regular unix
+ * error code will be returned.
+ */
+int
+bhnd_nvram_parser_getvar(struct bhnd_nvram *sc, const char *name, void *buf,
+ size_t *len, bhnd_nvram_type type)
+{
+ char *cstr, cstr_buf[NVRAM_VAL_MAX+1];
+ const char *val;
+ size_t cstr_size;
+ size_t limit, nbytes;
+ size_t field_len, val_len;
+ int error;
+
+ /* Verify name validity */
+ if (!bhnd_nvram_validate_name(name, strlen(name)))
+ return (EINVAL);
+
+ /* Fetch variable's string value */
+ if ((error = bhnd_nvram_find_var(sc, name, &val, &val_len)))
+ return (error);
+
+ nbytes = 0;
+ if (buf != NULL)
+ limit = *len;
+ else
+ limit = 0;
+
+ /* Populate C string requests directly from the fetched value */
+ if (type == BHND_NVRAM_TYPE_CSTR)
+ return (bhnd_nvram_parse_strvar(val, val_len, buf, len));
+
+ /* Determine actual string length. */
+ val_len = strnlen(val, val_len);
+
+ /* Try parsing as an octet string value (e.g. a MAC address) */
+ if (bhnd_nvram_parse_octet_string(val, val_len, buf, len, type) == 0)
+ return (0);
+
+ /* Otherwise, we need a NUL-terminated copy of the string value
+ * for parsing */
+ cstr_size = val_len + 1;
+ if (cstr_size <= sizeof(cstr)) {
+ /* prefer stack allocated buffer */
+ cstr = cstr_buf;
+ } else {
+ cstr = malloc(cstr_size, M_BHND_NVRAM, M_NOWAIT);
+ if (cstr == NULL)
+ return (EFBIG);
+ }
+
+ /* Copy and NUL terminate */
+ strncpy(cstr, val, val_len);
+ cstr[val_len] = '\0';
+
+ /* Parse */
+ for (char *p = cstr; *p != '\0';) {
+ char *endp;
+ int base;
+ bool is_int, is_negated;
+ union {
+ unsigned long u32;
+ long s32;
+ } intv;
+
+ /* Determine the field length */
+ field_len = val_len - (p - cstr);
+ field_len = bhnd_nvram_parse_field_len(p, field_len);
+
+ /* Skip any leading whitespace */
+ while (field_len > 0 && isspace(*p)) {
+ p++;
+ field_len--;
+ }
+
+ /* Empty field values cannot be parsed as a fixed
+ * data type */
+ if (field_len == 0) {
+ NVRAM_LOG(sc, "error: cannot parse empty string in "
+ "'%s'\n", cstr);
+ return (EFTYPE);
+ }
+
+ /* Attempt to identify the integer format */
+ is_int = bhnd_nvram_identify_intfmt(p, field_len, &base,
+ &is_negated);
+
+ /* Extract the field data */
+#define NV_READ_INT(_ctype, _max, _min, _dest, _strto) do { \
+ if (!is_int) { \
+ error = EFTYPE; \
+ goto finished; \
+ } \
+ \
+ if (is_negated && _min == 0) { \
+ error = ERANGE; \
+ goto finished; \
+ } \
+ \
+ _dest = _strto(p, &endp, base); \
+ if (endp == p || !nvram_is_ftermc(*endp)) { \
+ error = ERANGE; \
+ goto finished; \
+ } \
+ \
+ if (_dest > _max || _dest < _min) { \
+ error = ERANGE; \
+ goto finished; \
+ } \
+ \
+ if (limit > nbytes && limit - nbytes >= sizeof(_ctype)) \
+ *((_ctype *)((uint8_t *)buf + nbytes)) = _dest; \
+ \
+ nbytes += sizeof(_ctype); \
+} while(0)
+
+ switch (type) {
+ case BHND_NVRAM_TYPE_CHAR:
+ /* Copy out the characters directly */
+ for (size_t i = 0; i < field_len; i++) {
+ if (limit > nbytes)
+ *((char *)buf + nbytes) = p[i];
+ nbytes++;
+ }
+ break;
+
+ case BHND_NVRAM_TYPE_UINT8:
+ NV_READ_INT(uint8_t, UINT8_MAX, 0, intv.u32, strtoul);
+ break;
+
+ case BHND_NVRAM_TYPE_UINT16:
+ NV_READ_INT(uint16_t, UINT16_MAX, 0, intv.u32, strtoul);
+ break;
+
+ case BHND_NVRAM_TYPE_UINT32:
+ NV_READ_INT(uint32_t, UINT32_MAX, 0, intv.u32, strtoul);
+ break;
+
+ case BHND_NVRAM_TYPE_INT8:
+ NV_READ_INT(int8_t, INT8_MAX, INT8_MIN, intv.s32,
+ strtol);
+ break;
+
+ case BHND_NVRAM_TYPE_INT16:
+ NV_READ_INT(int16_t, INT16_MAX, INT16_MIN, intv.s32,
+ strtol);
+ break;
+
+ case BHND_NVRAM_TYPE_INT32:
+ NV_READ_INT(int32_t, INT32_MAX, INT32_MIN, intv.s32,
+ strtol);
+ break;
+
+ case BHND_NVRAM_TYPE_CSTR: /* Must be handled above */
+ /* fallthrough */
+ default:
+ NVRAM_LOG(sc, "unhandled NVRAM type: %d\n", type);
+ error = ENXIO;
+ goto finished;
+ }
+
+ /* Advance to next field, skip any trailing delimiter */
+ p += field_len;
+ if (nvram_is_fdelim(*p))
+ p++;
+ }
+
+ error = 0;
+
+finished:
+ if (cstr != cstr_buf)
+ free(cstr, M_BHND_NVRAM);
+
+ return (error);
+}
+
+/**
+ * Set an NVRAM variable.
+ *
+ * @param sc The NVRAM parser state.
+ * @param name The NVRAM variable name.
+ * @param[out] buf The new value.
+ * @param[in,out] len The size of @p buf.
+ * @param type The data type of @p buf.
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval EINVAL If @p len does not match the expected variable size.
+ */
+int
+bhnd_nvram_parser_setvar(struct bhnd_nvram *sc, const char *name,
+ const void *buf, size_t len, bhnd_nvram_type type)
+{
+ /* Verify name validity */
+ if (!bhnd_nvram_validate_name(name, strlen(name)))
+ return (EINVAL);
+
+ /* Verify buffer size alignment for the given type. If this is a
+ * variable width type, a width of 0 will always pass this check */
+ if (len % bhnd_nvram_type_width(type) != 0)
+ return (EINVAL);
+
+ /* Determine string format (or directly add variable, if a C string) */
+ switch (type) {
+ case BHND_NVRAM_TYPE_UINT8:
+ case BHND_NVRAM_TYPE_UINT16:
+ case BHND_NVRAM_TYPE_UINT32:
+ case BHND_NVRAM_TYPE_INT8:
+ case BHND_NVRAM_TYPE_INT16:
+ case BHND_NVRAM_TYPE_INT32:
+ // TODO: primitive type value support
+ return (EOPNOTSUPP);
+
+ case BHND_NVRAM_TYPE_CHAR:
+ case BHND_NVRAM_TYPE_CSTR:
+ return (bhnd_nvram_varmap_add(&sc->pending, name, buf, len));
+ }
+
+ return (0);
+}
+
+/**
+ * Return true if @p ptr + nbytes falls within our backing buffer, false
+ * otherwise.
+ */
+static bool
+bhnd_nvram_bufptr_valid(struct bhnd_nvram *sc, const void *ptr, size_t nbytes,
+ bool log_error)
+{
+ const uint8_t *p = ptr;
+
+ if (p < sc->buf)
+ goto failed;
+
+ if (nbytes > sc->buf_size)
+ goto failed;
+
+ if (p - sc->buf > sc->buf_size - nbytes)
+ goto failed;
+
+ return (true);
+
+failed:
+ if (log_error)
+ NVRAM_LOG(sc, "NVRAM record not readable at %p+%#zx (base=%p, "
+ "len=%zu)\n", p, nbytes, sc->buf, sc->buf_size);
+ return (false);
+}
+
+/**
+ * Parse a 'key=value' env string.
+ */
+static int
+bhnd_nvram_parse_env(struct bhnd_nvram *sc, const char *env, size_t len,
+ const char **key, size_t *key_len, const char **val, size_t *val_len)
+{
+ const char *p;
+
+ /* Key */
+ if ((p = memchr(env, '=', len)) == NULL) {
+ NVRAM_LOG(sc, "missing delim in '%.*s'\n",
+ NVRAM_PRINT_WIDTH(len), env);
+ return (EINVAL);
+ }
+
+ *key = env;
+ *key_len = p - env;
+
+ /* Skip '=' */
+ p++;
+
+ /* Vaue */
+ *val = p;
+ *val_len = len - (p - env);
+
+ return (0);
+}
+
+/**
+ * Fetch a string pointer to @p name's value, if any.
+ *
+ * @param sc The NVRAM parser state.
+ * @param name The NVRAM variable name.
+ * @param[out] value On success, a pointer to the variable's value
+ * string. The string may not be NUL terminated.
+ * @param[out] value_len On success, the length of @p value, not
+ * including a terminating NUL (if any exists).
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval non-zero If reading @p name otherwise fails, a regular unix
+ * error code will be returned.
+ */
+static int
+bhnd_nvram_find_var(struct bhnd_nvram *sc, const char *name, const char **value,
+ size_t *value_len)
+{
+ struct bhnd_nvram_tuple *t;
+ bhnd_nvram_op_enum_buf enum_fn;
+ const char *env;
+ size_t env_len;
+ size_t name_len;
+ int error;
+
+ enum_fn = sc->ops->enum_buf;
+ name_len = strlen(name);
+
+ /*
+ * Search path:
+ *
+ * - uncommitted changes
+ * - index lookup OR buffer scan
+ * - registered defaults
+ */
+
+ /* Search uncommitted changes */
+ t = bhnd_nvram_varmap_find(&sc->pending, name, name_len);
+ if (t != NULL) {
+ if (t->value != NULL) {
+ /* Uncommited value exists, is not a deletion */
+ *value = t->value;
+ *value_len = t->value_len;
+ return (0);
+ } else {
+ /* Value is marked for deletion. */
+ error = ENOENT;
+ goto failed;
+ }
+ }
+
+ /* Search backing buffer. We the index if available; otherwise,
+ * perform a buffer scan */
+ if (sc->idx != NULL) {
+ error = bhnd_nvram_index_lookup(sc, sc->idx, name, &env,
+ &env_len, value, value_len);
+ } else {
+ error = bhnd_nvram_buffer_lookup(sc, name, &env, &env_len,
+ value, value_len);
+ }
+
+failed:
+ /* If a parse error occured, we don't want to hide the issue by
+ * returning a default NVRAM value. Otherwise, look for a matching
+ * default. */
+ if (error != ENOENT)
+ return (error);
+
+ t = bhnd_nvram_varmap_find(&sc->defaults, name, name_len);
+ if (t != NULL) {
+ *value = t->value;
+ *value_len = t->value_len;
+ return (0);
+ }
+
+ /* Not found, and no default value available */
+ return (ENOENT);
+}
+
+/*
+ * An strcmp()-compatible lexical comparison implementation that
+ * handles non-NUL-terminated strings.
+ */
+static int
+bhnd_nvram_keycmp(const char *lhs, size_t lhs_len, const char *rhs,
+ size_t rhs_len)
+{
+ int order;
+
+ order = strncmp(lhs, rhs, ulmin(lhs_len, rhs_len));
+ if (order == 0) {
+ if (lhs_len < rhs_len)
+ order = -1;
+ else if (lhs_len > rhs_len)
+ order = 1;
+ }
+
+ return (order);
+}
+
+/* sort function for bhnd_nvram_idx_entry values */
+static int
+bhnd_nvram_sort_idx(void *ctx, const void *lhs, const void *rhs)
+{
+ struct bhnd_nvram *sc;
+ const struct bhnd_nvram_idx_entry *l_idx, *r_idx;
+ const char *l_str, *r_str;
+
+ sc = ctx;
+ l_idx = lhs;
+ r_idx = rhs;
+
+ /* Fetch string pointers */
+ l_str = (char *)(sc->buf + l_idx->env_offset);
+ r_str = (char *)(sc->buf + r_idx->env_offset);
+
+ /* Perform comparison */
+ return (bhnd_nvram_keycmp(l_str, l_idx->key_len, r_str,
+ r_idx->key_len));
+}
+
+
+/**
+ * Generate all indices for the NVRAM data backing @p nvram.
+ *
+ * @param sc The NVRAM parser state.
+ *
+ * @retval 0 success
+ * @retval non-zero If indexing @p nvram fails, a regular unix
+ * error code will be returned.
+ */
+static int
+bhnd_nvram_generate_index(struct bhnd_nvram *sc)
+{
+ bhnd_nvram_op_enum_buf enum_fn;
+ const char *key, *val;
+ const char *env;
+ const uint8_t *p;
+ size_t env_len;
+ size_t idx_bytes;
+ size_t key_len, val_len;
+ size_t num_records;
+ int error;
+
+ enum_fn = sc->ops->enum_buf;
+ num_records = 0;
+
+ /* Parse and register all device path aliases */
+ p = NULL;
+ while ((error = enum_fn(sc, &env, &env_len, p, &p)) == 0) {
+ struct bhnd_nvram_devpath *devpath;
+ char *eptr;
+ char suffix[NVRAM_KEY_MAX+1];
+ size_t suffix_len;
+ u_long index;
+
+ /* Hit EOF */
+ if (env == NULL)
+ break;
+
+ num_records++;
+
+ /* Skip string comparison if env_len < strlen(devpath) */
+ if (env_len < NVRAM_DEVPATH_LEN)
+ continue;
+
+ /* Check for devpath prefix */
+ if (strncmp(env, NVRAM_DEVPATH_STR, NVRAM_DEVPATH_LEN) != 0)
+ continue;
+
+ /* Split key and value */
+ error = bhnd_nvram_parse_env(sc, env, env_len, &key,
+ &key_len, &val, &val_len);
+ if (error)
+ return (error);
+
+ /* NUL terminate the devpath's suffix */
+ if (key_len >= sizeof(suffix)) {
+ NVRAM_LOG(sc, "variable '%.*s' exceeds NVRAM_KEY_MAX, "
+ "skipping devpath parsing\n",
+ NVRAM_PRINT_WIDTH(key_len), key);
+ continue;
+ } else {
+ suffix_len = key_len - NVRAM_DEVPATH_LEN;
+ if (suffix_len == 0)
+ continue;
+
+ strcpy(suffix, key+NVRAM_DEVPATH_LEN);
+ suffix[suffix_len] = '\0';
+ }
+
+ /* Parse the index value */
+ index = strtoul(suffix, &eptr, 10);
+ if (eptr == suffix || *eptr != '\0') {
+ NVRAM_LOG(sc, "invalid devpath variable '%.*s'\n",
+ NVRAM_PRINT_WIDTH(key_len), key);
+ continue;
+ }
+
+ /* Register path alias */
+ devpath = malloc(sizeof(*devpath), M_BHND_NVRAM, M_NOWAIT);
+ if (devpath == NULL)
+ return (ENOMEM);
+
+ devpath->index = index;
+ devpath->path = strndup(val, val_len, M_BHND_NVRAM);
+ LIST_INSERT_HEAD(&sc->devpaths, devpath, dp_link);
+ }
+
+ if (error)
+ return (error);
+
+ /* Save record count */
+ sc->num_buf_vars = num_records;
+
+ /* Skip generating variable index if threshold is not met */
+ if (sc->num_buf_vars < NVRAM_IDX_VAR_THRESH)
+ return (0);
+
+ /* Allocate and populate variable index */
+ idx_bytes = sizeof(struct bhnd_nvram_idx) +
+ (sizeof(struct bhnd_nvram_idx_entry) * sc->num_buf_vars);
+ sc->idx = malloc(idx_bytes, M_BHND_NVRAM, M_NOWAIT);
+ if (sc->idx == NULL) {
+ NVRAM_LOG(sc, "error allocating %zu byte index\n", idx_bytes);
+ goto bad_index;
+ }
+
+ sc->idx->num_entries = sc->num_buf_vars;
+
+ if (bootverbose) {
+ NVRAM_LOG(sc, "allocated %zu byte index for %zu variables "
+ "in %zu bytes\n", idx_bytes, sc->num_buf_vars,
+ sc->buf_size);
+ }
+
+ p = NULL;
+ for (size_t i = 0; i < sc->idx->num_entries; i++) {
+ struct bhnd_nvram_idx_entry *idx;
+ size_t env_offset;
+ size_t key_len, val_len;
+
+ /* Fetch next record */
+ if ((error = enum_fn(sc, &env, &env_len, p, &p)))
+ return (error);
+
+ /* Early EOF */
+ if (env == NULL) {
+ NVRAM_LOG(sc, "indexing failed, expected %zu records "
+ "(got %zu)\n", sc->idx->num_entries, i+1);
+ goto bad_index;
+ }
+
+ /* Calculate env offset */
+ env_offset = (const uint8_t *)env - (const uint8_t *)sc->buf;
+ if (env_offset > NVRAM_IDX_OFFSET_MAX) {
+ NVRAM_LOG(sc, "'%.*s' offset %#zx exceeds maximum "
+ "indexable value\n", NVRAM_PRINT_WIDTH(env_len),
+ env, env_offset);
+ goto bad_index;
+ }
+
+ /* Split key and value */
+ error = bhnd_nvram_parse_env(sc, env, env_len, &key, &key_len,
+ &val, &val_len);
+ if (error)
+ return (error);
+
+ if (key_len > NVRAM_IDX_LEN_MAX) {
+ NVRAM_LOG(sc, "key length %#zx at %#zx exceeds maximum "
+ "indexable value\n", key_len, env_offset);
+ goto bad_index;
+ }
+
+ if (val_len > NVRAM_IDX_LEN_MAX) {
+ NVRAM_LOG(sc, "value length %#zx for key '%.*s' "
+ "exceeds maximum indexable value\n", val_len,
+ NVRAM_PRINT_WIDTH(key_len), key);
+ goto bad_index;
+ }
+
+ idx = &sc->idx->entries[i];
+ idx->env_offset = env_offset;
+ idx->key_len = key_len;
+ idx->val_len = val_len;
+ }
+
+ /* Sort the index table */
+ qsort_r(sc->idx->entries, sc->idx->num_entries,
+ sizeof(sc->idx->entries[0]), sc, bhnd_nvram_sort_idx);
+
+ return (0);
+
+bad_index:
+ /* Fall back on non-indexed access */
+ NVRAM_LOG(sc, "reverting to non-indexed variable lookup\n");
+ if (sc->idx != NULL) {
+ free(sc->idx, M_BHND_NVRAM);
+ sc->idx = NULL;
+ }
+
+ return (0);
+}
+
+
+/**
+ * Perform an index lookup of @p name.
+ *
+ * @param sc The NVRAM parser state.
+ * @param idx The index to search.
+ * @param name The variable to search for.
+ * @param[out] env On success, the pointer to @p name within the
+ * backing buffer.
+ * @param[out] env_len On success, the length of @p env.
+ * @param[out] value On success, the pointer to @p name's value
+ * within the backing buffer.
+ * @param[out] value_len On success, the length of @p value.
+ *
+ * @retval 0 If @p name was found in the index.
+ * @retval ENOENT If @p name was not found in the index.
+ * @retval ENODEV If no index has been generated.
+ */
+static int
+bhnd_nvram_index_lookup(struct bhnd_nvram *sc, struct bhnd_nvram_idx *idx,
+ const char *name, const char **env, size_t *env_len, const char **value,
+ size_t *value_len)
+{
+ struct bhnd_nvram_idx_entry *idxe;
+ const char *idxe_key;
+ size_t min, mid, max;
+ size_t name_len;
+ int order;
+
+ if (idx->num_entries == 0)
+ return (ENOENT);
+
+ /*
+ * Locate the requested variable using a binary search.
+ */
+ min = 0;
+ mid = 0;
+ max = idx->num_entries - 1;
+ name_len = strlen(name);
+
+ while (max >= min) {
+ /* Select midpoint */
+ mid = (min + max) / 2;
+ idxe = &idx->entries[mid];
+
+ /* Determine which side of the partition to search */
+ idxe_key = (const char *) (sc->buf + idxe->env_offset);
+ order = bhnd_nvram_keycmp(idxe_key, idxe->key_len, name,
+ name_len);
+
+ if (order < 0) {
+ /* Search upper partition */
+ min = mid + 1;
+ } else if (order > 0) {
+ /* Search lower partition */
+ max = mid - 1;
+ } else if (order == 0) {
+ /* Match found */
+ *env = sc->buf + idxe->env_offset;
+ *env_len = idxe->key_len + idxe->val_len + 1 /* '=' */;
+
+ *value = *env + idxe->key_len + 1 /* '=' */;
+ *value_len = idxe->val_len;
+
+ return (0);
+ }
+ }
+
+ /* Not found */
+ return (ENOENT);
+}
+
+
+/**
+ * Perform a unindexed search for an entry matching @p name in the backing
+ * NVRAM data buffer.
+ *
+ * @param sc The NVRAM parser state.
+ * @param name The variable to search for.
+ * @param[out] env On success, the pointer to @p name within the
+ * backing buffer.
+ * @param[out] env_len On success, the length of @p env.
+ * @param[out] value On success, the pointer to @p name's value
+ * within the backing buffer.
+ * @param[out] value_len On success, the length of @p value.
+ *
+ * @retval 0 If @p name was found in the index.
+ * @retval ENOENT If @p name was not found in the index.
+ * @retval ENODEV If no index has been generated.
+ */
+static int
+bhnd_nvram_buffer_lookup(struct bhnd_nvram *sc, const char *name,
+ const char **env, size_t *env_len, const char **value, size_t *value_len)
+{
+ bhnd_nvram_op_enum_buf enum_fn;
+ const uint8_t *p;
+ size_t name_len;
+ int error;
+
+ enum_fn = sc->ops->enum_buf;
+ name_len = strlen(name);
+
+ /* Iterate over all records in the backing buffer */
+ p = NULL;
+ while ((error = enum_fn(sc, env, env_len, p, &p)) == 0) {
+ /* Hit EOF, not found */
+ if (*env == NULL)
+ return (ENOENT);
+
+ /* Skip string comparison if env_len < strlen('key=') */
+ if (*env_len < name_len + 1)
+ continue;
+
+ /* Skip string comparison if delimiter isn't found at
+ * expected position */
+ if (*(*env + name_len) != '=')
+ continue;
+
+ /* Check for match */
+ if (strncmp(*env, name, name_len) == 0) {
+ /* Found */
+ *value = *env + name_len + 1;
+ *value_len = *env_len - name_len - 1;
+ return (0);
+ };
+ }
+
+ return (error);
+}
+
+/* FMT_BCM NVRAM data size calculation */
+static int
+bhnd_nvram_bcm_getsize(const void *data, size_t *size)
+{
+ const struct bhnd_nvram_header *hdr;
+
+ if (*size < sizeof(*hdr))
+ return (EINVAL);
+
+ hdr = (const struct bhnd_nvram_header *) data;
+ *size = le32toh(hdr->size);
+ return (0);
+}
+
+/* FMT_BCM-specific parser initialization */
+static int
+bhnd_nvram_bcm_init(struct bhnd_nvram *sc)
+{
+ const uint8_t *p;
+ uint32_t cfg0;
+ uint8_t crc, valid;
+
+ /* Validate CRC */
+ if (sc->buf_size < NVRAM_CRC_SKIP)
+ return (EINVAL);
+
+ if (sc->buf_size < sizeof(struct bhnd_nvram_header))
+ return (EINVAL);
+
+ cfg0 = ((struct bhnd_nvram_header *)sc->buf)->cfg0;
+ valid = (cfg0 & NVRAM_CFG0_CRC_MASK) >> NVRAM_CFG0_CRC_SHIFT;
+
+ p = sc->buf;
+ crc = bhnd_nvram_crc8(p + NVRAM_CRC_SKIP, sc->buf_size-NVRAM_CRC_SKIP,
+ BHND_NVRAM_CRC8_INITIAL);
+
+ if (crc != valid) {
+ NVRAM_LOG(sc, "warning: NVRAM CRC error (crc=%#hhx, "
+ "expected=%hhx)\n", crc, valid);
+ }
+
+ return (0);
+}
+
+/* Populate FMT_BCM-specific default values */
+static int
+bhnd_nvram_bcm_init_defaults(struct bhnd_nvram *sc)
+{
+ struct bhnd_nvram_header *header;
+ char vbuf[NVRAM_VAL_MAX];
+ uint32_t value;
+ int error;
+ int nwrite;
+
+ /* Verify that our header is readable */
+ header = (struct bhnd_nvram_header *) sc->buf;
+ if (!bhnd_nvram_bufptr_valid(sc, header, sizeof(*header), true))
+ return (EINVAL);
+
+ /* Extract a value value from the NVRAM header, format it, and
+ * register a new default variable tuple */
+#define NVRAM_BCM_HEADER_DEFAULT(_field, _name) do { \
+ value = NVRAM_GET_BITS(le32toh(header->_field), _name); \
+ nwrite = snprintf(vbuf, sizeof(vbuf), _name ##_FMT, value); \
+ if (nwrite < 0 || nwrite >= sizeof(vbuf)) { \
+ NVRAM_LOG(sc, "%s: formatting '%s' failed: %d\n", \
+ __FUNCTION__, _name ## _VAR, nwrite); \
+ return (ENXIO); \
+ } \
+ error = bhnd_nvram_varmap_add(&sc->defaults, \
+ _name ##_VAR, vbuf, strlen(vbuf)); \
+ \
+ if (error) \
+ return (error); \
+} while(0)
+
+ NVRAM_BCM_HEADER_DEFAULT(cfg0, NVRAM_CFG0_SDRAM_INIT);
+ NVRAM_BCM_HEADER_DEFAULT(cfg1, NVRAM_CFG1_SDRAM_CFG);
+ NVRAM_BCM_HEADER_DEFAULT(cfg1, NVRAM_CFG1_SDRAM_REFRESH);
+ NVRAM_BCM_HEADER_DEFAULT(sdram_ncdl, NVRAM_SDRAM_NCDL);
+
+#undef NVRAM_BCM_HEADER_DEFAULT
+
+ return (0);
+}
+
+
+/* FMT_BCM record parsing */
+static int
+bhnd_nvram_bcm_enum_buf(struct bhnd_nvram *sc, const char **env, size_t *len,
+ const uint8_t *p, uint8_t const **next)
+{
+ /* First record is found following the NVRAM header */
+ if (p == NULL)
+ p = sc->buf + sizeof(struct bhnd_nvram_header);
+
+ if (!bhnd_nvram_bufptr_valid(sc, p, 1, true))
+ return (EINVAL);
+
+ /* EOF */
+ if (*p == '\0') {
+ *env = NULL;
+ *len = 0;
+ *next = p;
+ return (0);
+ }
+
+ /* Provide pointer to env data */
+ *env = p;
+ *len = strnlen(p, sc->buf_size - (p - sc->buf));
+
+ /* Advance to next entry and skip terminating NUL */
+ p += *len;
+ if (bhnd_nvram_bufptr_valid(sc, p, 1, false)) {
+ p++;
+ } else {
+ NVRAM_LOG(sc, "warning: missing NVRAM termination record");
+ }
+
+ *next = p;
+ return (0);
+}
+
+/* FMT_TLV NVRAM data size calculation */
+static int
+bhnd_nvram_tlv_getsize(const void *data, size_t *size)
+{
+ const uint8_t *const start = data;
+ size_t offset;
+ uint16_t rlen;
+
+ offset = 0;
+ while (offset < *size) {
+ uint8_t type;
+
+ /* Fetch type */
+ type = *(start+offset);
+
+ /* EOF */
+ if (type == NVRAM_TLV_TYPE_END) {
+ *size = offset + 1;
+ return (0);
+ }
+
+ if ((offset++) == *size)
+ return (EINVAL);
+
+ /* Determine record length */
+ if (type & NVRAM_TLV_TF_U8_LEN) {
+ rlen = *(start+offset);
+ } else {
+ rlen = *(start+offset) << 8;
+ if ((offset++) == *size)
+ return (EINVAL);
+ rlen |= *(start+offset);
+ }
+
+ if ((offset++) >= *size)
+ return (EINVAL);
+
+ /* Advance to next entry */
+ if (rlen > *size || *size - rlen < offset)
+ return (EINVAL);
+
+ offset += rlen;
+ }
+
+ /* EOF not found */
+ return (EINVAL);
+}
+
+/* FMT_TLV-specific parser initialization */
+static int
+bhnd_nvram_tlv_init(struct bhnd_nvram *sc)
+{
+ return (0);
+}
+
+/* FMT_TLV record parsing */
+static int
+bhnd_nvram_tlv_enum_buf(struct bhnd_nvram *sc, const char **env, size_t *len,
+ const uint8_t *p, uint8_t const **next)
+{
+ size_t rlen;
+ uint8_t type;
+
+ if (p == NULL)
+ p = sc->buf;
+
+ /* Fetch type */
+ if (!bhnd_nvram_bufptr_valid(sc, p, 1, true))
+ return (EINVAL);
+
+ type = *p;
+
+ /* EOF */
+ if (type == NVRAM_TLV_TYPE_END) {
+ *env = NULL;
+ *len = 0;
+ *next = p;
+ return (0);
+ }
+
+ /* Determine record length */
+ p++;
+ if (type & NVRAM_TLV_TF_U8_LEN) {
+ if (!bhnd_nvram_bufptr_valid(sc, p, 1, true))
+ return (EINVAL);
+
+ rlen = *p;
+ p += 1;
+ } else {
+ if (!bhnd_nvram_bufptr_valid(sc, p, 2, true))
+ return (EINVAL);
+ rlen = (p[0] << 8) | (p[1]);
+ p += 2;
+ }
+
+ /* Verify record readability */
+ if (!bhnd_nvram_bufptr_valid(sc, p, rlen, true))
+ return (EINVAL);
+
+ /* Error on non-env records */
+ if (type != NVRAM_TLV_TYPE_ENV) {
+ NVRAM_LOG(sc, "unsupported NVRAM TLV tag: %#hhx\n", type);
+ return (EINVAL);
+ }
+
+ /* Skip flag field */
+ if (rlen < 1)
+ return (EINVAL);
+ p++;
+ rlen--;
+
+ /* Provide pointer to env data */
+ *env = p;
+ *len = strnlen(*env, rlen);
+
+ /* Advance to next entry */
+ *next = p + rlen;
+
+ return (0);
+}
+
+/* FMT_BTXT NVRAM data size calculation */
+static int
+bhnd_nvram_txt_getsize(const void *data, size_t *size)
+{
+ *size = (strnlen(data, *size));
+ return (0);
+}
+
+/* FMT_BTXT-specific parser initialization */
+static int
+bhnd_nvram_txt_init(struct bhnd_nvram *sc)
+{
+ return (0);
+}
+
+/* Seek past the next line ending (\r, \r\n, or \n) */
+static const uint8_t *
+bhnd_nvram_txt_seek_eol(struct bhnd_nvram *sc, const uint8_t *p)
+{
+ while (p < sc->buf + sc->buf_size) {
+ switch (*p) {
+ case '\r':
+ /* \r\n */
+ if (bhnd_nvram_bufptr_valid(sc, p, 1, false)) {
+ if (*(p+1) == '\n')
+ p++;
+ }
+
+ return (p+1);
+ case '\n':
+ return (p+1);
+ default:
+ p++;
+ break;
+ }
+ }
+
+ return (p);
+}
+
+/* Seek to the next valid key=value entry (or EOF) */
+static const uint8_t *
+bhnd_nvram_txt_seek_nextline(struct bhnd_nvram *sc, const uint8_t *p)
+{
+ /* Skip leading whitespace and comments */
+ while (p < sc->buf + sc->buf_size) {
+ if (isspace(*p)) {
+ p++;
+ continue;
+ }
+
+ if (*p == '#') {
+ p = bhnd_nvram_txt_seek_eol(sc, p);
+ continue;
+ }
+
+ break;
+ }
+
+ return (p);
+}
+
+/* FMT_BTXT record parsing */
+static int
+bhnd_nvram_txt_enum_buf(struct bhnd_nvram *sc, const char **env, size_t *len,
+ const uint8_t *p, uint8_t const **next)
+{
+ const uint8_t *startp;
+ size_t line_len;
+
+ if (p == NULL)
+ p = sc->buf;
+
+ /* Skip any leading whitespace and comments */
+ p = bhnd_nvram_txt_seek_nextline(sc, p);
+
+ /* EOF? */
+ if (!bhnd_nvram_bufptr_valid(sc, p, 1, false)) {
+ *env = NULL;
+ *len = 0;
+ *next = p;
+ return (0);
+ }
+
+ /* Find record termination (EOL, or '#') */
+ startp = p;
+ while (p < sc->buf + sc->buf_size) {
+ if (*p == '#' || *p == '\n' || *p == '\r')
+ break;
+
+ p++;
+ }
+
+ /* Calculate line length, check for EOF */
+ line_len = p - startp;
+ if (!bhnd_nvram_bufptr_valid(sc, p, 1, false)) {
+ *env = NULL;
+ *len = 0;
+ *next = p;
+ return (0);
+ }
+
+ /* Got env data; trim any tailing whitespace */
+ *env = startp;
+ *len = line_len;
+
+ for (size_t i = 0; i < line_len && line_len > 0; i++) {
+ char c = startp[line_len - i - 1];
+ if (!isspace(c))
+ break;
+
+ *len -= 1;
+ }
+
+ /* Advance to next entry */
+ p = bhnd_nvram_txt_seek_nextline(sc, p);
+
+ *next = p;
+ return (0);
+}
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_parserreg.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_parserreg.h
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_parserreg.h
@@ -0,0 +1,75 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BHND_NVRAM_BHND_NVRAM_PARSERREG_H_
+#define _BHND_NVRAM_BHND_NVRAM_PARSERREG_H_
+
+
+
+#define NVRAM_GET_BITS(_value, _field) \
+ ((_value & _field ## _MASK) >> _field ## _SHIFT)
+
+/* NVRAM header fields */
+#define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */
+#define NVRAM_VERSION 1
+
+#define NVRAM_CRC_SKIP 9 /* skip magic, size, and crc8 */
+
+#define NVRAM_CFG0_CRC_MASK 0x000000FF
+#define NVRAM_CFG0_CRC_SHIFT 0
+#define NVRAM_CFG0_VER_MASK 0x0000FF00
+#define NVRAM_CFG0_VER_SHIFT 8
+#define NVRAM_CFG0_SDRAM_INIT_MASK 0xFFFF0000
+#define NVRAM_CFG0_SDRAM_INIT_SHIFT 16
+#define NVRAM_CFG0_SDRAM_INIT_VAR "sdram_init"
+#define NVRAM_CFG0_SDRAM_INIT_FMT "0x%04x"
+
+#define NVRAM_CFG1_SDRAM_CFG_MASK 0x0000FFFF
+#define NVRAM_CFG1_SDRAM_CFG_SHIFT 0
+#define NVRAM_CFG1_SDRAM_CFG_VAR "sdram_config"
+#define NVRAM_CFG1_SDRAM_CFG_FMT "0x%04x"
+
+#define NVRAM_CFG1_SDRAM_REFRESH_MASK 0xFFFF0000
+#define NVRAM_CFG1_SDRAM_REFRESH_SHIFT 16
+#define NVRAM_CFG1_SDRAM_REFRESH_VAR "sdram_refresh"
+#define NVRAM_CFG1_SDRAM_REFRESH_FMT "0x%04x"
+
+#define NVRAM_SDRAM_NCDL_MASK UINT32_MAX
+#define NVRAM_SDRAM_NCDL_SHIFT 0
+#define NVRAM_SDRAM_NCDL_VAR "sdram_ncdl"
+#define NVRAM_SDRAM_NCDL_FMT "0x%08x"
+
+/* WGT634U-specific TLV encoding */
+#define NVRAM_TLV_TF_U8_LEN 0x01 /**< type has 8-bit length */
+#define NVRAM_TLV_TYPE_END 0x00 /**< end of table */
+#define NVRAM_TLV_TYPE_ENV 0x01 /**< variable record */
+
+#endif /* _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_ */
Index: head/sys/dev/bhnd/nvram/bhnd_nvram_parservar.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_parservar.h
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_parservar.h
@@ -0,0 +1,86 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_
+#define _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_
+
+#include <sys/types.h>
+
+#include "bhnd_nvram_common.h"
+
+#include "bhnd_nvram_parser.h"
+
+#define NVRAM_IDX_VAR_THRESH 15 /**< index is generated if minimum variable count is met */
+#define NVRAM_IDX_OFFSET_MAX UINT16_MAX /**< maximum indexable offset */
+#define NVRAM_IDX_LEN_MAX UINT8_MAX /**< maximum indexable key/value length */
+
+#define NVRAM_KEY_MAX 64 /**< maximum key length (not incl. NUL) */
+#define NVRAM_VAL_MAX 255 /**< maximum value length (not incl. NUL) */
+
+#define NVRAM_DEVPATH_STR "devpath" /**< name prefix of device path aliases */
+#define NVRAM_DEVPATH_LEN (sizeof(NVRAM_DEVPATH_STR) - 1)
+
+#define NVRAM_SMALL_HASH_SIZE 16 /**< hash table size for pending/default tuples */
+
+/**
+ * NVRAM devpath record.
+ *
+ * Aliases index values to full device paths.
+ */
+struct bhnd_nvram_devpath {
+ u_long index; /** alias index */
+ char *path; /** aliased path */
+
+ LIST_ENTRY(bhnd_nvram_devpath) dp_link;
+};
+
+/**
+ * NVRAM index record.
+ *
+ * Provides entry offsets into a backing NVRAM buffer.
+ */
+struct bhnd_nvram_idx_entry {
+ uint16_t env_offset; /**< offset to env string */
+ uint8_t key_len; /**< key length */
+ uint8_t val_len; /**< value length */
+};
+
+/**
+ * NVRAM index.
+ *
+ * Provides a compact binary search index into the backing NVRAM buffer.
+ */
+struct bhnd_nvram_idx {
+ size_t num_entries; /**< entry count */
+ struct bhnd_nvram_idx_entry entries[]; /**< index entries */
+};
+
+#endif /* _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_ */
Index: head/sys/dev/bhnd/nvram/bhnd_nvramvar.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvramvar.h
+++ head/sys/dev/bhnd/nvram/bhnd_nvramvar.h
@@ -0,0 +1,68 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BHND_NVRAM_BHND_NVRAMVAR_H_
+#define _BHND_NVRAM_BHND_NVRAMVAR_H_
+
+#include <sys/param.h>
+#include <sys/bus.h>
+
+#include "bhnd_nvram_parser.h"
+
+DECLARE_CLASS(bhnd_nvram_driver);
+
+int bhnd_nvram_probe(device_t dev);
+int bhnd_nvram_attach(device_t dev, void *data, size_t size,
+ bhnd_nvram_format fmt);
+int bhnd_nvram_resume(device_t dev);
+int bhnd_nvram_suspend(device_t dev);
+int bhnd_nvram_detach(device_t dev);
+
+/**
+ * bhnd_nvram driver instance state. Must be first member of all subclass
+ * softc structures.
+ */
+struct bhnd_nvram_softc {
+ device_t dev;
+ struct mtx mtx; /**< nvram mutex */
+ struct bhnd_nvram nvram; /**< nvram shadow */
+};
+
+
+#define BHND_NVRAM_LOCK_INIT(sc) \
+ mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
+ "bhnd_nvram lock", MTX_DEF)
+#define BHND_NVRAM_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define BHND_NVRAM_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+#define BHND_NVRAM_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what)
+#define BHND_NVRAM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
+
+#endif /* _BHND_NVRAM_BHND_NVRAMVAR_H_ */
Index: head/sys/dev/bhnd/nvram/bhnd_sprom.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_sprom.c
+++ head/sys/dev/bhnd/nvram/bhnd_sprom.c
@@ -169,7 +169,8 @@
* Default bhnd sprom driver implementation of BHND_NVRAM_GETVAR().
*/
static int
-bhnd_sprom_getvar_meth(device_t dev, const char *name, void *buf, size_t *len)
+bhnd_sprom_getvar_method(device_t dev, const char *name, void *buf, size_t *len,
+ bhnd_nvram_type type)
{
struct bhnd_sprom_softc *sc;
int error;
@@ -177,7 +178,7 @@
sc = device_get_softc(dev);
SPROM_LOCK(sc);
- error = bhnd_sprom_getvar(&sc->shadow, name, buf, len);
+ error = bhnd_sprom_getvar(&sc->shadow, name, buf, len, type);
SPROM_UNLOCK(sc);
return (error);
@@ -187,8 +188,8 @@
* Default bhnd sprom driver implementation of BHND_NVRAM_SETVAR().
*/
static int
-bhnd_sprom_setvar_meth(device_t dev, const char *name, const void *buf,
- size_t len)
+bhnd_sprom_setvar_method(device_t dev, const char *name, const void *buf,
+ size_t len, bhnd_nvram_type type)
{
struct bhnd_sprom_softc *sc;
int error;
@@ -196,7 +197,7 @@
sc = device_get_softc(dev);
SPROM_LOCK(sc);
- error = bhnd_sprom_setvar(&sc->shadow, name, buf, len);
+ error = bhnd_sprom_setvar(&sc->shadow, name, buf, len, type);
SPROM_UNLOCK(sc);
return (error);
@@ -211,8 +212,8 @@
DEVMETHOD(device_detach, bhnd_sprom_detach),
/* NVRAM interface */
- DEVMETHOD(bhnd_nvram_getvar, bhnd_sprom_getvar_meth),
- DEVMETHOD(bhnd_nvram_setvar, bhnd_sprom_setvar_meth),
+ DEVMETHOD(bhnd_nvram_getvar, bhnd_sprom_getvar_method),
+ DEVMETHOD(bhnd_nvram_setvar, bhnd_sprom_setvar_method),
DEVMETHOD_END
};
Index: head/sys/dev/bhnd/nvram/bhnd_sprom_parser.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_sprom_parser.h
+++ head/sys/dev/bhnd/nvram/bhnd_sprom_parser.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BHND_NVRAM_BHND_SPROM_PARSER_H_
+#define _BHND_NVRAM_BHND_SPROM_PARSER_H_
+
+#include <dev/bhnd/bhnd.h>
+
+struct bhnd_sprom;
+
+int bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r,
+ bus_size_t offset);
+void bhnd_sprom_fini(struct bhnd_sprom *sprom);
+int bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf,
+ size_t *len, bhnd_nvram_type type);
+int bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name,
+ const void *buf, size_t len, bhnd_nvram_type type);
+
+/**
+ * bhnd sprom parser instance state.
+ */
+struct bhnd_sprom {
+ device_t dev; /**< sprom parent device */
+
+ uint8_t sp_rev; /**< sprom revision */
+
+ struct bhnd_resource *sp_res; /**< sprom resource. */
+ bus_size_t sp_res_off; /**< offset to sprom image */
+
+ uint8_t *sp_shadow; /**< sprom shadow */
+ bus_size_t sp_size_max; /**< maximum possible sprom length */
+ size_t sp_size; /**< shadow size */
+ size_t sp_capacity; /**< shadow buffer capacity */
+};
+
+#endif /* _BHND_NVRAM_BHND_SPROM_PARSER_H_ */
Index: head/sys/dev/bhnd/nvram/bhnd_sprom_parser.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_sprom_parser.c
+++ head/sys/dev/bhnd/nvram/bhnd_sprom_parser.c
@@ -0,0 +1,756 @@
+/*-
+ * 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/bus.h>
+#include <sys/endian.h>
+#include <sys/limits.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include "bhnd_nvram_common.h"
+
+#include "bhnd_sprom_parservar.h"
+
+/*
+ * BHND SPROM Parser
+ *
+ * Provides identification, decoding, and encoding of BHND SPROM data.
+ */
+
+static int sprom_direct_read(struct bhnd_sprom *sc, size_t offset,
+ void *buf, size_t nbytes, uint8_t *crc);
+static int sprom_extend_shadow(struct bhnd_sprom *sc,
+ size_t image_size, uint8_t *crc);
+static int sprom_populate_shadow(struct bhnd_sprom *sc);
+
+static int sprom_get_var_defn(struct bhnd_sprom *sc,
+ const char *name,
+ const struct bhnd_nvram_vardefn **var,
+ const struct bhnd_sprom_vardefn **sprom,
+ size_t *size, size_t *nelem,
+ bhnd_nvram_type req_type);
+
+static char sprom_get_delim_char(struct bhnd_sprom *sc,
+ bhnd_nvram_sfmt sfmt);
+
+/* SPROM revision is always located at the second-to-last byte */
+#define SPROM_REV(_sc) SPROM_READ_1((_sc), (_sc)->sp_size - 2)
+
+/* SPROM CRC is always located at the last byte */
+#define SPROM_CRC_OFF(_sc) SPROM_CRC_LEN(_sc)
+
+/* SPROM CRC covers all but the final CRC byte */
+#define SPROM_CRC_LEN(_sc) ((_sc)->sp_size - 1)
+
+/* SPROM shadow I/O (with byte-order translation) */
+#define SPROM_READ_1(_sc, _off) SPROM_READ_ENC_1(_sc, _off)
+#define SPROM_READ_2(_sc, _off) le16toh(SPROM_READ_ENC_2(_sc, _off))
+#define SPROM_READ_4(_sc, _off) le32toh(SPROM_READ_ENC_4(_sc, _off))
+
+#define SPROM_WRITE_1(_sc, _off, _v) SPROM_WRITE_ENC_1(_sc, _off, (_v))
+#define SPROM_WRITE_2(_sc, _off, _v) SPROM_WRITE_ENC_2(_sc, _off, \
+ htole16(_v))
+#define SPROM_WRITE_4(_sc, _off, _v) SPROM_WRITE_ENC_4(_sc, _off, \
+ htole32(_v))
+
+/* SPROM shadow I/O (without byte-order translation) */
+#define SPROM_READ_ENC_1(_sc, _off) (*(uint8_t *)((_sc)->sp_shadow + _off))
+#define SPROM_READ_ENC_2(_sc, _off) (*(uint16_t *)((_sc)->sp_shadow + _off))
+#define SPROM_READ_ENC_4(_sc, _off) (*(uint32_t *)((_sc)->sp_shadow + _off))
+
+#define SPROM_WRITE_ENC_1(_sc, _off, _v) \
+ *((uint8_t *)((_sc)->sp_shadow + _off)) = (_v)
+#define SPROM_WRITE_ENC_2(_sc, _off, _v) \
+ *((uint16_t *)((_sc)->sp_shadow + _off)) = (_v)
+#define SPROM_WRITE_ENC_4(_sc, _off, _v) \
+ *((uint32_t *)((_sc)->sp_shadow + _off)) = (_v)
+
+/* Call @p _next macro with the C type, widened (signed or unsigned) 32-bit C
+ * type, width, and min/max values associated with @p _dtype */
+#define SPROM_SWITCH_TYPE(_dtype, _next, ...) \
+do { \
+ switch (_dtype) { \
+ case BHND_NVRAM_TYPE_UINT8: \
+ _next (uint8_t, uint32_t, 1, 0, \
+ UINT8_MAX, ## __VA_ARGS__); \
+ break; \
+ case BHND_NVRAM_TYPE_UINT16: \
+ _next (uint16_t, uint32_t, 2, 0, \
+ UINT16_MAX, ## __VA_ARGS__); \
+ break; \
+ case BHND_NVRAM_TYPE_UINT32: \
+ _next (uint32_t, uint32_t, 4, 0, \
+ UINT32_MAX, ## __VA_ARGS__); \
+ break; \
+ case BHND_NVRAM_TYPE_INT8: \
+ _next (int8_t, int32_t, 1, \
+ INT8_MIN, INT8_MAX, ## __VA_ARGS__); \
+ break; \
+ case BHND_NVRAM_TYPE_INT16: \
+ _next (int16_t, int32_t, 2, \
+ INT16_MIN, INT16_MAX, ## __VA_ARGS__); \
+ break; \
+ case BHND_NVRAM_TYPE_INT32: \
+ _next (int32_t, int32_t, 4, \
+ INT32_MIN, INT32_MAX, ## __VA_ARGS__); \
+ break; \
+ case BHND_NVRAM_TYPE_CHAR: \
+ _next (char, int32_t, 1, \
+ CHAR_MIN, CHAR_MAX, ## __VA_ARGS__); \
+ break; \
+ case BHND_NVRAM_TYPE_CSTR: \
+ panic("%s: BHND_NVRAM_TYPE_CSTR unhandled", \
+ __FUNCTION__); \
+ break; \
+ } \
+} while (0)
+
+
+/* Verify the range of _val of (_stype) within _type */
+#define SPROM_VERIFY_RANGE(_type, _widen, _width, _min, _max, _val, \
+ _stype) \
+do { \
+ if (BHND_NVRAM_SIGNED_TYPE(_stype)) { \
+ int32_t sval = (int32_t) (_val); \
+ if (sval > (_max) || sval < (_min)) \
+ return (ERANGE); \
+ } else { \
+ if ((_val) > (_max)) \
+ return (ERANGE); \
+ } \
+} while(0)
+
+/*
+ * Table of supported SPROM image formats, sorted by image size, ascending.
+ */
+#define SPROM_FMT(_sz, _revmin, _revmax, _sig) \
+ { SPROM_SZ_ ## _sz, _revmin, _revmax, \
+ SPROM_SIG_ ## _sig ## _OFF, \
+ SPROM_SIG_ ## _sig }
+
+static const struct sprom_fmt {
+ size_t size;
+ uint8_t rev_min;
+ uint8_t rev_max;
+ size_t sig_offset;
+ uint16_t sig_req;
+} sprom_fmts[] = {
+ SPROM_FMT(R1_3, 1, 3, NONE),
+ SPROM_FMT(R4_8_9, 4, 4, R4),
+ SPROM_FMT(R4_8_9, 8, 9, R8_9),
+ SPROM_FMT(R10, 10, 10, R10),
+ SPROM_FMT(R11, 11, 11, R11)
+};
+
+/**
+ * Identify the SPROM format at @p offset within @p r, verify the CRC,
+ * and allocate a local shadow copy of the SPROM data.
+ *
+ * After successful initialization, @p r will not be accessed; any pin
+ * configuration required for SPROM access may be reset.
+ *
+ * @param[out] sprom On success, will be initialized with shadow of the SPROM
+ * data.
+ * @param r An active resource mapping the SPROM data.
+ * @param offset Offset of the SPROM data within @p resource.
+ */
+int
+bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r,
+ bus_size_t offset)
+{
+ bus_size_t res_size;
+ int error;
+
+ sprom->dev = rman_get_device(r->res);
+ sprom->sp_res = r;
+ sprom->sp_res_off = offset;
+
+ /* Determine maximum possible SPROM image size */
+ res_size = rman_get_size(r->res);
+ if (offset >= res_size)
+ return (EINVAL);
+
+ sprom->sp_size_max = MIN(res_size - offset, SPROM_SZ_MAX);
+
+ /* Allocate and populate SPROM shadow */
+ sprom->sp_size = 0;
+ sprom->sp_capacity = sprom->sp_size_max;
+ sprom->sp_shadow = malloc(sprom->sp_capacity, M_BHND_NVRAM, M_NOWAIT);
+ if (sprom->sp_shadow == NULL)
+ return (ENOMEM);
+
+ /* Read and identify SPROM image */
+ if ((error = sprom_populate_shadow(sprom)))
+ return (error);
+
+ return (0);
+}
+
+/**
+ * Release all resources held by @p sprom.
+ *
+ * @param sprom A SPROM instance previously initialized via bhnd_sprom_init().
+ */
+void
+bhnd_sprom_fini(struct bhnd_sprom *sprom)
+{
+ free(sprom->sp_shadow, M_BHND_NVRAM);
+}
+
+/* Perform a read using a SPROM offset descriptor, safely widening the result
+ * to its 32-bit representation before assigning it to @p _dest. */
+#define SPROM_GETVAR_READ(_type, _widen, _width, _min, _max, _sc, _off, \
+ _dest) \
+do { \
+ _type _v = (_type)SPROM_READ_ ## _width(_sc, _off->offset); \
+ if (_off->shift > 0) { \
+ _v >>= _off->shift; \
+ } else if (off->shift < 0) { \
+ _v <<= -_off->shift; \
+ } \
+ \
+ if (_off->cont) \
+ _dest |= ((uint32_t) (_widen) _v) & _off->mask; \
+ else \
+ _dest = ((uint32_t) (_widen) _v) & _off->mask; \
+} while(0)
+
+/* Emit a value read using a SPROM offset descriptor, narrowing the
+ * result output representation. */
+#define SPROM_GETVAR_WRITE(_type, _widen, _width, _min, _max, _off, \
+ _src, _buf) \
+do { \
+ _type _v = (_type) (_widen) _src; \
+ *((_type *)_buf) = _v; \
+} while(0)
+
+/* String format a value read using a SPROM offset descriptor */
+#define SPROM_GETVAR_SNPRINTF(_type, _widen, _width, _min, _max, _src, \
+ _buf, _remain, _fmt, _nwrite) \
+do { \
+ _nwrite = snprintf(_buf, _remain, _fmt, (_type) (_widen) _src); \
+} while(0)
+
+/**
+ * Read a SPROM variable, performing conversion to host byte order.
+ *
+ * @param sc The SPROM parser state.
+ * @param name The SPROM variable name.
+ * @param[out] buf On success, the requested value 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 size of the requested value.
+ * @param type The requested data type to be written to @p buf.
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too
+ * small to hold the requested value.
+ * @retval non-zero If reading @p name otherwise fails, a regular unix
+ * error code will be returned.
+ */
+int
+bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf,
+ size_t *len, bhnd_nvram_type type)
+{
+ const struct bhnd_nvram_vardefn *nv;
+ const struct bhnd_sprom_vardefn *sv;
+ void *outp;
+ size_t all1_offs;
+ size_t req_size, nelem;
+ size_t str_remain;
+ char str_delim;
+ uint32_t val;
+ int error;
+
+ error = sprom_get_var_defn(sc, name, &nv, &sv, &req_size, &nelem, type);
+ if (error)
+ return (error);
+
+ outp = buf;
+ str_remain = 0;
+ str_delim = '\0';
+
+
+ if (type != BHND_NVRAM_TYPE_CSTR) {
+ /* Provide required size */
+ if (outp == NULL) {
+ *len = req_size;
+ return (0);
+ }
+
+ /* Check (and update) target buffer len */
+ if (*len < req_size)
+ return (ENOMEM);
+ else
+ *len = req_size;
+ } else {
+ /* String length calculation requires performing
+ * the actual string formatting */
+ KASSERT(req_size == 0,
+ ("req_size set for variable-length type"));
+
+ /* If caller is querying length, the len argument
+ * may be uninitialized */
+ if (outp != NULL)
+ str_remain = *len;
+
+ /* Fetch delimiter for the variable's string format */
+ str_delim = sprom_get_delim_char(sc, nv->sfmt);
+ }
+
+ /* Read data */
+ all1_offs = 0;
+ val = 0;
+ for (size_t i = 0; i < sv->num_offsets; i++) {
+ const struct bhnd_sprom_offset *off;
+
+ off = &sv->offsets[i];
+ KASSERT(!off->cont || i > 0, ("cont marked on first offset"));
+
+ /* If not a continuation, advance the output buffer; if
+ * a C string, this requires appending a delimiter character */
+ if (i > 0 && !off->cont) {
+ size_t width = bhnd_nvram_type_width(type);
+
+ /* Non-fixed width types (such as CSTR) will have a 0
+ * width value */
+ if (width != 0) {
+ KASSERT(outp != NULL, ("NULL output buffer"));
+ outp = ((uint8_t *)outp) + width;
+ }
+
+ /* Append CSTR delim, if necessary */
+ if (type == BHND_NVRAM_TYPE_CSTR &&
+ str_delim != '\0' &&
+ i != 0)
+ {
+ if (outp != NULL && str_remain >= 1) {
+ *((char *)outp) = str_delim;
+ outp = ((char *)outp + 1);
+
+ /* Drop outp reference if we hit 0 */
+ if (str_remain-- == 0)
+ outp = NULL;
+ }
+
+ if (SIZE_MAX - 1 < req_size)
+ return (EFTYPE); /* too long */
+ req_size++;
+ }
+ }
+
+ /* Read the value, widening to a common uint32
+ * representation */
+ SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_READ, sc, off, val);
+
+ /* If IGNALL1, record whether value has all bits set. */
+ if (nv->flags & BHND_NVRAM_VF_IGNALL1) {
+ uint32_t all1;
+
+ all1 = off->mask;
+ if (off->shift > 0)
+ all1 >>= off->shift;
+ else if (off->shift < 0)
+ all1 <<= -off->shift;
+
+ if ((val & all1) == all1)
+ all1_offs++;
+ }
+
+ /* Skip writing if additional continuations remain */
+ if (i+1 < sv->num_offsets && sv->offsets[i].cont)
+ continue;
+
+ /* Perform write */
+ if (type == BHND_NVRAM_TYPE_CSTR) {
+ const char *fmtstr;
+ int written;
+
+ fmtstr = bhnd_nvram_type_fmt(off->type, nv->sfmt, i);
+ if (fmtstr == NULL) {
+ device_printf(sc->dev, "no NVRAM format string "
+ "for '%s' (type=%d)\n", name, off->type);
+ return (EOPNOTSUPP);
+ }
+
+ SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_SNPRINTF, val,
+ outp, str_remain, fmtstr, written);
+
+ if (written <= 0)
+ return (EFTYPE);
+
+ /* Calculate remaining capacity, drop outp reference
+ * if we hit 0 -- otherwise, advance the buffer
+ * position */
+ if (written >= str_remain) {
+ str_remain = 0;
+ outp = NULL;
+ } else {
+ str_remain -= written;
+ if (outp != NULL)
+ outp = (char *)outp + written;
+ }
+
+ /* Add additional bytes to total length */
+ if (SIZE_MAX - written < req_size)
+ return (EFTYPE); /* string too long */
+ req_size += written;
+ } else {
+ /* Verify range */
+ SPROM_SWITCH_TYPE(type, SPROM_VERIFY_RANGE, val,
+ off->type);
+
+ /* Write the value, narrowing to the appropriate output
+ * width. */
+ SPROM_SWITCH_TYPE(type, SPROM_GETVAR_WRITE, off, val,
+ outp);
+ }
+ }
+
+ /* Should value should be treated as uninitialized? */
+ if (nv->flags & BHND_NVRAM_VF_IGNALL1 && all1_offs == sv->num_offsets)
+ return (ENOENT);
+
+ /* If this is a C string request, we need to provide the computed
+ * length. */
+ if (type == BHND_NVRAM_TYPE_CSTR) {
+ /* Account for final trailing NUL */
+ if (SIZE_MAX - 1 < req_size)
+ return (EFTYPE); /* string too long */
+ req_size++;
+
+ /* Return an error if a too-small output buffer was provided */
+ if (buf != NULL && *len < req_size) {
+ *len = req_size;
+ return (ENOMEM);
+ }
+
+ *len = req_size;
+ }
+
+ return (0);
+}
+
+/* Perform a read of a variable offset from _src, safely widening the result
+ * to its 32-bit representation before assigning it to @p _dest. */
+#define SPROM_SETVAR_READ(_type, _widen, _width, _min, _max, _off, \
+ _src, _dest) \
+do { \
+ _type _v = *(const _type *)_src; \
+ if (_off->shift > 0) { \
+ _v <<= _off->shift; \
+ } else if (off->shift < 0) { \
+ _v >>= -_off->shift; \
+ } \
+ _dest = ((uint32_t) (_widen) _v) & _off->mask; \
+} while(0)
+
+
+/* Emit a value read using a SPROM offset descriptor, narrowing the
+ * result output representation and, if necessary, OR'ing it with the
+ * previously read value from @p _buf. */
+#define SPROM_SETVAR_WRITE(_type, _widen, _width, _min, _max, _sc, \
+ _off, _src) \
+do { \
+ _type _v = (_type) (_widen) _src; \
+ if (_off->cont) \
+ _v |= SPROM_READ_ ## _width(_sc, _off->offset); \
+ SPROM_WRITE_ ## _width(_sc, _off->offset, _v); \
+} while(0)
+
+/**
+ * Set a local value for a SPROM variable, performing conversion to SPROM byte
+ * order.
+ *
+ * The new value will be written to the backing SPROM shadow.
+ *
+ * @param sc The SPROM parser state.
+ * @param name The SPROM variable name.
+ * @param[out] buf The new value.
+ * @param[in,out] len The size of @p buf.
+ * @param type The data type of @p buf.
+ *
+ * @retval 0 success
+ * @retval ENOENT The requested variable was not found.
+ * @retval EINVAL If @p len does not match the expected variable size.
+ */
+int
+bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name, const void *buf,
+ size_t len, bhnd_nvram_type type)
+{
+ const struct bhnd_nvram_vardefn *nv;
+ const struct bhnd_sprom_vardefn *sv;
+ size_t req_size, nelem;
+ int error;
+ uint8_t crc;
+
+ error = sprom_get_var_defn(sc, name, &nv, &sv, &req_size, &nelem, type);
+ if (error)
+ return (error);
+
+ /* String parsing is currently unsupported */
+ if (type == BHND_NVRAM_TYPE_CSTR)
+ return (EOPNOTSUPP);
+
+ /* Provide required size */
+ if (len != req_size)
+ return (EINVAL);
+
+ /* Write data */
+ for (size_t i = 0; i < sv->num_offsets; i++) {
+ const struct bhnd_sprom_offset *off;
+ uint32_t val;
+
+ off = &sv->offsets[i];
+ KASSERT(!off->cont || i > 0, ("cont marked on first offset"));
+
+ /* If not a continuation, advance the input pointer */
+ if (i > 0 && !off->cont) {
+ buf = ((const uint8_t *)buf) +
+ bhnd_nvram_type_width(sv->offsets[i-1].type);
+ }
+
+ /* Read the value, widening to a common uint32
+ * representation */
+ SPROM_SWITCH_TYPE(nv->type, SPROM_SETVAR_READ, off, buf, val);
+
+ /* Verify range */
+ SPROM_SWITCH_TYPE(nv->type, SPROM_VERIFY_RANGE, val, type);
+
+ /* Write the value, narrowing to the appropriate output
+ * width. */
+ SPROM_SWITCH_TYPE(off->type, SPROM_SETVAR_WRITE, sc, off, val);
+ }
+
+ /* Update CRC */
+ crc = ~bhnd_nvram_crc8(sc->sp_shadow, SPROM_CRC_LEN(sc),
+ BHND_NVRAM_CRC8_INITIAL);
+ SPROM_WRITE_1(sc, SPROM_CRC_OFF(sc), crc);
+
+ return (0);
+}
+
+/* Read and identify the SPROM image by incrementally performing
+ * read + CRC of all supported image formats */
+static int
+sprom_populate_shadow(struct bhnd_sprom *sc)
+{
+ const struct sprom_fmt *fmt;
+ int error;
+ uint16_t sig;
+ uint8_t srom_rev;
+ uint8_t crc;
+
+ crc = BHND_NVRAM_CRC8_INITIAL;
+
+ /* Identify the SPROM revision (and populate the SPROM shadow) */
+ for (size_t i = 0; i < nitems(sprom_fmts); i++) {
+ fmt = &sprom_fmts[i];
+
+ /* Read image data and check CRC */
+ if ((error = sprom_extend_shadow(sc, fmt->size, &crc)))
+ return (error);
+
+ /* Skip on invalid CRC */
+ if (crc != BHND_NVRAM_CRC8_VALID)
+ continue;
+
+ /* Fetch SROM revision */
+ srom_rev = SPROM_REV(sc);
+
+ /* Early sromrev 1 devices (specifically some BCM440x enet
+ * cards) are reported to have been incorrectly programmed
+ * with a revision of 0x10. */
+ if (fmt->size == SPROM_SZ_R1_3 && srom_rev == 0x10)
+ srom_rev = 0x1;
+
+ /* Verify revision range */
+ if (srom_rev < fmt->rev_min || srom_rev > fmt->rev_max)
+ continue;
+
+ /* Verify signature (if any) */
+ sig = SPROM_SIG_NONE;
+ if (fmt->sig_offset != SPROM_SIG_NONE_OFF)
+ sig = SPROM_READ_2(sc, fmt->sig_offset);
+
+ if (sig != fmt->sig_req) {
+ device_printf(sc->dev,
+ "invalid sprom %hhu signature: 0x%hx "
+ "(expected 0x%hx)\n",
+ srom_rev, sig, fmt->sig_req);
+ return (EINVAL);
+ }
+
+ /* Identified */
+ sc->sp_rev = srom_rev;
+ return (0);
+ }
+
+ /* identification failed */
+ device_printf(sc->dev, "unrecognized SPROM format\n");
+ return (EINVAL);
+}
+
+/*
+ * Extend the shadowed SPROM buffer to image_size, reading any required
+ * data from the backing SPROM resource and updating the CRC.
+ */
+static int
+sprom_extend_shadow(struct bhnd_sprom *sc, size_t image_size,
+ uint8_t *crc)
+{
+ int error;
+
+ KASSERT(image_size >= sc->sp_size, (("shadow truncation unsupported")));
+
+ /* Verify the request fits within our shadow buffer */
+ if (image_size > sc->sp_capacity)
+ return (ENOSPC);
+
+ /* Skip no-op requests */
+ if (sc->sp_size == image_size)
+ return (0);
+
+ /* Populate the extended range */
+ error = sprom_direct_read(sc, sc->sp_size, sc->sp_shadow + sc->sp_size,
+ image_size - sc->sp_size, crc);
+ if (error)
+ return (error);
+
+ sc->sp_size = image_size;
+ return (0);
+}
+
+/**
+ * Read nbytes at the given offset from the backing SPROM resource, and
+ * update the CRC.
+ */
+static int
+sprom_direct_read(struct bhnd_sprom *sc, size_t offset, void *buf,
+ size_t nbytes, uint8_t *crc)
+{
+ bus_size_t res_offset;
+ uint16_t *p;
+
+ KASSERT(nbytes % sizeof(uint16_t) == 0, ("unaligned sprom size"));
+ KASSERT(offset % sizeof(uint16_t) == 0, ("unaligned sprom offset"));
+
+ /* Check for read overrun */
+ if (offset >= sc->sp_size_max || sc->sp_size_max - offset < nbytes) {
+ device_printf(sc->dev, "requested SPROM read would overrun\n");
+ return (EINVAL);
+ }
+
+ /* Perform read and update CRC */
+ p = (uint16_t *)buf;
+ res_offset = sc->sp_res_off + offset;
+
+ bhnd_bus_read_region_stream_2(sc->sp_res, res_offset, p,
+ (nbytes / sizeof(uint16_t)));
+ *crc = bhnd_nvram_crc8(p, nbytes, *crc);
+
+ return (0);
+}
+
+
+/**
+ * Locate the variable and SPROM revision-specific definitions
+ * for variable with @p name.
+ */
+static int
+sprom_get_var_defn(struct bhnd_sprom *sc, const char *name,
+ const struct bhnd_nvram_vardefn **var,
+ const struct bhnd_sprom_vardefn **sprom,
+ size_t *size, size_t *nelem, bhnd_nvram_type req_type)
+{
+ /* Find variable definition */
+ *var = bhnd_nvram_find_vardefn(name);
+ if (*var == NULL)
+ return (ENOENT);
+
+ /* Find revision-specific SPROM definition */
+ for (size_t i = 0; i < (*var)->num_sp_defs; i++) {
+ const struct bhnd_sprom_vardefn *sp = &(*var)->sp_defs[i];
+
+ if (sc->sp_rev < sp->compat.first)
+ continue;
+
+ if (sc->sp_rev > sp->compat.last)
+ continue;
+
+ /* Found */
+ *sprom = sp;
+
+ /* Calculate element count and total size, in bytes */
+ *nelem = 0;
+ for (size_t j = 0; j < sp->num_offsets; j++)
+ if (!sp->offsets[j].cont)
+ *nelem += 1;
+
+ *size = bhnd_nvram_type_width(req_type) * (*nelem);
+ return (0);
+ }
+
+ /* Not supported by this SPROM revision */
+ return (ENOENT);
+}
+
+/**
+ * Return the array element delimiter for @p sfmt, or '\0' if none.
+ */
+static char
+sprom_get_delim_char(struct bhnd_sprom *sc, bhnd_nvram_sfmt sfmt)
+{
+ switch (sfmt) {
+ case BHND_NVRAM_SFMT_HEX:
+ case BHND_NVRAM_SFMT_DEC:
+ return (',');
+
+ case BHND_NVRAM_SFMT_CCODE:
+ case BHND_NVRAM_SFMT_LEDDC:
+ return ('\0');
+
+ case BHND_NVRAM_SFMT_MACADDR:
+ return (':');
+
+ default:
+ device_printf(sc->dev, "unknown NVRAM string format: %d\n",
+ sfmt);
+ return (',');
+ }
+}
Index: head/sys/dev/bhnd/nvram/bhnd_sprom_parservar.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_sprom_parservar.h
+++ head/sys/dev/bhnd/nvram/bhnd_sprom_parservar.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2016 Landon Fuller <landon@landonf.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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BHND_NVRAM_SPROM_PARSERVAR_H_
+#define _BHND_NVRAM_SPROM_PARSERVAR_H_
+
+#include "bhnd_sprom_parser.h"
+
+#define SPROM_SZ_R1_3 128 /**< SPROM image size (rev 1-3) */
+#define SPROM_SZ_R4_8_9 440 /**< SPROM image size (rev 4, 8-9) */
+#define SPROM_SZ_R10 460 /**< SPROM image size (rev 10) */
+#define SPROM_SZ_R11 468 /**< SPROM image size (rev 11) */
+
+/** Maximum supported SPROM image size */
+#define SPROM_SZ_MAX SPROM_SZ_R11
+
+#define SPROM_SIG_NONE 0x0
+#define SPROM_SIG_NONE_OFF 0x0
+
+/** SPROM signature (rev 4) */
+#define SPROM_SIG_R4 0x5372
+#define SPROM_SIG_R4_OFF 64 /**< SPROM signature offset (rev 4) */
+
+/** SPROM signature (rev 8, 9) */
+#define SPROM_SIG_R8_9 SPROM_SIG_R4
+#define SPROM_SIG_R8_9_OFF 128 /**< SPROM signature offset (rev 8-9) */
+
+/** SPROM signature (rev 10) */
+#define SPROM_SIG_R10 SPROM_SIG_R4
+#define SPROM_SIG_R10_OFF 438 /**< SPROM signature offset (rev 10) */
+
+/** SPROM signature (rev 11) */
+#define SPROM_SIG_R11 0x0634
+#define SPROM_SIG_R11_OFF 128 /**< SPROM signature offset (rev 11) */
+
+#endif /* _BHND_NVRAM_SPROM_PARSERVAR_H_ */
Index: head/sys/dev/bhnd/nvram/bhnd_sprom_subr.c
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_sprom_subr.c
+++ head/sys/dev/bhnd/nvram/bhnd_sprom_subr.c
@@ -1,569 +0,0 @@
-/*-
- * Copyright (c) 2015 Landon Fuller <landon@landonf.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/bus.h>
-#include <sys/endian.h>
-#include <sys/rman.h>
-#include <sys/systm.h>
-
-#include <machine/bus.h>
-#include <machine/resource.h>
-
-#include <dev/bhnd/bhndvar.h>
-
-#include "nvramvar.h"
-
-#include "bhnd_spromreg.h"
-#include "bhnd_spromvar.h"
-
-/*
- * BHND SPROM Parser
- *
- * Provides identification, decoding, and encoding of BHND SPROM data.
- */
-
-static int sprom_direct_read(struct bhnd_sprom *sc, size_t offset,
- void *buf, size_t nbytes, uint8_t *crc);
-static int sprom_extend_shadow(struct bhnd_sprom *sc, size_t image_size,
- uint8_t *crc);
-static int sprom_populate_shadow(struct bhnd_sprom *sc);
-
-static int sprom_var_defn(struct bhnd_sprom *sc, const char *name,
- const struct bhnd_nvram_var **var,
- const struct bhnd_sprom_var **sprom, size_t *size);
-
-/* SPROM revision is always located at the second-to-last byte */
-#define SPROM_REV(_sc) SPROM_READ_1((_sc), (_sc)->sp_size - 2)
-
-/* SPROM CRC is always located at the last byte */
-#define SPROM_CRC_OFF(_sc) SPROM_CRC_LEN(_sc)
-
-/* SPROM CRC covers all but the final CRC byte */
-#define SPROM_CRC_LEN(_sc) ((_sc)->sp_size - 1)
-
-/* SPROM shadow I/O (with byte-order translation) */
-#define SPROM_READ_1(_sc, _off) SPROM_READ_ENC_1(_sc, _off)
-#define SPROM_READ_2(_sc, _off) le16toh(SPROM_READ_ENC_2(_sc, _off))
-#define SPROM_READ_4(_sc, _off) le32toh(SPROM_READ_ENC_4(_sc, _off))
-
-#define SPROM_WRITE_1(_sc, _off, _v) SPROM_WRITE_ENC_1(_sc, _off, (_v))
-#define SPROM_WRITE_2(_sc, _off, _v) SPROM_WRITE_ENC_2(_sc, _off, \
- htole16(_v))
-#define SPROM_WRITE_4(_sc, _off, _v) SPROM_WRITE_ENC_4(_sc, _off, \
- htole32(_v))
-
-/* SPROM shadow I/O (without byte-order translation) */
-#define SPROM_READ_ENC_1(_sc, _off) (*(uint8_t *)((_sc)->sp_shadow + _off))
-#define SPROM_READ_ENC_2(_sc, _off) (*(uint16_t *)((_sc)->sp_shadow + _off))
-#define SPROM_READ_ENC_4(_sc, _off) (*(uint32_t *)((_sc)->sp_shadow + _off))
-
-#define SPROM_WRITE_ENC_1(_sc, _off, _v) \
- *((uint8_t *)((_sc)->sp_shadow + _off)) = (_v)
-#define SPROM_WRITE_ENC_2(_sc, _off, _v) \
- *((uint16_t *)((_sc)->sp_shadow + _off)) = (_v)
-#define SPROM_WRITE_ENC_4(_sc, _off, _v) \
- *((uint32_t *)((_sc)->sp_shadow + _off)) = (_v)
-
-/* Call @p _next macro with the C type, widened (signed or unsigned) C
- * type, and width associated with @p _dtype */
-#define SPROM_SWITCH_TYPE(_dtype, _next, ...) \
-do { \
- switch (_dtype) { \
- case BHND_NVRAM_DT_UINT8: \
- _next (uint8_t, uint32_t, 1, \
- ## __VA_ARGS__); \
- break; \
- case BHND_NVRAM_DT_UINT16: \
- _next (uint16_t, uint32_t, 2, \
- ## __VA_ARGS__); \
- break; \
- case BHND_NVRAM_DT_UINT32: \
- _next (uint32_t, uint32_t, 4, \
- ## __VA_ARGS__); \
- break; \
- case BHND_NVRAM_DT_INT8: \
- _next (int8_t, int32_t, 1, \
- ## __VA_ARGS__); \
- break; \
- case BHND_NVRAM_DT_INT16: \
- _next (int16_t, int32_t, 2, \
- ## __VA_ARGS__); \
- break; \
- case BHND_NVRAM_DT_INT32: \
- _next (int32_t, int32_t, 4, \
- ## __VA_ARGS__); \
- break; \
- case BHND_NVRAM_DT_CHAR: \
- _next (uint8_t, uint32_t, 1, \
- ## __VA_ARGS__); \
- break; \
- } \
-} while (0)
-
-/*
- * Table of supported SPROM image formats, sorted by image size, ascending.
- */
-#define SPROM_FMT(_sz, _revmin, _revmax, _sig) \
- { SPROM_SZ_ ## _sz, _revmin, _revmax, \
- SPROM_SIG_ ## _sig ## _OFF, \
- SPROM_SIG_ ## _sig }
-
-static const struct sprom_fmt {
- size_t size;
- uint8_t rev_min;
- uint8_t rev_max;
- size_t sig_offset;
- uint16_t sig_req;
-} sprom_fmts[] = {
- SPROM_FMT(R1_3, 1, 3, NONE),
- SPROM_FMT(R4_8_9, 4, 4, R4),
- SPROM_FMT(R4_8_9, 8, 9, R8_9),
- SPROM_FMT(R10, 10, 10, R10),
- SPROM_FMT(R11, 11, 11, R11)
-};
-
-/**
- * Identify the SPROM format at @p offset within @p r, verify the CRC,
- * and allocate a local shadow copy of the SPROM data.
- *
- * After successful initialization, @p r will not be accessed; any pin
- * configuration required for SPROM access may be reset.
- *
- * @param[out] sprom On success, will be initialized with shadow of the SPROM
- * data.
- * @param r An active resource mapping the SPROM data.
- * @param offset Offset of the SPROM data within @p resource.
- */
-int
-bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r,
- bus_size_t offset)
-{
- bus_size_t res_size;
- int error;
-
- sprom->dev = rman_get_device(r->res);
- sprom->sp_res = r;
- sprom->sp_res_off = offset;
-
- /* Determine maximum possible SPROM image size */
- res_size = rman_get_size(r->res);
- if (offset >= res_size)
- return (EINVAL);
-
- sprom->sp_size_max = MIN(res_size - offset, SPROM_SZ_MAX);
-
- /* Allocate and populate SPROM shadow */
- sprom->sp_size = 0;
- sprom->sp_capacity = sprom->sp_size_max;
- sprom->sp_shadow = malloc(sprom->sp_capacity, M_BHND, M_NOWAIT);
- if (sprom->sp_shadow == NULL)
- return (ENOMEM);
-
- /* Read and identify SPROM image */
- if ((error = sprom_populate_shadow(sprom)))
- return (error);
-
- return (0);
-}
-
-/**
- * Release all resources held by @p sprom.
- *
- * @param sprom A SPROM instance previously initialized via bhnd_sprom_init().
- */
-void
-bhnd_sprom_fini(struct bhnd_sprom *sprom)
-{
- free(sprom->sp_shadow, M_BHND);
-}
-
-/* Perform a read using a SPROM offset descriptor, safely widening the
- * result to its 32-bit representation before assigning it to @p _dest. */
-#define SPROM_GETVAR_READ(_type, _widen, _width, _sc, _off, _dest) \
-do { \
- _type _v = (_type)SPROM_READ_ ## _width(_sc, _off->offset); \
- if (_off->shift > 0) { \
- _v >>= _off->shift; \
- } else if (off->shift < 0) { \
- _v <<= -_off->shift; \
- } \
- _dest = ((uint32_t) (_widen) _v) & _off->mask; \
-} while(0)
-
-/* Emit a value read using a SPROM offset descriptor, narrowing the
- * result output representation and, if necessary, OR'ing it with the
- * previously read value from @p _buf. */
-#define SPROM_GETVAR_WRITE(_type, _widen, _width, _off, _src, _buf) \
-do { \
- _type _v = (_type) (_widen) _src; \
- if (_off->cont) \
- _v |= *((_type *)_buf); \
- *((_type *)_buf) = _v; \
-} while(0)
-
-/**
- * Read a SPROM variable, performing conversion to host byte order.
- *
- * @param sc The SPROM parser state.
- * @param name The SPROM variable name.
- * @param[out] buf On success, the requested value 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 size of the requested value.
- *
- * @retval 0 success
- * @retval ENOENT The requested variable was not found.
- * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too
- * small to hold the requested value.
- * @retval non-zero If reading @p name otherwise fails, a regular unix
- * error code will be returned.
- */
-int
-bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf,
- size_t *len)
-{
- const struct bhnd_nvram_var *nv;
- const struct bhnd_sprom_var *sv;
- size_t all1_offs;
- size_t req_size;
- int error;
-
- if ((error = sprom_var_defn(sc, name, &nv, &sv, &req_size)))
- return (error);
-
- /* Provide required size */
- if (buf == NULL) {
- *len = req_size;
- return (0);
- }
-
- /* Check (and update) target buffer len */
- if (*len < req_size)
- return (ENOMEM);
- else
- *len = req_size;
-
- /* Read data */
- all1_offs = 0;
- for (size_t i = 0; i < sv->num_offsets; i++) {
- const struct bhnd_sprom_offset *off;
- uint32_t val;
-
- off = &sv->offsets[i];
- KASSERT(!off->cont || i > 0, ("cont marked on first offset"));
-
- /* If not a continuation, advance the output buffer */
- if (i > 0 && !off->cont) {
- buf = ((uint8_t *)buf) +
- bhnd_nvram_type_width(sv->offsets[i-1].type);
- }
-
- /* Read the value, widening to a common uint32
- * representation */
- SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_READ, sc, off, val);
-
- /* If IGNALL1, record whether value has all bits set. */
- if (nv->flags & BHND_NVRAM_VF_IGNALL1) {
- uint32_t all1;
-
- all1 = off->mask;
- if (off->shift > 0)
- all1 >>= off->shift;
- else if (off->shift < 0)
- all1 <<= -off->shift;
-
- if ((val & all1) == all1)
- all1_offs++;
- }
-
- /* Write the value, narrowing to the appropriate output
- * width. */
- SPROM_SWITCH_TYPE(nv->type, SPROM_GETVAR_WRITE, off, val, buf);
- }
-
- /* Should value should be treated as uninitialized? */
- if (nv->flags & BHND_NVRAM_VF_IGNALL1 && all1_offs == sv->num_offsets)
- return (ENOENT);
-
- return (0);
-}
-
-/* Perform a read of a variable offset from _src, safely widening the result
- * to its 32-bit representation before assigning it to @p
- * _dest. */
-#define SPROM_SETVAR_READ(_type, _widen, _width, _off, _src, _dest) \
-do { \
- _type _v = *(const _type *)_src; \
- if (_off->shift > 0) { \
- _v <<= _off->shift; \
- } else if (off->shift < 0) { \
- _v >>= -_off->shift; \
- } \
- _dest = ((uint32_t) (_widen) _v) & _off->mask; \
-} while(0)
-
-
-/* Emit a value read using a SPROM offset descriptor, narrowing the
- * result output representation and, if necessary, OR'ing it with the
- * previously read value from @p _buf. */
-#define SPROM_SETVAR_WRITE(_type, _widen, _width, _sc, _off, _src) \
-do { \
- _type _v = (_type) (_widen) _src; \
- if (_off->cont) \
- _v |= SPROM_READ_ ## _width(_sc, _off->offset); \
- SPROM_WRITE_ ## _width(_sc, _off->offset, _v); \
-} while(0)
-
-/**
- * Set a local value for a SPROM variable, performing conversion to SPROM byte
- * order.
- *
- * The new value will be written to the backing SPROM shadow.
- *
- * @param sc The SPROM parser state.
- * @param name The SPROM variable name.
- * @param[out] buf The new value.
- * @param[in,out] len The size of @p buf.
- *
- * @retval 0 success
- * @retval ENOENT The requested variable was not found.
- * @retval EINVAL If @p len does not match the expected variable size.
- */
-int
-bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name, const void *buf,
- size_t len)
-{
- const struct bhnd_nvram_var *nv;
- const struct bhnd_sprom_var *sv;
- size_t req_size;
- int error;
- uint8_t crc;
-
- if ((error = sprom_var_defn(sc, name, &nv, &sv, &req_size)))
- return (error);
-
- /* Provide required size */
- if (len != req_size)
- return (EINVAL);
-
- /* Write data */
- for (size_t i = 0; i < sv->num_offsets; i++) {
- const struct bhnd_sprom_offset *off;
- uint32_t val;
-
- off = &sv->offsets[i];
- KASSERT(!off->cont || i > 0, ("cont marked on first offset"));
-
- /* If not a continuation, advance the input pointer */
- if (i > 0 && !off->cont) {
- buf = ((const uint8_t *)buf) +
- bhnd_nvram_type_width(sv->offsets[i-1].type);
- }
-
- /* Read the value, widening to a common uint32
- * representation */
- SPROM_SWITCH_TYPE(nv->type, SPROM_SETVAR_READ, off, buf, val);
-
- /* Write the value, narrowing to the appropriate output
- * width. */
- SPROM_SWITCH_TYPE(off->type, SPROM_SETVAR_WRITE, sc, off, val);
- }
-
- /* Update CRC */
- crc = ~bhnd_nvram_crc8(sc->sp_shadow, SPROM_CRC_LEN(sc),
- BHND_NVRAM_CRC8_INITIAL);
- SPROM_WRITE_1(sc, SPROM_CRC_OFF(sc), crc);
-
- return (0);
-}
-
-/* Read and identify the SPROM image by incrementally performing
- * read + CRC of all supported image formats */
-static int
-sprom_populate_shadow(struct bhnd_sprom *sc)
-{
- const struct sprom_fmt *fmt;
- int error;
- uint16_t sig;
- uint8_t srom_rev;
- uint8_t crc;
-
- crc = BHND_NVRAM_CRC8_INITIAL;
-
- /* Identify the SPROM revision (and populate the SPROM shadow) */
- for (size_t i = 0; i < nitems(sprom_fmts); i++) {
- fmt = &sprom_fmts[i];
-
- /* Read image data and check CRC */
- if ((error = sprom_extend_shadow(sc, fmt->size, &crc)))
- return (error);
-
- /* Skip on invalid CRC */
- if (crc != BHND_NVRAM_CRC8_VALID)
- continue;
-
- /* Fetch SROM revision */
- srom_rev = SPROM_REV(sc);
-
- /* Early sromrev 1 devices (specifically some BCM440x enet
- * cards) are reported to have been incorrectly programmed
- * with a revision of 0x10. */
- if (fmt->size == SPROM_SZ_R1_3 && srom_rev == 0x10)
- srom_rev = 0x1;
-
- /* Verify revision range */
- if (srom_rev < fmt->rev_min || srom_rev > fmt->rev_max)
- continue;
-
- /* Verify signature (if any) */
- sig = SPROM_SIG_NONE;
- if (fmt->sig_offset != SPROM_SIG_NONE_OFF)
- sig = SPROM_READ_2(sc, fmt->sig_offset);
-
- if (sig != fmt->sig_req) {
- device_printf(sc->dev,
- "invalid sprom %hhu signature: 0x%hx "
- "(expected 0x%hx)\n",
- srom_rev, sig, fmt->sig_req);
- return (EINVAL);
- }
-
- /* Identified */
- sc->sp_rev = srom_rev;
- return (0);
- }
-
- /* identification failed */
- device_printf(sc->dev, "unrecognized SPROM format\n");
- return (EINVAL);
-}
-
-/*
- * Extend the shadowed SPROM buffer to image_size, reading any required
- * data from the backing SPROM resource and updating the CRC.
- */
-static int
-sprom_extend_shadow(struct bhnd_sprom *sc, size_t image_size,
- uint8_t *crc)
-{
- int error;
-
- KASSERT(image_size >= sc->sp_size, (("shadow truncation unsupported")));
-
- /* Verify the request fits within our shadow buffer */
- if (image_size > sc->sp_capacity)
- return (ENOSPC);
-
- /* Skip no-op requests */
- if (sc->sp_size == image_size)
- return (0);
-
- /* Populate the extended range */
- error = sprom_direct_read(sc, sc->sp_size, sc->sp_shadow + sc->sp_size,
- image_size - sc->sp_size, crc);
- if (error)
- return (error);
-
- sc->sp_size = image_size;
- return (0);
-}
-
-/**
- * Read nbytes at the given offset from the backing SPROM resource, and
- * update the CRC.
- */
-static int
-sprom_direct_read(struct bhnd_sprom *sc, size_t offset, void *buf,
- size_t nbytes, uint8_t *crc)
-{
- bus_size_t res_offset;
- uint16_t *p;
-
- KASSERT(nbytes % sizeof(uint16_t) == 0, ("unaligned sprom size"));
- KASSERT(offset % sizeof(uint16_t) == 0, ("unaligned sprom offset"));
-
- /* Check for read overrun */
- if (offset >= sc->sp_size_max || sc->sp_size_max - offset < nbytes) {
- device_printf(sc->dev, "requested SPROM read would overrun\n");
- return (EINVAL);
- }
-
- /* Perform read and update CRC */
- p = (uint16_t *)buf;
- res_offset = sc->sp_res_off + offset;
-
- bhnd_bus_read_region_stream_2(sc->sp_res, res_offset, p,
- (nbytes / sizeof(uint16_t)));
- *crc = bhnd_nvram_crc8(p, nbytes, *crc);
-
- return (0);
-}
-
-
-/**
- * Locate the variable and SPROM revision-specific definitions
- * for variable with @p name.
- */
-static int
-sprom_var_defn(struct bhnd_sprom *sc, const char *name,
- const struct bhnd_nvram_var **var,
- const struct bhnd_sprom_var **sprom,
- size_t *size)
-{
- /* Find variable definition */
- *var = bhnd_nvram_var_defn(name);
- if (*var == NULL)
- return (ENOENT);
-
- /* Find revision-specific SPROM definition */
- for (size_t i = 0; i < (*var)->num_sp_descs; i++) {
- const struct bhnd_sprom_var *sp = &(*var)->sprom_descs[i];
-
- if (sc->sp_rev < sp->compat.first)
- continue;
-
- if (sc->sp_rev > sp->compat.last)
- continue;
-
- /* Found */
- *sprom = sp;
-
- /* Calculate size in bytes */
- *size = bhnd_nvram_type_width((*var)->type) * sp->num_offsets;
- return (0);
- }
-
- /* Not supported by this SPROM revision */
- return (ENOENT);
-}
Index: head/sys/dev/bhnd/nvram/bhnd_spromreg.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_spromreg.h
+++ head/sys/dev/bhnd/nvram/bhnd_spromreg.h
@@ -1,63 +0,0 @@
-/*-
- * Copyright (c) 2016 Landon Fuller <landon@landonf.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.
- *
- * $FreeBSD$
- */
-
-#ifndef _BHND_NVRAM_SPROMREG_H_
-#define _BHND_NVRAM_SPROMREG_H_
-
-#define SPROM_SZ_R1_3 128 /**< SPROM image size (rev 1-3) */
-#define SPROM_SZ_R4_8_9 440 /**< SPROM image size (rev 4, 8-9) */
-#define SPROM_SZ_R10 460 /**< SPROM image size (rev 10) */
-#define SPROM_SZ_R11 468 /**< SPROM image size (rev 11) */
-
-/** Maximum supported SPROM image size */
-#define SPROM_SZ_MAX SPROM_SZ_R11
-
-#define SPROM_SIG_NONE 0x0
-#define SPROM_SIG_NONE_OFF 0x0
-
-/** SPROM signature (rev 4) */
-#define SPROM_SIG_R4 0x5372
-#define SPROM_SIG_R4_OFF 64 /**< SPROM signature offset (rev 4) */
-
-/** SPROM signature (rev 8, 9) */
-#define SPROM_SIG_R8_9 SPROM_SIG_R4
-#define SPROM_SIG_R8_9_OFF 128 /**< SPROM signature offset (rev 8-9) */
-
-/** SPROM signature (rev 10) */
-#define SPROM_SIG_R10 SPROM_SIG_R4
-#define SPROM_SIG_R10_OFF 438 /**< SPROM signature offset (rev 10) */
-
-/** SPROM signature (rev 11) */
-#define SPROM_SIG_R11 0x0634
-#define SPROM_SIG_R11_OFF 128 /**< SPROM signature offset (rev 11) */
-
-
-#endif /* _BHND_NVRAM_SPROMREG_H_ */
Index: head/sys/dev/bhnd/nvram/bhnd_spromvar.h
===================================================================
--- head/sys/dev/bhnd/nvram/bhnd_spromvar.h
+++ head/sys/dev/bhnd/nvram/bhnd_spromvar.h
@@ -34,8 +34,9 @@
#include <dev/bhnd/bhnd.h>
+#include "bhnd_sprom_parser.h"
+
DECLARE_CLASS(bhnd_sprom_driver);
-struct bhnd_sprom;
int bhnd_sprom_probe(device_t dev);
int bhnd_sprom_attach(device_t dev, bus_size_t offset);
@@ -43,32 +44,6 @@
int bhnd_sprom_suspend(device_t dev);
int bhnd_sprom_detach(device_t dev);
-int bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r,
- bus_size_t offset);
-void bhnd_sprom_fini(struct bhnd_sprom *sprom);
-int bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf,
- size_t *len);
-int bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name,
- const void *buf, size_t len);
-
-/**
- * bhnd sprom parser instance state.
- */
-struct bhnd_sprom {
- device_t dev; /**< sprom parent device */
-
- uint8_t sp_rev; /**< sprom revision */
-
- struct bhnd_resource *sp_res; /**< sprom resource. */
- bus_size_t sp_res_off; /**< offset to sprom image */
-
- uint8_t *sp_shadow; /**< sprom shadow */
- bus_size_t sp_size_max; /**< maximum possible sprom length */
- size_t sp_size; /**< shadow size */
- size_t sp_capacity; /**< shadow buffer capacity */
-};
-
-
/**
* bhnd_sprom driver instance state. Must be first member of all subclass
* softc structures.
Index: head/sys/dev/bhnd/nvram/nvram_subr.c
===================================================================
--- head/sys/dev/bhnd/nvram/nvram_subr.c
+++ head/sys/dev/bhnd/nvram/nvram_subr.c
@@ -1,149 +0,0 @@
-/*-
- * Copyright (c) 2016 Landon Fuller <landon@landonf.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/types.h>
-#include <sys/systm.h>
-
-#include "bhnd_nvram_map_data.h"
-
-/*
- * CRC-8 lookup table used to checksum SPROM and NVRAM data via
- * bhnd_nvram_crc8().
- *
- * Generated with following parameters:
- * polynomial: CRC-8 (x^8 + x^7 + x^6 + x^4 + x^2 + 1)
- * reflected bits: false
- * reversed: true
- */
-const uint8_t bhnd_nvram_crc8_tab[] = {
- 0x00, 0xf7, 0xb9, 0x4e, 0x25, 0xd2, 0x9c, 0x6b, 0x4a, 0xbd, 0xf3,
- 0x04, 0x6f, 0x98, 0xd6, 0x21, 0x94, 0x63, 0x2d, 0xda, 0xb1, 0x46,
- 0x08, 0xff, 0xde, 0x29, 0x67, 0x90, 0xfb, 0x0c, 0x42, 0xb5, 0x7f,
- 0x88, 0xc6, 0x31, 0x5a, 0xad, 0xe3, 0x14, 0x35, 0xc2, 0x8c, 0x7b,
- 0x10, 0xe7, 0xa9, 0x5e, 0xeb, 0x1c, 0x52, 0xa5, 0xce, 0x39, 0x77,
- 0x80, 0xa1, 0x56, 0x18, 0xef, 0x84, 0x73, 0x3d, 0xca, 0xfe, 0x09,
- 0x47, 0xb0, 0xdb, 0x2c, 0x62, 0x95, 0xb4, 0x43, 0x0d, 0xfa, 0x91,
- 0x66, 0x28, 0xdf, 0x6a, 0x9d, 0xd3, 0x24, 0x4f, 0xb8, 0xf6, 0x01,
- 0x20, 0xd7, 0x99, 0x6e, 0x05, 0xf2, 0xbc, 0x4b, 0x81, 0x76, 0x38,
- 0xcf, 0xa4, 0x53, 0x1d, 0xea, 0xcb, 0x3c, 0x72, 0x85, 0xee, 0x19,
- 0x57, 0xa0, 0x15, 0xe2, 0xac, 0x5b, 0x30, 0xc7, 0x89, 0x7e, 0x5f,
- 0xa8, 0xe6, 0x11, 0x7a, 0x8d, 0xc3, 0x34, 0xab, 0x5c, 0x12, 0xe5,
- 0x8e, 0x79, 0x37, 0xc0, 0xe1, 0x16, 0x58, 0xaf, 0xc4, 0x33, 0x7d,
- 0x8a, 0x3f, 0xc8, 0x86, 0x71, 0x1a, 0xed, 0xa3, 0x54, 0x75, 0x82,
- 0xcc, 0x3b, 0x50, 0xa7, 0xe9, 0x1e, 0xd4, 0x23, 0x6d, 0x9a, 0xf1,
- 0x06, 0x48, 0xbf, 0x9e, 0x69, 0x27, 0xd0, 0xbb, 0x4c, 0x02, 0xf5,
- 0x40, 0xb7, 0xf9, 0x0e, 0x65, 0x92, 0xdc, 0x2b, 0x0a, 0xfd, 0xb3,
- 0x44, 0x2f, 0xd8, 0x96, 0x61, 0x55, 0xa2, 0xec, 0x1b, 0x70, 0x87,
- 0xc9, 0x3e, 0x1f, 0xe8, 0xa6, 0x51, 0x3a, 0xcd, 0x83, 0x74, 0xc1,
- 0x36, 0x78, 0x8f, 0xe4, 0x13, 0x5d, 0xaa, 0x8b, 0x7c, 0x32, 0xc5,
- 0xae, 0x59, 0x17, 0xe0, 0x2a, 0xdd, 0x93, 0x64, 0x0f, 0xf8, 0xb6,
- 0x41, 0x60, 0x97, 0xd9, 0x2e, 0x45, 0xb2, 0xfc, 0x0b, 0xbe, 0x49,
- 0x07, 0xf0, 0x9b, 0x6c, 0x22, 0xd5, 0xf4, 0x03, 0x4d, 0xba, 0xd1,
- 0x26, 0x68, 0x9f
-};
-
-
-/**
- * Return the size of type @p dt.
- *
- * @param dt NVRAM data type.
- * @result the byte width of @p dt.
- */
-size_t
-bhnd_nvram_type_width(bhnd_nvram_dt dt)
-{
- switch (dt) {
- case BHND_NVRAM_DT_INT8:
- case BHND_NVRAM_DT_UINT8:
- case BHND_NVRAM_DT_CHAR:
- return (sizeof(uint8_t));
-
- case BHND_NVRAM_DT_INT16:
- case BHND_NVRAM_DT_UINT16:
- return (sizeof(uint16_t));
-
- case BHND_NVRAM_DT_INT32:
- case BHND_NVRAM_DT_UINT32:
- return (sizeof(uint32_t));
- }
-
- /* Quiesce gcc4.2 */
- panic("bhnd nvram data type %u unknown", dt);
-}
-
-
-/**
- * Return the variable definition for @p varname, if any.
- *
- * @param varname variable name
- *
- * @retval bhnd_nvram_var If a valid definition for @p varname is found.
- * @retval NULL If no definition for @p varname is found.
- */
-const struct bhnd_nvram_var *
-bhnd_nvram_var_defn(const char *varname)
-{
- size_t min, mid, max;
- int order;
-
- /*
- * Locate the requested variable using a binary search.
- *
- * The variable table is guaranteed to be sorted in lexicographical
- * order (using the 'C' locale for collation rules)
- */
- min = 0;
- mid = 0;
- max = nitems(bhnd_nvram_vars) - 1;
-
- while (max >= min) {
- /* Select midpoint */
- mid = (min + max) / 2;
-
- /* Determine which side of the partition to search */
- order = strcmp(bhnd_nvram_vars[mid].name, varname);
- if (order < 0) {
- /* Search upper partition */
- min = mid + 1;
- } else if (order > 0) {
- /* Search lower partition */
- max = mid - 1;
- } else if (order == 0) {
- /* Match found */
- return (&bhnd_nvram_vars[mid]);
- }
- }
-
- /* Not found */
- return (NULL);
-}
Index: head/sys/dev/bhnd/nvram/nvramvar.h
===================================================================
--- head/sys/dev/bhnd/nvram/nvramvar.h
+++ head/sys/dev/bhnd/nvram/nvramvar.h
@@ -1,128 +0,0 @@
-/*-
- * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.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.
- *
- * $FreeBSD$
- */
-
-#ifndef _BHND_NVRAM_BHND_NVRAMVAR_H_
-#define _BHND_NVRAM_BHND_NVRAMVAR_H_
-
-/** NVRAM Primitive data types */
-typedef enum {
- BHND_NVRAM_DT_UINT8 = 0, /**< unsigned 8-bit integer */
- BHND_NVRAM_DT_UINT16 = 1, /**< unsigned 16-bit integer */
- BHND_NVRAM_DT_UINT32 = 2, /**< unsigned 32-bit integer */
- BHND_NVRAM_DT_INT8 = 3, /**< signed 8-bit integer */
- BHND_NVRAM_DT_INT16 = 4, /**< signed 16-bit integer */
- BHND_NVRAM_DT_INT32 = 5, /**< signed 32-bit integer */
- BHND_NVRAM_DT_CHAR = 6, /**< ASCII char */
-} bhnd_nvram_dt;
-
-/** NVRAM data type string representations */
-typedef enum {
- BHND_NVRAM_VFMT_HEX = 1, /**< hex format */
- BHND_NVRAM_VFMT_DEC = 2, /**< decimal format */
- BHND_NVRAM_VFMT_MACADDR = 3, /**< mac address (canonical form, hex octets,
- separated with ':') */
- BHND_NVRAM_VFMT_LEDDC = 4, /**< LED PWM duty-cycle (2 bytes -- on/off) */
- BHND_NVRAM_VFMT_CCODE = 5 /**< count code format (2-3 ASCII chars, or hex string) */
-} bhnd_nvram_fmt;
-
-/** NVRAM variable flags */
-enum {
- BHND_NVRAM_VF_ARRAY = (1<<0), /**< variable is an array */
- BHND_NVRAM_VF_MFGINT = (1<<1), /**< mfg-internal variable; should not be externally visible */
- BHND_NVRAM_VF_IGNALL1 = (1<<2) /**< hide variable if its value has all bits set. */
-};
-
-#define BHND_SPROMREV_MAX UINT8_MAX /**< maximum supported SPROM revision */
-
-/** SPROM revision compatibility declaration */
-struct bhnd_sprom_compat {
- uint8_t first; /**< first compatible SPROM revision */
- uint8_t last; /**< last compatible SPROM revision, or BHND_SPROMREV_MAX */
-};
-
-/** SPROM value descriptor */
-struct bhnd_sprom_offset {
- uint16_t offset; /**< byte offset within SPROM */
- bool cont:1; /**< value should be bitwise OR'd with the
- * previous offset descriptor */
- bhnd_nvram_dt type:7; /**< data type */
- int8_t shift; /**< shift to be applied to the value */
- uint32_t mask; /**< mask to be applied to the value(s) */
-};
-
-/** SPROM-specific variable definition */
-struct bhnd_sprom_var {
- struct bhnd_sprom_compat compat; /**< sprom compatibility declaration */
- const struct bhnd_sprom_offset *offsets; /**< offset descriptors */
- size_t num_offsets; /**< number of offset descriptors */
-};
-
-/** NVRAM variable definition */
-struct bhnd_nvram_var {
- const char *name; /**< variable name */
- bhnd_nvram_dt type; /**< base data type */
- bhnd_nvram_fmt fmt; /**< string format */
- uint32_t flags; /**< BHND_NVRAM_VF_* flags */
-
- const struct bhnd_sprom_var *sprom_descs; /**< SPROM-specific variable descriptors */
- size_t num_sp_descs; /**< number of sprom descriptors */
-};
-
-size_t bhnd_nvram_type_width(bhnd_nvram_dt dt);
-const struct bhnd_nvram_var *bhnd_nvram_var_defn(const char *varname);
-
-/** Initial bhnd_nvram_crc8 value */
-#define BHND_NVRAM_CRC8_INITIAL 0xFF
-
-/** Valid CRC-8 checksum */
-#define BHND_NVRAM_CRC8_VALID 0x9F
-
-extern const uint8_t bhnd_nvram_crc8_tab[];
-
-/**
- * Calculate CRC-8 over @p buf.
- *
- * @param buf input buffer
- * @param size buffer size
- * @param crc last computed crc, or BHND_NVRAM_CRC8_INITIAL
- */
-static inline uint8_t
-bhnd_nvram_crc8(const void *buf, size_t size, uint8_t crc)
-{
- const uint8_t *p = (const uint8_t *)buf;
- while (size--)
- crc = bhnd_nvram_crc8_tab[(crc ^ *p++)];
-
- return (crc);
-}
-
-
-#endif /* _BHND_NVRAM_BHND_NVRAMVAR_H_ */
\ No newline at end of file
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
@@ -108,20 +108,20 @@
}
# Format Constants
- FMT["hex"] = "BHND_NVRAM_VFMT_HEX"
- FMT["decimal"] = "BHND_NVRAM_VFMT_DEC"
- FMT["ccode"] = "BHND_NVRAM_VFMT_CCODE"
- FMT["macaddr"] = "BHND_NVRAM_VFMT_MACADDR"
- FMT["led_dc"] = "BHND_NVRAM_VFMT_LEDDC"
+ FMT["hex"] = "BHND_NVRAM_SFMT_HEX"
+ FMT["decimal"] = "BHND_NVRAM_SFMT_DEC"
+ FMT["ccode"] = "BHND_NVRAM_SFMT_CCODE"
+ FMT["macaddr"] = "BHND_NVRAM_SFMT_MACADDR"
+ FMT["led_dc"] = "BHND_NVRAM_SFMT_LEDDC"
# Data Type Constants
- DTYPE["u8"] = "BHND_NVRAM_DT_UINT8"
- DTYPE["u16"] = "BHND_NVRAM_DT_UINT16"
- DTYPE["u32"] = "BHND_NVRAM_DT_UINT32"
- DTYPE["i8"] = "BHND_NVRAM_DT_INT8"
- DTYPE["i16"] = "BHND_NVRAM_DT_INT16"
- DTYPE["i32"] = "BHND_NVRAM_DT_INT32"
- DTYPE["char"] = "BHND_NVRAM_DT_CHAR"
+ DTYPE["u8"] = "BHND_NVRAM_TYPE_UINT8"
+ DTYPE["u16"] = "BHND_NVRAM_TYPE_UINT16"
+ DTYPE["u32"] = "BHND_NVRAM_TYPE_UINT32"
+ DTYPE["i8"] = "BHND_NVRAM_TYPE_INT8"
+ DTYPE["i16"] = "BHND_NVRAM_TYPE_INT16"
+ DTYPE["i32"] = "BHND_NVRAM_TYPE_INT32"
+ DTYPE["char"] = "BHND_NVRAM_TYPE_CHAR"
# Default masking for standard types
TMASK["u8"] = "0x000000FF"
@@ -259,10 +259,10 @@
emit("}, " num_offs_written "},\n")
}
-# emit the bhnd_nvram_var definition for variable name `v`
-function emit_var_defn (v)
+# emit a bhnd_nvram_vardef for variable name `v`
+function emit_nvram_vardef (v)
{
- emit(sprintf("{\"%s\", %s, %s, %s, (struct bhnd_sprom_var[]) {\n",
+ emit(sprintf("{\"%s\", %s, %s, %s, (struct bhnd_sprom_vardefn[]) {\n",
v suffix,
DTYPE[vars[v,VAR_BASE_TYPE]],
FMT[vars[v,VAR_FMT]],
@@ -430,12 +430,12 @@
# Emit all variable definitions
if (OUT_T == OUT_T_DATA) {
- emit("#include <dev/bhnd/nvram/nvramvar.h>\n")
- emit("static const struct bhnd_nvram_var bhnd_nvram_vars[] = "\
- "{\n")
+ emit("#include <dev/bhnd/nvram/bhnd_nvram_common.h>\n")
+ emit("static const struct bhnd_nvram_vardefn "\
+ "bhnd_nvram_vardefs[] = {\n")
output_depth++
for (i = 0; i < num_output_vars; i++)
- emit_var_defn(output_vars[i])
+ emit_nvram_vardef(output_vars[i])
output_depth--
emit("};\n")
} else if (OUT_T == OUT_T_HEADER) {
Index: head/sys/dev/bwn/bwn_mac.c
===================================================================
--- head/sys/dev/bwn/bwn_mac.c
+++ head/sys/dev/bwn/bwn_mac.c
@@ -103,13 +103,13 @@
device_printf(dev, "got rid=%d res=%p\n", sc->rspec[0].rid, r);
uint8_t macaddr[6];
- error = bhnd_nvram_getvar(dev, BHND_NVAR_MACADDR, macaddr,
- sizeof(macaddr));
+ error = bhnd_nvram_getvar_array(dev, BHND_NVAR_MACADDR, macaddr,
+ sizeof(macaddr), BHND_NVRAM_TYPE_UINT8);
if (error)
return (error);
device_printf(dev, "got macaddr %6D\n", macaddr, ":");
-
+
return (0);
}
Index: head/sys/mips/conf/BCM
===================================================================
--- head/sys/mips/conf/BCM
+++ head/sys/mips/conf/BCM
@@ -13,6 +13,8 @@
# ships with cfe firmware
options CFE
+device cfe
+
options ALT_BREAK_TO_DEBUGGER
options BREAK_TO_DEBUGGER
options BOOTVERBOSE=0
Index: head/sys/mips/conf/BCM.hints
===================================================================
--- head/sys/mips/conf/BCM.hints
+++ head/sys/mips/conf/BCM.hints
@@ -2,3 +2,6 @@
hint.bhnd.0.at="nexus0"
hint.bhnd.0.maddr="0x18000000"
hint.bhnd.0.msize="0x00100000"
+
+# NVRAM via CFE
+hint.bhnd_nvram.0.at="nexus0"
Index: head/sys/mips/conf/SENTRY5
===================================================================
--- head/sys/mips/conf/SENTRY5
+++ head/sys/mips/conf/SENTRY5
@@ -35,6 +35,7 @@
options CFE
options CFE_CONSOLE
options ALT_BREAK_TO_DEBUGGER
+device cfe
makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
makeoptions MODULES_OVERRIDE=""
Index: head/sys/mips/conf/SENTRY5.hints
===================================================================
--- head/sys/mips/conf/SENTRY5.hints
+++ head/sys/mips/conf/SENTRY5.hints
@@ -2,3 +2,6 @@
hint.bhnd.0.at="nexus0"
hint.bhnd.0.maddr="0x18000000"
hint.bhnd.0.msize="0x00100000"
+
+# NVRAM via CFE
+hint.bhnd_nvram.0.at="nexus0"
Index: head/sys/modules/bhnd/Makefile
===================================================================
--- head/sys/modules/bhnd/Makefile
+++ head/sys/modules/bhnd/Makefile
@@ -4,10 +4,15 @@
.PATH: ${.CURDIR}/../../dev/bhnd/nvram
KMOD= bhnd
-SRCS= bhnd.c bhnd_subr.c \
- bhnd_sprom.c bhnd_sprom_subr.c \
- nvram_subr.c \
- bhnd_nvram_map.h bhnd_nvram_map_data.h
+SRCS= bhnd.c \
+ bhnd_subr.c
+
+SRCS+= bhnd_nvram.c \
+ bhnd_nvram_parser.c \
+ bhnd_sprom.c \
+ bhnd_sprom_parser.c
+SRCS+= bhnd_nvram_common.c
+SRCS+= bhnd_nvram_map.h bhnd_nvram_map_data.h
SRCS+= bhnd_bus_if.c bhnd_bus_if.h \
bhnd_chipc_if.c bhnd_chipc_if.h \

File Metadata

Mime Type
text/plain
Expires
Wed, May 27, 3:16 PM (3 h, 18 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33561084
Default Alt Text
D7489.id.diff (182 KB)

Event Timeline