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 __FBSDID("$FreeBSD$"); -#include #include #include #include @@ -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 +#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 + * 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 +__FBSDID("$FreeBSD$"); + +/* + * BHND CFE NVRAM driver. + * + * Provides access to device NVRAM via CFE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#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 + * 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 +__FBSDID("$FreeBSD$"); + +/* + * BHND CFE NVRAM driver. + * + * Provides access to device NVRAM via CFE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#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 + * 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 +#include + +#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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#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 + * 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 +#include + +#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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 + * 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 + * 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 + +#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 + * 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 +#include + +#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 + * 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 + +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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 + * 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 - * 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 -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#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 - * 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 +#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 - * 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 -__FBSDID("$FreeBSD$"); - -#include -#include -#include - -#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 - * 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 \n") - emit("static const struct bhnd_nvram_var bhnd_nvram_vars[] = "\ - "{\n") + emit("#include \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 \