Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F158001327
D7489.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
182 KB
Referenced Files
None
Subscribers
None
D7489.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Thu, May 28, 9:03 AM (21 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33561084
Default Alt Text
D7489.diff (182 KB)
Attached To
Mode
D7489: [bhnd] Initial NVRAM support
Attached
Detach File
Event Timeline
Log In to Comment