Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -1244,6 +1244,7 @@ dev/bhnd/nvram/bhnd_nvram_iores.c optional bhnd dev/bhnd/nvram/bhnd_nvram_plist.c optional bhnd dev/bhnd/nvram/bhnd_nvram_store.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_store_subr.c optional bhnd dev/bhnd/nvram/bhnd_nvram_subr.c optional bhnd dev/bhnd/nvram/bhnd_nvram_value.c optional bhnd dev/bhnd/nvram/bhnd_nvram_value_fmts.c optional bhnd Index: sys/dev/bhnd/nvram/bhnd_nvram_data.h =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_data.h +++ sys/dev/bhnd/nvram/bhnd_nvram_data.h @@ -44,6 +44,7 @@ #include "bhnd_nvram.h" #include "bhnd_nvram_io.h" +#include "bhnd_nvram_value.h" /* NVRAM data class */ typedef struct bhnd_nvram_data_class bhnd_nvram_data_class; @@ -108,7 +109,6 @@ bhnd_nvram_data_class *bhnd_nvram_data_get_class(struct bhnd_nvram_data *nv); size_t bhnd_nvram_data_count(struct bhnd_nvram_data *nv); - int bhnd_nvram_data_size(struct bhnd_nvram_data *nv, size_t *size); @@ -119,10 +119,13 @@ const char *bhnd_nvram_data_next(struct bhnd_nvram_data *nv, void **cookiep); - void *bhnd_nvram_data_find(struct bhnd_nvram_data *nv, const char *name); +int bhnd_nvram_data_getvar_order( + struct bhnd_nvram_data *nv, void *cookiep1, + void *cookiep2); + int bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, size_t *len, bhnd_nvram_type type); @@ -133,4 +136,13 @@ const char *bhnd_nvram_data_getvar_name(struct bhnd_nvram_data *nv, void *cookiep); +int bhnd_nvram_data_copy_val(struct bhnd_nvram_data *nv, + void *cookiep, bhnd_nvram_val **val); + +int bhnd_nvram_data_filter_setvar( + struct bhnd_nvram_data *nv, const char *name, + bhnd_nvram_val *value, bhnd_nvram_val **result); +int bhnd_nvram_data_filter_unsetvar( + struct bhnd_nvram_data *nv, const char *name); + #endif /* _BHND_NVRAM_BHND_NVRAM_DATA_H_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_data.c =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_data.c +++ sys/dev/bhnd/nvram/bhnd_nvram_data.c @@ -350,7 +350,26 @@ const char * bhnd_nvram_data_next(struct bhnd_nvram_data *nv, void **cookiep) { - return (nv->cls->op_next(nv, cookiep)); + const char *name; +#ifdef BHND_NV_INVARIANTS + void *prev = *cookiep; +#endif + + /* Fetch next */ + if ((name = nv->cls->op_next(nv, cookiep)) == NULL) + return (NULL); + + /* Enforce precedence ordering invariant between bhnd_nvram_data_next() + * and bhnd_nvram_data_getvar_order() */ +#ifdef BHND_NV_INVARIANTS + if (prev != NULL && + bhnd_nvram_data_getvar_order(nv, prev, *cookiep) > 0) + { + BHND_NV_PANIC("%s: returned out-of-order entry", __FUNCTION__); + } +#endif + + return (name); } /** @@ -388,7 +407,7 @@ cookiep = NULL; while ((next = bhnd_nvram_data_next(nv, &cookiep))) { - if (strcasecmp(name, next) == 0) + if (strcmp(name, next) == 0) return (cookiep); } @@ -397,6 +416,37 @@ } /** + * Compare the declaration order of two NVRAM variables. + * + * Variable declaration order is used to determine the current order of + * the variables in the source data, as well as to determine the precedence + * of variable declarations in data sources that define duplicate names. + * + * The comparison order will match the order of variables returned via + * bhnd_nvstore_path_data_next(). + * + * @param nv The NVRAM data. + * @param cookiep1 An NVRAM variable cookie previously + * returned via bhnd_nvram_data_next() or + * bhnd_nvram_data_find(). + * @param cookiep2 An NVRAM variable cookie previously + * returned via bhnd_nvram_data_next() or + * bhnd_nvram_data_find(). + * + * @retval <= -1 If @p cookiep1 has an earlier declaration order than + * @p cookiep2. + * @retval 0 If @p cookiep1 and @p cookiep2 are identical. + * @retval >= 1 If @p cookiep has a later declaration order than + * @p cookiep2. + */ +int +bhnd_nvram_data_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1, + void *cookiep2) +{ + return (nv->cls->op_getvar_order(nv, cookiep1, cookiep2)); +} + +/** * Read a variable and decode as @p type. * * @param nv The NVRAM data. @@ -423,6 +473,58 @@ return (nv->cls->op_getvar(nv, cookiep, buf, len, type)); } +/* + * Common bhnd_nvram_data_getvar_ptr() wrapper used by + * bhnd_nvram_data_generic_rp_getvar() and + * bhnd_nvram_data_generic_rp_copy_val(). + * + * If a variable definition for the requested variable is found via + * bhnd_nvram_find_vardefn(), the definition will be used to populate fmt. + */ +static const void * +bhnd_nvram_data_getvar_ptr_info(struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type, const bhnd_nvram_val_fmt **fmt) +{ + const struct bhnd_nvram_vardefn *vdefn; + const char *name; + const void *vptr; + + BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR, + ("instance does not advertise READ_PTR support")); + + /* Fetch pointer to variable data */ + vptr = bhnd_nvram_data_getvar_ptr(nv, cookiep, len, type); + if (vptr == NULL) + return (NULL); + + /* Select a default value format implementation */ + + + /* Fetch the reference variable name */ + name = bhnd_nvram_data_getvar_name(nv, cookiep); + + /* Trim path prefix, if any; the Broadcom NVRAM format assumes a global + * namespace for all variable definitions */ + if (bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_DEVPATHS) + name = bhnd_nvram_trim_path_name(name); + + /* Check the variable definition table for a matching entry; if + * it exists, use it to populate the value format. */ + vdefn = bhnd_nvram_find_vardefn(name); + if (vdefn != NULL) { + BHND_NV_ASSERT(vdefn->fmt != NULL, + ("NULL format for %s", name)); + *fmt = vdefn->fmt; + } else if (*type == BHND_NVRAM_TYPE_STRING) { + /* Default to Broadcom-specific string interpretation */ + *fmt = &bhnd_nvram_val_bcm_string_fmt; + } else { + /* Fall back on native formatting */ + *fmt = bhnd_nvram_val_default_fmt(*type); + } + + return (vptr); +} /** * A generic implementation of bhnd_nvram_data_getvar(). @@ -432,17 +534,15 @@ * of the caller. * * If a variable definition for the requested variable is available via - * bhnd_nvram_find_vardefn(), the definition will be used to provide - * formatting hints to bhnd_nvram_coerce_value(). + * bhnd_nvram_find_vardefn(), the definition will be used to provide a + * formatting instance to bhnd_nvram_val_init(). */ int bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *outp, size_t *olen, bhnd_nvram_type otype) { bhnd_nvram_val val; - const struct bhnd_nvram_vardefn *vdefn; const bhnd_nvram_val_fmt *fmt; - const char *name; const void *vptr; bhnd_nvram_type vtype; size_t vlen; @@ -451,28 +551,12 @@ BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR, ("instance does not advertise READ_PTR support")); - /* Fetch pointer to our variable data */ - vptr = bhnd_nvram_data_getvar_ptr(nv, cookiep, &vlen, &vtype); + /* Fetch variable data and value format*/ + vptr = bhnd_nvram_data_getvar_ptr_info(nv, cookiep, &vlen, &vtype, + &fmt); if (vptr == NULL) return (EINVAL); - /* Use the NVRAM string support */ - switch (vtype) { - case BHND_NVRAM_TYPE_STRING: - case BHND_NVRAM_TYPE_STRING_ARRAY: - fmt = &bhnd_nvram_val_bcm_string_fmt; - break; - default: - fmt = NULL; - } - - /* Check the variable definition table for a matching entry; if - * it exists, use it to populate the value format. */ - name = bhnd_nvram_data_getvar_name(nv, cookiep); - vdefn = bhnd_nvram_find_vardefn(name); - if (vdefn != NULL) - fmt = vdefn->fmt; - /* Attempt value coercion */ error = bhnd_nvram_val_init(&val, fmt, vptr, vlen, vtype, BHND_NVRAM_VAL_BORROW_DATA); @@ -487,6 +571,63 @@ } /** + * Return a caller-owned copy of an NVRAM entry's variable data. + * + * The caller is responsible for deallocating the returned value via + * bhnd_nvram_val_release(). + * + * @param nv The NVRAM data. + * @param cookiep An NVRAM variable cookie previously returned + * via bhnd_nvram_data_next() or bhnd_nvram_data_find(). + * @param[out] value On success, the caller-owned value instance. + * + * @retval 0 success + * @retval ENOMEM If allocation fails. + * @retval non-zero If initialization of the value otherwise fails, a + * regular unix error code will be returned. + */ +int +bhnd_nvram_data_copy_val(struct bhnd_nvram_data *nv, void *cookiep, + bhnd_nvram_val **value) +{ + return (nv->cls->op_copy_val(nv, cookiep, value)); +} + +/** + * A generic implementation of bhnd_nvram_data_copy_val(). + * + * This implementation will call bhnd_nvram_data_getvar_ptr() to fetch + * a pointer to the variable data and perform data coercion on behalf + * of the caller. + * + * If a variable definition for the requested variable is available via + * bhnd_nvram_find_vardefn(), the definition will be used to provide a + * formatting instance to bhnd_nvram_val_init(). + */ +int +bhnd_nvram_data_generic_rp_copy_val(struct bhnd_nvram_data *nv, + void *cookiep, bhnd_nvram_val **value) +{ + const bhnd_nvram_val_fmt *fmt; + const void *vptr; + bhnd_nvram_type vtype; + size_t vlen; + + BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR, + ("instance does not advertise READ_PTR support")); + + /* Fetch variable data and value format*/ + vptr = bhnd_nvram_data_getvar_ptr_info(nv, cookiep, &vlen, &vtype, + &fmt); + if (vptr == NULL) + return (EINVAL); + + /* Allocate and return the new value instance */ + return (bhnd_nvram_val_new(value, fmt, vptr, vlen, vtype, + BHND_NVRAM_VAL_DYNAMIC)); +} + +/** * If available and supported by the NVRAM data instance, return a reference * to the internal buffer containing an entry's variable data, * @@ -526,3 +667,44 @@ { return (nv->cls->op_getvar_name(nv, cookiep)); } + +/** + * Filter a request to set variable @p name with @p value. + * + * On success, the caller owns a reference to @p result, and must release + * any held resources via bhnd_nvram_val_release(). + * + * @param nv The NVRAM data instance. + * @param name The name of the variable to be set. + * @param value The proposed value to be set. + * @param[out] result On success, a caller-owned reference to the filtered + * value to be set. + * + * @retval 0 success + * @retval ENOENT if @p name is unrecognized by @p nv. + * @retval EINVAL if @p name is read-only. + * @retval EINVAL if @p value cannot be converted to the required value + * type. + */ +int +bhnd_nvram_data_filter_setvar(struct bhnd_nvram_data *nv, const char *name, + bhnd_nvram_val *value, bhnd_nvram_val **result) +{ + return (nv->cls->op_filter_setvar(nv, name, value, result)); +} + +/** + * Filter a request to delete variable @p name. + * + * @param nv The NVRAM data instance. + * @param name The name of the variable to be deleted. + * + * @retval 0 success + * @retval ENOENT if @p name is unrecognized by @p nv. + * @retval EINVAL if @p name is read-only. + */ +int +bhnd_nvram_data_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name) +{ + return (nv->cls->op_filter_unsetvar(nv, name)); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c +++ sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c @@ -618,7 +618,7 @@ return (NULL); } - *cookiep = (void *)(uintptr_t)envp; + *cookiep = __DECONST(void *, envp); return (envp); } @@ -629,12 +629,52 @@ } static int +bhnd_nvram_bcm_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1, + void *cookiep2) +{ + struct bhnd_nvram_bcm *bcm; + struct bhnd_nvram_bcm_hvar *hvar1, *hvar2; + + bcm = (struct bhnd_nvram_bcm *)nv; + + hvar1 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep1); + hvar2 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep2); + + /* Header variables are always ordered below any variables defined + * in the BCM data */ + if (hvar1 != NULL && hvar2 == NULL) { + return (1); /* hvar follows non-hvar */ + } else if (hvar1 == NULL && hvar2 != NULL) { + return (-1); /* non-hvar precedes hvar */ + } + + /* Otherwise, both cookies are either hvars or non-hvars. We can + * safely fall back on pointer order, which will provide a correct + * ordering matching the behavior of bhnd_nvram_data_next() for + * both cases */ + if (cookiep1 < cookiep2) + return (-1); + + if (cookiep1 > cookiep2) + return (1); + + return (0); +} + +static int bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, size_t *len, bhnd_nvram_type type) { return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); } +static int +bhnd_nvram_bcm_copy_val(struct bhnd_nvram_data *nv, void *cookiep, + bhnd_nvram_val **value) +{ + return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value)); +} + static const void * bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, size_t *len, bhnd_nvram_type *type) @@ -683,6 +723,35 @@ return (cookiep); } +static int +bhnd_nvram_bcm_filter_setvar(struct bhnd_nvram_data *nv, const char *name, + bhnd_nvram_val *value, bhnd_nvram_val **result) +{ + bhnd_nvram_val *str; + int error; + + /* Name (trimmed of any path prefix) must be valid */ + if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name))) + return (EINVAL); + + /* Value must be bcm-formatted string */ + error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt, + value, BHND_NVRAM_VAL_DYNAMIC); + if (error) + return (error); + + /* Success. Transfer result ownership to the caller. */ + *result = str; + return (0); +} + +static int +bhnd_nvram_bcm_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name) +{ + /* We permit deletion of any variable */ + return (0); +} + /** * Return the internal BCM data reference for a header-defined variable * with @p name, or NULL if none exists. Index: sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c +++ sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c @@ -351,12 +351,32 @@ } static int +bhnd_nvram_bcmraw_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1, + void *cookiep2) +{ + if (cookiep1 < cookiep2) + return (-1); + + if (cookiep1 > cookiep2) + return (1); + + return (0); +} + +static int bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, size_t *len, bhnd_nvram_type type) { return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); } +static int +bhnd_nvram_bcmraw_copy_val(struct bhnd_nvram_data *nv, void *cookiep, + bhnd_nvram_val **value) +{ + return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value)); +} + static const void * bhnd_nvram_bcmraw_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, size_t *len, bhnd_nvram_type *type) @@ -378,3 +398,32 @@ /* Cookie points to key\0value\0 */ return (cookiep); } + +static int +bhnd_nvram_bcmraw_filter_setvar(struct bhnd_nvram_data *nv, const char *name, + bhnd_nvram_val *value, bhnd_nvram_val **result) +{ + bhnd_nvram_val *str; + int error; + + /* Name (trimmed of any path prefix) must be valid */ + if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name))) + return (EINVAL); + + /* Value must be bcm-formatted string */ + error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt, + value, BHND_NVRAM_VAL_DYNAMIC); + if (error) + return (error); + + /* Success. Transfer result ownership to the caller. */ + *result = str; + return (0); +} + +static int +bhnd_nvram_bcmraw_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name) +{ + /* We permit deletion of any variable */ + return (0); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c +++ sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c @@ -77,8 +77,10 @@ char btxt[8]; }; -static size_t bhnd_nvram_btxt_io_offset(struct bhnd_nvram_btxt *btxt, - void *cookiep); +static void *bhnd_nvram_btxt_offset_to_cookiep(struct bhnd_nvram_btxt *btxt, + size_t io_offset); +static size_t bhnd_nvram_btxt_cookiep_to_offset(struct bhnd_nvram_btxt *btxt, + void *cookiep); static int bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io, size_t offset, size_t *line_len, size_t *env_len); @@ -322,35 +324,40 @@ btxt = (struct bhnd_nvram_btxt *)nv; io_size = bhnd_nvram_io_getsize(btxt->data); - io_offset = bhnd_nvram_btxt_io_offset(btxt, *cookiep); + + if (*cookiep == NULL) { + /* Start search at initial file offset */ + io_offset = 0x0; + } else { + /* Start search after the current entry */ + io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, *cookiep); + + /* Scan past the current entry by finding the next newline */ + error = bhnd_nvram_btxt_seek_eol(btxt->data, &io_offset); + if (error) { + BHND_NV_LOG("unexpected error in seek_eol(): %d\n", + error); + return (NULL); + } + } /* Already at EOF? */ if (io_offset == io_size) return (NULL); - /* Seek to the next entry (if any) */ - if ((error = bhnd_nvram_btxt_seek_eol(btxt->data, &io_offset))) { - BHND_NV_LOG("unexpected error in seek_eol(): %d\n", error); - return (NULL); - } - + /* Seek to the first valid entry, or EOF */ if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset))) { BHND_NV_LOG("unexpected error in seek_next(): %d\n", error); return (NULL); } - /* Provide the new cookie for this offset */ - if (io_offset > UINTPTR_MAX) { - BHND_NV_LOG("io_offset > UINPTR_MAX!\n"); - return (NULL); - } - - *cookiep = (void *)(uintptr_t)io_offset; - /* Hit EOF? */ if (io_offset == io_size) return (NULL); + /* Provide the new cookie for this offset */ + *cookiep = bhnd_nvram_btxt_offset_to_cookiep(btxt, io_offset); + /* Fetch the name pointer; it must be at least 1 byte long */ error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &nptr, 1, NULL); if (error) { @@ -363,12 +370,32 @@ } static int +bhnd_nvram_btxt_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1, + void *cookiep2) +{ + if (cookiep1 < cookiep2) + return (-1); + + if (cookiep1 > cookiep2) + return (1); + + return (0); +} + +static int bhnd_nvram_btxt_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, size_t *len, bhnd_nvram_type type) { return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); } +static int +bhnd_nvram_btxt_copy_val(struct bhnd_nvram_data *nv, void *cookiep, + bhnd_nvram_val **value) +{ + return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value)); +} + const void * bhnd_nvram_btxt_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, size_t *len, bhnd_nvram_type *type) @@ -383,7 +410,7 @@ btxt = (struct bhnd_nvram_btxt *)nv; io_size = bhnd_nvram_io_getsize(btxt->data); - io_offset = bhnd_nvram_btxt_io_offset(btxt, cookiep); + io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, cookiep); /* At EOF? */ if (io_offset == io_size) @@ -429,7 +456,7 @@ btxt = (struct bhnd_nvram_btxt *)nv; io_size = bhnd_nvram_io_getsize(btxt->data); - io_offset = bhnd_nvram_btxt_io_offset(btxt, cookiep); + io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, cookiep); /* At EOF? */ if (io_offset == io_size) @@ -444,20 +471,51 @@ return (ptr); } -/* Convert cookie back to an I/O offset */ +/** + * Return a cookiep for the given I/O offset. + */ +static void * +bhnd_nvram_btxt_offset_to_cookiep(struct bhnd_nvram_btxt *btxt, + size_t io_offset) +{ + const void *ptr; + int error; + + BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(btxt->data), + ("io_offset %zu out-of-range", io_offset)); + BHND_NV_ASSERT(io_offset < UINTPTR_MAX, + ("io_offset %#zx exceeds UINTPTR_MAX", io_offset)); + + error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_offset, NULL); + if (error) + BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error); + + ptr = (const uint8_t *)ptr + io_offset; + return (__DECONST(void *, ptr)); +} + +/* Convert a cookiep back to an I/O offset */ static size_t -bhnd_nvram_btxt_io_offset(struct bhnd_nvram_btxt *btxt, void *cookiep) +bhnd_nvram_btxt_cookiep_to_offset(struct bhnd_nvram_btxt *btxt, void *cookiep) { - size_t io_size; - uintptr_t cval; + const void *ptr; + intptr_t offset; + size_t io_size; + int error; + + BHND_NV_ASSERT(cookiep != NULL, ("null cookiep")); io_size = bhnd_nvram_io_getsize(btxt->data); - cval = (uintptr_t)cookiep; + error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_size, NULL); + if (error) + BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error); - BHND_NV_ASSERT(cval < SIZE_MAX, ("cookie > SIZE_MAX)")); - BHND_NV_ASSERT(cval <= io_size, ("cookie > io_size)")); + offset = (const uint8_t *)cookiep - (const uint8_t *)ptr; + BHND_NV_ASSERT(offset >= 0, ("invalid cookiep")); + BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)")); + BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)")); - return ((size_t)cval); + return ((size_t)offset); } /* Determine the entry length and env 'key=value' string length of the entry @@ -584,3 +642,50 @@ *offset += (p - baseptr); return (0); } + +static int +bhnd_nvram_btxt_filter_setvar(struct bhnd_nvram_data *nv, const char *name, + bhnd_nvram_val *value, bhnd_nvram_val **result) +{ + bhnd_nvram_val *str; + const char *inp; + bhnd_nvram_type itype; + size_t ilen; + int error; + + /* Name (trimmed of any path prefix) must be valid */ + if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name))) + return (EINVAL); + + /* Value must be bcm-formatted string */ + error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt, + value, BHND_NVRAM_VAL_DYNAMIC); + if (error) + return (error); + + /* Value string must not contain our record delimiter character ('\n'), + * or our comment character ('#') */ + inp = bhnd_nvram_val_bytes(str, &ilen, &itype); + BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_STRING, ("non-string value")); + for (size_t i = 0; i < ilen; i++) { + switch (inp[i]) { + case '\n': + case '#': + BHND_NV_LOG("invalid character (%#hhx) in value\n", + inp[i]); + bhnd_nvram_val_release(str); + return (EINVAL); + } + } + + /* Success. Transfer result ownership to the caller. */ + *result = str; + return (0); +} + +static int +bhnd_nvram_btxt_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name) +{ + /* We permit deletion of any variable */ + return (0); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c +++ sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c @@ -666,25 +666,34 @@ return (0); } +/** + * Common variable decoding; fetches and decodes variable to @p val, + * using @p storage for actual data storage. + * + * The returned @p val instance will hold a borrowed reference to @p storage, + * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond + * the lifetime of @p storage. + * + * The caller is responsible for releasing any allocated value state + * via bhnd_nvram_val_release(). + */ static int -bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, - size_t *len, bhnd_nvram_type otype) +bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep, + union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val) { - bhnd_nvram_val val; struct bhnd_nvram_sprom *sp; - struct sprom_opcode_idx *idx; + struct sprom_opcode_idx *entry; const struct bhnd_nvram_vardefn *var; - union bhnd_nvram_sprom_storage storage; union bhnd_nvram_sprom_storage *inp; - union bhnd_nvram_sprom_intv intv; bhnd_nvram_type var_btype; + union bhnd_nvram_sprom_intv intv; size_t ilen, ipos, iwidth; size_t nelem; bool all_bits_set; int error; sp = (struct bhnd_nvram_sprom *)nv; - idx = cookiep; + entry = cookiep; BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep")); @@ -699,7 +708,7 @@ * canonical NVRAM variable definition, but some SPROM layouts may * define a smaller element count. */ - if ((error = sprom_opcode_parse_var(&sp->state, idx))) { + if ((error = sprom_opcode_parse_var(&sp->state, entry))) { BHND_NV_LOG("variable evaluation failed: %d\n", error); return (error); } @@ -724,9 +733,9 @@ } ilen = nelem * iwidth; - /* Decode into our own local storage. */ - inp = &storage; - if (ilen > sizeof(storage)) { + /* Decode into our caller's local storage */ + inp = storage; + if (ilen > sizeof(*storage)) { BHND_NV_LOG("error decoding '%s', SPROM_ARRAY_MAXLEN " "incorrect\n", var->name); return (EFTYPE); @@ -739,7 +748,7 @@ /* * Decode the SPROM data, iteratively decoding up to nelem values. */ - if ((error = sprom_opcode_state_seek(&sp->state, idx))) { + if ((error = sprom_opcode_state_seek(&sp->state, entry))) { BHND_NV_LOG("variable seek failed: %d\n", error); return (error); } @@ -840,8 +849,9 @@ /* Perform coercion of the array element */ nbyte = iwidth; - error = bhnd_nvram_value_coerce(&intv, sizeof(intv), - intv_type, ptr, &nbyte, var_btype); + error = bhnd_nvram_value_coerce(&intv.u32, + sizeof(intv.u32), intv_type, ptr, &nbyte, + var_btype); if (error) return (error); @@ -871,13 +881,45 @@ if ((var->flags & BHND_NVRAM_VF_IGNALL1) && all_bits_set) return (ENOENT); + /* Provide value wrapper */ + return (bhnd_nvram_val_init(val, var->fmt, inp, ilen, var->type, + BHND_NVRAM_VAL_BORROW_DATA)); + return (error); +} + +static int +bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1, + void *cookiep2) +{ + struct sprom_opcode_idx_entry *e1, *e2; + + e1 = cookiep1; + e2 = cookiep2; + + /* Use the index entry order; this matches the order of variables + * returned via bhnd_nvram_sprom_next() */ + if (e1 < e2) + return (-1); + else if (e1 > e2) + return (1); + + return (0); +} + +static int +bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, + size_t *len, bhnd_nvram_type otype) +{ + bhnd_nvram_val val; + union bhnd_nvram_sprom_storage storage; + int error; - /* Perform value coercion from our local representation */ - error = bhnd_nvram_val_init(&val, var->fmt, inp, ilen, var->type, - BHND_NVRAM_VAL_BORROW_DATA); + /* Decode variable to a new value instance */ + error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val); if (error) return (error); + /* Perform value coercion */ error = bhnd_nvram_val_encode(&val, buf, len, otype); /* Clean up */ @@ -885,6 +927,29 @@ return (error); } +static int +bhnd_nvram_sprom_copy_val(struct bhnd_nvram_data *nv, void *cookiep, + bhnd_nvram_val **value) +{ + bhnd_nvram_val val; + union bhnd_nvram_sprom_storage storage; + int error; + + /* Decode variable to a new value instance */ + error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val); + if (error) + return (error); + + /* Attempt to copy to heap */ + *value = bhnd_nvram_val_copy(&val); + bhnd_nvram_val_release(&val); + + if (*value == NULL) + return (ENOMEM); + + return (0); +} + static const void * bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, size_t *len, bhnd_nvram_type *type) @@ -906,6 +971,21 @@ return (var->name); } +static int +bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data *nv, const char *name, + bhnd_nvram_val *value, bhnd_nvram_val **result) +{ + // XXX TODO + return (ENXIO); +} + +static int +bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name) +{ + // XXX TODO + return (ENXIO); +} + /** * Initialize SPROM opcode evaluation state. * Index: sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c +++ sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c @@ -82,6 +82,10 @@ (((_env)->hdr.size < sizeof((_env)->flags)) ? 0 : \ ((_env)->hdr.size - sizeof((_env)->flags))) +/* Maximum supported length of the envp data field, in bytes */ +#define NVRAM_TLV_ENVP_DATA_MAX_LEN \ + (UINT8_MAX - sizeof(uint8_t) /* flags */) + static int bhnd_nvram_tlv_parse_size( struct bhnd_nvram_io *io, @@ -368,12 +372,32 @@ } static int +bhnd_nvram_tlv_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1, + void *cookiep2) +{ + if (cookiep1 < cookiep2) + return (-1); + + if (cookiep1 > cookiep2) + return (1); + + return (0); +} + +static int bhnd_nvram_tlv_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, size_t *len, bhnd_nvram_type type) { return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); } +static int +bhnd_nvram_tlv_copy_val(struct bhnd_nvram_data *nv, void *cookiep, + bhnd_nvram_val **value) +{ + return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value)); +} + static const void * bhnd_nvram_tlv_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, size_t *len, bhnd_nvram_type *type) @@ -417,6 +441,61 @@ return (&env->envp[0]); } +static int +bhnd_nvram_tlv_filter_setvar(struct bhnd_nvram_data *nv, const char *name, + bhnd_nvram_val *value, bhnd_nvram_val **result) +{ + bhnd_nvram_val *str; + const char *inp; + bhnd_nvram_type itype; + size_t ilen; + size_t name_len, tlv_nremain; + int error; + + tlv_nremain = NVRAM_TLV_ENVP_DATA_MAX_LEN; + + /* Name (trimmed of any path prefix) must be valid */ + if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name))) + return (EINVAL); + + /* 'name=' must fit within the maximum TLV_ENV record length */ + name_len = strlen(name) + 1; /* '=' */ + if (tlv_nremain < name_len) { + BHND_NV_LOG("'%s=' exceeds maximum TLV_ENV record length\n", + name); + return (EINVAL); + } + tlv_nremain -= name_len; + + /* Convert value to a (bcm-formatted) string */ + error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt, + value, BHND_NVRAM_VAL_DYNAMIC); + if (error) + return (error); + + /* The string value must fit within remaining TLV_ENV record length */ + inp = bhnd_nvram_val_bytes(str, &ilen, &itype); + if (tlv_nremain < ilen) { + BHND_NV_LOG("'%.*s\\0' exceeds maximum TLV_ENV record length\n", + BHND_NV_PRINT_WIDTH(ilen), inp); + + bhnd_nvram_val_release(str); + return (EINVAL); + } + tlv_nremain -= name_len; + + /* Success. Transfer result ownership to the caller. */ + *result = str; + return (0); +} + +static int +bhnd_nvram_tlv_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name) +{ + /* We permit deletion of any variable */ + return (0); +} + /** * Iterate over the records starting at @p next, returning the parsed * record's @p tag, @p size, and @p offset. @@ -637,26 +716,43 @@ static void * bhnd_nvram_tlv_to_cookie(struct bhnd_nvram_tlv *tlv, size_t io_offset) { + const void *ptr; + int error; + BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(tlv->data), ("io_offset %zu out-of-range", io_offset)); BHND_NV_ASSERT(io_offset < UINTPTR_MAX, ("io_offset %#zx exceeds UINTPTR_MAX", io_offset)); - return ((void *)(uintptr_t)(io_offset)); + error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_offset, NULL); + if (error) + BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error); + + ptr = (const uint8_t *)ptr + io_offset; + return (__DECONST(void *, ptr)); } /* Convert a cookiep back to an I/O offset */ static size_t bhnd_nvram_tlv_to_offset(struct bhnd_nvram_tlv *tlv, void *cookiep) { - size_t io_size; - uintptr_t cval; + const void *ptr; + intptr_t offset; + size_t io_size; + int error; + + BHND_NV_ASSERT(cookiep != NULL, ("null cookiep")); io_size = bhnd_nvram_io_getsize(tlv->data); - cval = (uintptr_t)cookiep; - BHND_NV_ASSERT(cval < SIZE_MAX, ("cookie > SIZE_MAX)")); - BHND_NV_ASSERT(cval <= io_size, ("cookie > io_size)")); + error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_size, NULL); + if (error) + BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error); + + offset = (const uint8_t *)cookiep - (const uint8_t *)ptr; + BHND_NV_ASSERT(offset >= 0, ("invalid cookiep")); + BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)")); + BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)")); - return ((size_t)cval); + return ((size_t)offset); } Index: sys/dev/bhnd/nvram/bhnd_nvram_datavar.h =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_datavar.h +++ sys/dev/bhnd/nvram/bhnd_nvram_datavar.h @@ -43,20 +43,21 @@ /** Registered NVRAM parser class instances. */ SET_DECLARE(bhnd_nvram_data_class_set, bhnd_nvram_data_class); -void *bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv, - const char *name); -int bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, - void *cookiep, void *outp, size_t *olen, bhnd_nvram_type otype); - -/** @see bhnd_nvram_data_class_desc() */ -typedef const char *(bhnd_nvram_data_op_class_desc)(void); +void *bhnd_nvram_data_generic_find( + struct bhnd_nvram_data *nv, const char *name); +int bhnd_nvram_data_generic_rp_getvar( + struct bhnd_nvram_data *nv, void *cookiep, + void *outp, size_t *olen, bhnd_nvram_type otype); +int bhnd_nvram_data_generic_rp_copy_val( + struct bhnd_nvram_data *nv, void *cookiep, + bhnd_nvram_val **val); /** @see bhnd_nvram_data_probe() */ typedef int (bhnd_nvram_data_op_probe)(struct bhnd_nvram_io *io); /** @see bhnd_nvram_data_new() */ typedef int (bhnd_nvram_data_op_new)(struct bhnd_nvram_data *nv, - struct bhnd_nvram_io *io); + struct bhnd_nvram_io *io); /** Free all resources associated with @p nv. Called by * bhnd_nvram_data_release() when the reference count reaches zero. */ @@ -79,25 +80,45 @@ /** @see bhnd_nvram_data_next() */ typedef const char *(bhnd_nvram_data_op_next)(struct bhnd_nvram_data *nv, - void **cookiep); + void **cookiep); /** @see bhnd_nvram_data_find() */ typedef void *(bhnd_nvram_data_op_find)(struct bhnd_nvram_data *nv, - const char *name); + const char *name); + +/** @see bhnd_nvram_data_copy_val() */ +typedef int (bhnd_nvram_data_op_copy_val)( + struct bhnd_nvram_data *nv, void *cookiep, + bhnd_nvram_val **value); + +/** @see bhnd_nvram_data_getvar_order() */ +typedef int (bhnd_nvram_data_op_getvar_order)( + struct bhnd_nvram_data *nv, void *cookiep1, + void *cookiep2); /** @see bhnd_nvram_data_getvar_name() */ typedef const char *(bhnd_nvram_data_op_getvar_name)( - struct bhnd_nvram_data *nv, void *cookiep); + struct bhnd_nvram_data *nv, + void *cookiep); /** @see bhnd_nvram_data_getvar() */ typedef int (bhnd_nvram_data_op_getvar)(struct bhnd_nvram_data *nv, - void *cookiep, void *buf, size_t *len, - bhnd_nvram_type type); + void *cookiep, void *buf, size_t *len, + bhnd_nvram_type type); /** @see bhnd_nvram_data_getvar_ptr() */ typedef const void *(bhnd_nvram_data_op_getvar_ptr)( - struct bhnd_nvram_data *nv, void *cookiep, - size_t *len, bhnd_nvram_type *type); + struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type); + +/** @see bhnd_nvram_data_filter_setvar() */ +typedef int (bhnd_nvram_data_op_filter_setvar)( + struct bhnd_nvram_data *nv, const char *name, + bhnd_nvram_val *value, bhnd_nvram_val **result); + +/** @see bhnd_nvram_data_filter_unsetvar() */ +typedef int (bhnd_nvram_data_op_filter_unsetvar)( + struct bhnd_nvram_data *nv, const char *name); /** * NVRAM data class. @@ -105,18 +126,23 @@ struct bhnd_nvram_data_class { const char *desc; /**< description */ size_t size; /**< instance size */ - bhnd_nvram_data_op_probe *op_probe; - bhnd_nvram_data_op_new *op_new; - bhnd_nvram_data_op_free *op_free; - bhnd_nvram_data_op_count *op_count; - bhnd_nvram_data_op_size *op_size; - bhnd_nvram_data_op_serialize *op_serialize; - bhnd_nvram_data_op_caps *op_caps; - bhnd_nvram_data_op_next *op_next; - bhnd_nvram_data_op_find *op_find; - bhnd_nvram_data_op_getvar *op_getvar; - bhnd_nvram_data_op_getvar_ptr *op_getvar_ptr; - bhnd_nvram_data_op_getvar_name *op_getvar_name; + + bhnd_nvram_data_op_probe *op_probe; + bhnd_nvram_data_op_new *op_new; + bhnd_nvram_data_op_free *op_free; + bhnd_nvram_data_op_count *op_count; + bhnd_nvram_data_op_size *op_size; + bhnd_nvram_data_op_serialize *op_serialize; + bhnd_nvram_data_op_caps *op_caps; + bhnd_nvram_data_op_next *op_next; + bhnd_nvram_data_op_find *op_find; + bhnd_nvram_data_op_copy_val *op_copy_val; + bhnd_nvram_data_op_getvar_order *op_getvar_order; + bhnd_nvram_data_op_getvar *op_getvar; + bhnd_nvram_data_op_getvar_ptr *op_getvar_ptr; + bhnd_nvram_data_op_getvar_name *op_getvar_name; + bhnd_nvram_data_op_filter_setvar *op_filter_setvar; + bhnd_nvram_data_op_filter_unsetvar *op_filter_unsetvar; }; /** @@ -163,9 +189,13 @@ _macro(_cname, caps) \ _macro(_cname, next) \ _macro(_cname, find) \ + _macro(_cname, copy_val) \ + _macro(_cname, getvar_order) \ _macro(_cname, getvar) \ _macro(_cname, getvar_ptr) \ - _macro(_cname, getvar_name) + _macro(_cname, getvar_name) \ + _macro(_cname, filter_setvar) \ + _macro(_cname, filter_unsetvar) /** * Define a bhnd_nvram_data_class with class name @p _n and description Index: sys/dev/bhnd/nvram/bhnd_nvram_private.h =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_private.h +++ sys/dev/bhnd/nvram/bhnd_nvram_private.h @@ -73,14 +73,43 @@ #define bhnd_nv_isxdigit(c) isxdigit(c) #define bhnd_nv_toupper(c) toupper(c) -#define bhnd_nv_malloc(size) malloc((size), M_BHND_NVRAM, M_WAITOK) +#define bhnd_nv_malloc(size) malloc((size), M_BHND_NVRAM, M_NOWAIT) #define bhnd_nv_calloc(n, size) malloc((n) * (size), M_BHND_NVRAM, \ - M_WAITOK | M_ZERO) + M_NOWAIT | M_ZERO) #define bhnd_nv_reallocf(buf, size) reallocf((buf), (size), M_BHND_NVRAM, \ - M_WAITOK) + M_NOWAIT) #define bhnd_nv_free(buf) free((buf), M_BHND_NVRAM) -#define bhnd_nv_strdup(str) strdup(str, M_BHND_NVRAM) -#define bhnd_nv_strndup(str, len) strndup(str, len, M_BHND_NVRAM) +#define bhnd_nv_asprintf(buf, fmt, ...) asprintf((buf), M_BHND_NVRAM, \ + fmt, ## __VA_ARGS__) + +/* We need our own strdup() implementation to pass required M_NOWAIT */ +static inline char * +bhnd_nv_strdup(const char *str) +{ + char *dest; + size_t len; + + len = strlen(str); + dest = malloc(len + 1, M_BHND_NVRAM, M_NOWAIT); + memcpy(dest, str, len); + dest[len] = '\0'; + + return (dest); +} + +/* We need our own strndup() implementation to pass required M_NOWAIT */ +static inline char * +bhnd_nv_strndup(const char *str, size_t len) +{ + char *dest; + + len = strnlen(str, len); + dest = malloc(len + 1, M_BHND_NVRAM, M_NOWAIT); + memcpy(dest, str, len); + dest[len] = '\0'; + + return (dest); +} #ifdef INVARIANTS #define BHND_NV_INVARIANTS @@ -121,12 +150,28 @@ #define bhnd_nv_free(buf) free((buf)) #define bhnd_nv_strdup(str) strdup(str) #define bhnd_nv_strndup(str, len) strndup(str, len) +#define bhnd_nv_asprintf(buf, fmt, ...) asprintf((buf), fmt, ## __VA_ARGS__) #ifndef NDEBUG #define BHND_NV_INVARIANTS #endif -#define BHND_NV_ASSERT(expr, ...) assert(expr) +#ifdef BHND_NV_INVARIANTS + +#define BHND_NV_ASSERT(expr, msg) do { \ + if (!(expr)) { \ + fprintf(stderr, "Assertion failed: %s, function %s, " \ + "file %s, line %u\n", __STRING(expr), __FUNCTION__, \ + __FILE__, __LINE__); \ + BHND_NV_PANIC msg; \ + } \ +} while(0) + +#else /* !BHND_NV_INVARIANTS */ + +#define BHND_NV_ASSERT(expr, msg) + +#endif /* BHND_NV_INVARIANTS */ #define BHND_NV_VERBOSE (0) #define BHND_NV_PANIC(fmt, ...) do { \ @@ -211,8 +256,9 @@ size_t bhnd_nvram_trim_field(const char **inp, size_t ilen, char delim); -bool bhnd_nvram_validate_name(const char *name, - size_t name_len); +const char *bhnd_nvram_trim_path_name(const char *name); + +bool bhnd_nvram_validate_name(const char *name); /** * Calculate CRC-8 over @p buf using the Broadcom SPROM/NVRAM CRC-8 Index: sys/dev/bhnd/nvram/bhnd_nvram_store.h =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_store.h +++ sys/dev/bhnd/nvram/bhnd_nvram_store.h @@ -35,12 +35,8 @@ #ifdef _KERNEL #include #include -#include #else /* !_KERNEL */ #include - -#include - #include #include #endif @@ -49,6 +45,7 @@ #include "bhnd_nvram_data.h" #include "bhnd_nvram_io.h" +#include "bhnd_nvram_value.h" struct bhnd_nvram_store; @@ -60,9 +57,15 @@ void bhnd_nvram_store_free(struct bhnd_nvram_store *store); + int bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name, - void *buf, size_t *len, bhnd_nvram_type type); + void *outp, size_t *olen, bhnd_nvram_type otype); int bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name, - const void *buf, size_t len, bhnd_nvram_type type); + const void *inp, size_t ilen, bhnd_nvram_type itype); +int bhnd_nvram_store_unsetvar(struct bhnd_nvram_store *sc, + const char *name); + +int bhnd_nvram_store_setval(struct bhnd_nvram_store *sc, const char *name, + bhnd_nvram_val *value); #endif /* _BHND_NVRAM_BHND_NVRAM_STORE_H_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_store.c =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_store.c +++ sys/dev/bhnd/nvram/bhnd_nvram_store.c @@ -30,16 +30,22 @@ #include __FBSDID("$FreeBSD$"); +#include +#include #include #ifdef _KERNEL -#include +#include #include +#include + #else /* !_KERNEL */ +#include #include +#include #include #include #include @@ -59,11 +65,11 @@ * Manages in-memory and persistent representations of NVRAM data. */ -static int bhnd_nvram_sort_idx(void *ctx, const void *lhs, - const void *rhs); -static int bhnd_nvram_generate_index(struct bhnd_nvram_store *sc); -static void *bhnd_nvram_index_lookup(struct bhnd_nvram_store *sc, - const char *name); +static int bhnd_nvstore_parse_data( + struct bhnd_nvram_store *sc); + +static int bhnd_nvstore_parse_path_entries( + struct bhnd_nvram_store *sc); /** * Allocate and initialize a new NVRAM data store instance. @@ -92,28 +98,45 @@ if (sc == NULL) return (ENOMEM); - LIST_INIT(&sc->paths); + BHND_NVSTORE_LOCK_INIT(sc); + BHND_NVSTORE_LOCK(sc); + + /* Initialize path hash table */ + sc->num_paths = 0; + for (size_t i = 0; i < nitems(sc->paths); i++) + LIST_INIT(&sc->paths[i]); + + /* Initialize alias hash table */ + sc->num_aliases = 0; + for (size_t i = 0; i < nitems(sc->aliases); i++) + LIST_INIT(&sc->aliases[i]); /* Retain the NVRAM data */ - sc->nv = bhnd_nvram_data_retain(data); + sc->data = bhnd_nvram_data_retain(data); + sc->data_caps = bhnd_nvram_data_caps(data); - /* Allocate uncommitted change list */ - sc->pending = nvlist_create(NV_FLAG_IGNORE_CASE); - if (sc->pending == NULL) { - error = ENOMEM; + /* Register required root path */ + error = bhnd_nvstore_register_path(sc, BHND_NVSTORE_ROOT_PATH, + BHND_NVSTORE_ROOT_PATH_LEN); + if (error) goto cleanup; - } - /* Generate all indices */ - if ((error = bhnd_nvram_generate_index(sc))) - goto cleanup; + sc->root_path = bhnd_nvstore_get_path(sc, BHND_NVSTORE_ROOT_PATH, + BHND_NVSTORE_ROOT_PATH_LEN); + BHND_NV_ASSERT(sc->root_path, ("missing root path")); - BHND_NVSTORE_LOCK_INIT(sc); + /* Parse all variables vended by our backing NVRAM data instance, + * generating all path entries, alias entries, and variable indexes */ + if ((error = bhnd_nvstore_parse_data(sc))) + goto cleanup; *store = sc; + + BHND_NVSTORE_UNLOCK(sc); return (0); cleanup: + BHND_NVSTORE_UNLOCK(sc); bhnd_nvram_store_free(sc); return (error); } @@ -166,411 +189,399 @@ void bhnd_nvram_store_free(struct bhnd_nvram_store *sc) { - struct bhnd_nvstore_path *dpath, *dnext; - - LIST_FOREACH_SAFE(dpath, &sc->paths, dp_link, dnext) { - bhnd_nv_free(dpath->path); - bhnd_nv_free(dpath); - } + + /* Clean up alias hash table */ + for (size_t i = 0; i < nitems(sc->aliases); i++) { + bhnd_nvstore_alias *alias, *anext; + LIST_FOREACH_SAFE(alias, &sc->aliases[i], na_link, anext) + bhnd_nv_free(alias); + } - if (sc->pending != NULL) - nvlist_destroy(sc->pending); + /* Clean up path hash table */ + for (size_t i = 0; i < nitems(sc->paths); i++) { + bhnd_nvstore_path *path, *pnext; + LIST_FOREACH_SAFE(path, &sc->paths[i], np_link, pnext) + bhnd_nvstore_path_free(path); + } - if (sc->idx != NULL) - bhnd_nv_free(sc->idx); + if (sc->data != NULL) + bhnd_nvram_data_release(sc->data); - if (sc->nv != NULL) - bhnd_nvram_data_release(sc->nv); BHND_NVSTORE_LOCK_DESTROY(sc); bhnd_nv_free(sc); } /** - * 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. + * Parse all variables vended by our backing NVRAM data instance, + * generating all path entries, alias entries, and variable indexes. + * + * @param sc The NVRAM store instance to be initialized with + * paths, aliases, and data parsed from its backing + * data. * - * @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_store_getvar(struct bhnd_nvram_store *sc, const char *name, - void *buf, size_t *len, bhnd_nvram_type type) + * @retval 0 success + * @retval non-zero if an error occurs during parsing, a regular unix error + * code will be returned. + */ +static int +bhnd_nvstore_parse_data(struct bhnd_nvram_store *sc) { + const char *name; void *cookiep; - const void *inp; - size_t ilen; - bhnd_nvram_type itype; int error; - /* - * Search order: - * - * - uncommitted changes - * - index lookup OR buffer scan - */ - - BHND_NVSTORE_LOCK(sc); - - /* Is variable marked for deletion? */ - if (nvlist_exists_null(sc->pending, name)) { - BHND_NVSTORE_UNLOCK(sc); - return (ENOENT); - } - - /* Does an uncommitted value exist? */ - if (nvlist_exists_string(sc->pending, name)) { - /* Uncommited value exists, is not a deletion */ - inp = nvlist_get_string(sc->pending, name); - ilen = strlen(inp) + 1; - itype = BHND_NVRAM_TYPE_STRING; + /* Parse and register all device paths and path aliases. This enables + * resolution of _forward_ references to device paths aliases when + * scanning variable entries below */ + if ((error = bhnd_nvstore_parse_path_entries(sc))) + return (error); - /* Coerce borrowed data reference before releasing - * our lock. */ - error = bhnd_nvram_value_coerce(inp, ilen, itype, buf, len, - type); + /* Calculate the per-path variable counts, and report dangling alias + * references as an error. */ + cookiep = NULL; + while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) { + bhnd_nvstore_path *path; + bhnd_nvstore_name_info info; - BHND_NVSTORE_UNLOCK(sc); + /* Parse the name info */ + error = bhnd_nvstore_parse_name_info(name, + BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info); + if (error) + return (error); - return (error); - } else if (nvlist_exists(sc->pending, name)) { - BHND_NV_PANIC("invalid value type for pending change %s", name); + switch (info.type) { + case BHND_NVSTORE_VAR: + /* Fetch referenced path */ + path = bhnd_nvstore_var_get_path(sc, &info); + if (path == NULL) { + BHND_NV_LOG("variable '%s' has dangling " + "path reference\n", name); + return (EFTYPE); + } + + /* Increment path variable count */ + if (path->num_vars == SIZE_MAX) { + BHND_NV_LOG("more than SIZE_MAX variables in " + "path %s\n", path->path_str); + return (EFTYPE); + } + path->num_vars++; + break; + + case BHND_NVSTORE_ALIAS_DECL: + /* Skip -- path alias already parsed and recorded */ + break; + } } - /* Fetch variable from parsed NVRAM data. */ - if ((cookiep = bhnd_nvram_index_lookup(sc, name)) == NULL) { - BHND_NVSTORE_UNLOCK(sc); - return (ENOENT); + /* If the backing NVRAM data instance vends only a single root ("/") + * path, we may be able to skip generating an index for the root + * path */ + if (sc->num_paths == 1) { + bhnd_nvstore_path *path; + + /* If the backing instance provides its own name-based lookup + * indexing, we can skip generating a duplicate here */ + if (sc->data_caps & BHND_NVRAM_DATA_CAP_INDEXED) + return (0); + + /* If the sole root path contains fewer variables than the + * minimum indexing threshhold, we do not need to generate an + * index */ + path = bhnd_nvstore_get_root_path(sc); + if (path->num_vars < BHND_NV_IDX_VAR_THRESHOLD) + return (0); } - /* Let the parser itself perform value coercion */ - error = bhnd_nvram_data_getvar(sc->nv, cookiep, buf, len, type); - BHND_NVSTORE_UNLOCK(sc); + /* Allocate per-path index instances */ + for (size_t i = 0; i < nitems(sc->paths); i++) { + bhnd_nvstore_path *path; - return (error); -} + LIST_FOREACH(path, &sc->paths[i], np_link) { + path->index = bhnd_nvstore_index_new(path->num_vars); + if (path->index == NULL) + return (ENOMEM); + } + } -/** - * 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_store_setvar(struct bhnd_nvram_store *sc, const char *name, - const void *buf, size_t len, bhnd_nvram_type type) -{ - const char *inp; - char vbuf[512]; + /* Populate per-path indexes */ + cookiep = NULL; + while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) { + bhnd_nvstore_name_info info; + bhnd_nvstore_path *path; - /* Verify name validity */ - if (!bhnd_nvram_validate_name(name, strlen(name))) - return (EINVAL); + /* Parse the name info */ + error = bhnd_nvstore_parse_name_info(name, + BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info); + if (error) + return (error); - /* 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_value_size(buf, len, type, 1) != 0) - return (EINVAL); + switch (info.type) { + case BHND_NVSTORE_VAR: + /* Fetch referenced path */ + path = bhnd_nvstore_var_get_path(sc, &info); + BHND_NV_ASSERT(path != NULL, + ("dangling path reference")); + + /* Append to index */ + error = bhnd_nvstore_index_append(sc, path->index, + cookiep); + if (error) + return (error); + break; + + case BHND_NVSTORE_ALIAS_DECL: + /* Skip */ + break; + } + } - /* 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_UINT64: - case BHND_NVRAM_TYPE_INT8: - case BHND_NVRAM_TYPE_INT16: - case BHND_NVRAM_TYPE_INT32: - case BHND_NVRAM_TYPE_INT64: - case BHND_NVRAM_TYPE_NULL: - case BHND_NVRAM_TYPE_DATA: - case BHND_NVRAM_TYPE_BOOL: - case BHND_NVRAM_TYPE_UINT8_ARRAY: - case BHND_NVRAM_TYPE_UINT16_ARRAY: - case BHND_NVRAM_TYPE_UINT32_ARRAY: - case BHND_NVRAM_TYPE_UINT64_ARRAY: - case BHND_NVRAM_TYPE_INT8_ARRAY: - case BHND_NVRAM_TYPE_INT16_ARRAY: - case BHND_NVRAM_TYPE_INT32_ARRAY: - case BHND_NVRAM_TYPE_INT64_ARRAY: - case BHND_NVRAM_TYPE_CHAR_ARRAY: - case BHND_NVRAM_TYPE_STRING_ARRAY: - case BHND_NVRAM_TYPE_BOOL_ARRAY: - // TODO: non-char/string value support - return (EOPNOTSUPP); - - case BHND_NVRAM_TYPE_CHAR: - case BHND_NVRAM_TYPE_STRING: - inp = buf; - - /* Must not exceed buffer size */ - if (len > sizeof(vbuf)) - return (EINVAL); - - /* Must have room for a trailing NUL */ - if (len == sizeof(vbuf) && inp[len-1] != '\0') - return (EINVAL); - - /* Copy out the string value and append trailing NUL */ - strlcpy(vbuf, buf, len); - - /* Add to pending change list */ - BHND_NVSTORE_LOCK(sc); - nvlist_add_string(sc->pending, name, vbuf); - BHND_NVSTORE_UNLOCK(sc); + /* Prepare indexes for querying */ + for (size_t i = 0; i < nitems(sc->paths); i++) { + bhnd_nvstore_path *path; + + LIST_FOREACH(path, &sc->paths[i], np_link) { + error = bhnd_nvstore_index_prepare(sc, path->index); + if (error) + return (error); + } } return (0); } -/* sort function for bhnd_nvstore_index cookie values */ -static int -bhnd_nvram_sort_idx(void *ctx, const void *lhs, const void *rhs) -{ - struct bhnd_nvram_store *sc; - const char *l_str, *r_str; - - sc = ctx; - - /* Fetch string pointers from the cookiep values */ - l_str = bhnd_nvram_data_getvar_name(sc->nv, *(void * const *)lhs); - r_str = bhnd_nvram_data_getvar_name(sc->nv, *(void * const *)rhs); - - /* Perform comparison */ - return (strcasecmp(l_str, r_str)); -} /** - * Parse and register all device paths and path aliases in @p nvram. + * Parse and register path and path alias entries for all declarations found in + * the NVRAM data backing @p nvram. * - * @param sc The NVRAM parser state. + * @param sc The NVRAM store instance. * * @retval 0 success - * @retval non-zero If registering device paths fails, a regular unix - * error code will be returned. + * @retval non-zero If parsing fails, a regular unix error code will be + * returned. */ static int -bhnd_nvram_register_devpaths(struct bhnd_nvram_store *sc) +bhnd_nvstore_parse_path_entries(struct bhnd_nvram_store *sc) { const char *name; void *cookiep; int error; - /* Skip if backing parser does not support device paths */ - if (!(bhnd_nvram_data_caps(sc->nv) & BHND_NVRAM_DATA_CAP_DEVPATHS)) + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + /* Skip path registration if the data source does not support device + * paths. */ + if (!(sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)) { + BHND_NV_ASSERT(sc->root_path != NULL, ("missing root path")); return (0); + } - /* Parse and register all device path aliases */ + /* Otherwise, parse and register all paths and path aliases */ cookiep = NULL; - while ((name = bhnd_nvram_data_next(sc->nv, &cookiep))) { - struct bhnd_nvstore_path *devpath; - const char *suffix; - char *eptr; - char *path; - size_t path_len; - u_long index; - - path = NULL; - - /* Check for devpath prefix */ - if (strncmp(name, "devpath", strlen("devpath")) != 0) - continue; - - /* Parse index value that should follow a 'devpath' prefix */ - suffix = name + strlen("devpath"); - index = strtoul(suffix, &eptr, 10); - if (eptr == suffix || *eptr != '\0') { - BHND_NV_LOG("invalid devpath variable '%s'\n", name); - continue; - } + while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) { + bhnd_nvstore_name_info info; - /* Determine path value length */ - error = bhnd_nvram_data_getvar(sc->nv, cookiep, NULL, &path_len, - BHND_NVRAM_TYPE_STRING); + /* Parse the name info */ + error = bhnd_nvstore_parse_name_info(name, + BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info); if (error) return (error); - /* Allocate path buffer */ - if ((path = bhnd_nv_malloc(path_len)) == NULL) - return (ENOMEM); - - /* Decode to our new buffer */ - error = bhnd_nvram_data_getvar(sc->nv, cookiep, path, &path_len, - BHND_NVRAM_TYPE_STRING); + /* Register the path */ + error = bhnd_nvstore_var_register_path(sc, &info, cookiep); if (error) { - bhnd_nv_free(path); + BHND_NV_LOG("failed to register path for %s: %d\n", + name, error); return (error); } + } - /* Register path alias */ - devpath = bhnd_nv_malloc(sizeof(*devpath)); - if (devpath == NULL) { - bhnd_nv_free(path); - return (ENOMEM); + return (0); +} +/** + * Read an NVRAM variable. + * + * @param sc The NVRAM parser state. + * @param name The NVRAM variable name. + * @param[out] outp 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] olen The capacity of @p outp. On success, will be set + * to the actual size of the requested value. + * @param otype The requested data type to be written to + * @p outp. + * + * @retval 0 success + * @retval ENOENT The requested variable was not found. + * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen 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_store_getvar(struct bhnd_nvram_store *sc, const char *name, + void *outp, size_t *olen, bhnd_nvram_type otype) +{ + bhnd_nvstore_name_info info; + bhnd_nvstore_path *path; + bhnd_nvram_prop *prop; + void *cookiep; + int error; + + BHND_NVSTORE_LOCK(sc); + + /* Parse the variable name */ + error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL, + sc->data_caps, &info); + if (error) + goto finished; + + /* Fetch the variable's enclosing path entry */ + if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL) { + error = ENOENT; + goto finished; + } + + /* Search uncommitted updates first */ + prop = bhnd_nvstore_path_get_update(sc, path, info.name); + if (prop != NULL) { + if (bhnd_nvram_prop_is_null(prop)) { + /* NULL denotes a pending deletion */ + error = ENOENT; + } else { + error = bhnd_nvram_prop_encode(prop, outp, olen, otype); } + goto finished; + } - devpath->index = index; - devpath->path = path; - LIST_INSERT_HEAD(&sc->paths, devpath, dp_link); + /* Search the backing NVRAM data */ + cookiep = bhnd_nvstore_path_data_lookup(sc, path, info.name); + if (cookiep != NULL) { + /* Found in backing store */ + error = bhnd_nvram_data_getvar(sc->data, cookiep, outp, olen, + otype); + goto finished; } - return (0); + /* Not found */ + error = ENOENT; + +finished: + BHND_NVSTORE_UNLOCK(sc); + return (error); } /** - * Generate all indices for the NVRAM data backing @p nvram. + * Common bhnd_nvram_store_set*() and bhnd_nvram_store_unsetvar() + * implementation. * - * @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. + * If @p value is NULL, the variable will be marked for deletion. */ static int -bhnd_nvram_generate_index(struct bhnd_nvram_store *sc) +bhnd_nvram_store_setval_common(struct bhnd_nvram_store *sc, const char *name, + bhnd_nvram_val *value) { - const char *name; - void *cookiep; - size_t idx_bytes; - size_t num_vars; - int error; + bhnd_nvstore_path *path; + bhnd_nvstore_name_info info; + int error; - /* Parse and register all device path aliases */ - if ((error = bhnd_nvram_register_devpaths(sc))) + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + /* Parse the variable name */ + error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL, + sc->data_caps, &info); + if (error) return (error); - /* Skip generating a variable index if threshold is not met ... */ - num_vars = bhnd_nvram_data_count(sc->nv); - if (num_vars < NVRAM_IDX_VAR_THRESH) - return (0); + /* Fetch the variable's enclosing path entry */ + if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL) + return (error); - /* ... or if the backing data instance implements indexed lookup - * internally */ - if (bhnd_nvram_data_caps(sc->nv) & BHND_NVRAM_DATA_CAP_INDEXED) - return (0); + /* Register the update entry */ + return (bhnd_nvstore_path_register_update(sc, path, info.name, value)); +} - /* Allocate and populate variable index */ - idx_bytes = sizeof(struct bhnd_nvstore_index) + - (sizeof(void *) * num_vars); - sc->idx = bhnd_nv_malloc(idx_bytes); - if (sc->idx == NULL) { - BHND_NV_LOG("error allocating %zu byte index\n", idx_bytes); - goto bad_index; - } +/** + * Set an NVRAM variable. + * + * @param sc The NVRAM parser state. + * @param name The NVRAM variable name. + * @param value The new value. + * + * @retval 0 success + * @retval ENOENT The requested variable @p name was not found. + * @retval EINVAL If @p value is invalid. + */ +int +bhnd_nvram_store_setval(struct bhnd_nvram_store *sc, const char *name, + bhnd_nvram_val *value) +{ + int error; - sc->idx->num_cookiep = num_vars; + BHND_NVSTORE_LOCK(sc); + error = bhnd_nvram_store_setval_common(sc, name, value); + BHND_NVSTORE_UNLOCK(sc); -#ifdef _KERNEL - if (bootverbose) { - BHND_NV_LOG("allocated %zu byte index for %zu variables\n", - idx_bytes, num_vars); - } -#endif /* _KERNEL */ + return (error); +} - cookiep = NULL; - for (size_t i = 0; i < sc->idx->num_cookiep; i++) { - /* Fetch next entry */ - name = bhnd_nvram_data_next(sc->nv, &cookiep); - - /* Early EOF */ - if (name == NULL) { - BHND_NV_LOG("indexing failed, expected %zu records " - "(got %zu)\n", sc->idx->num_cookiep, i+1); - goto bad_index; - } +/** + * Set an NVRAM variable. + * + * @param sc The NVRAM parser state. + * @param name The NVRAM variable name. + * @param[out] inp The new value. + * @param[in,out] ilen The size of @p inp. + * @param itype The data type of @p inp. + * + * @retval 0 success + * @retval ENOENT The requested variable @p name was not found. + * @retval EINVAL If the new value is invalid. + * @retval EINVAL If @p name is read-only. + */ +int +bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name, + const void *inp, size_t ilen, bhnd_nvram_type itype) +{ + bhnd_nvram_val val; + int error; - /* Save the variable's cookiep */ - sc->idx->cookiep[i] = cookiep; + error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype, + BHND_NVRAM_VAL_FIXED|BHND_NVRAM_VAL_BORROW_DATA); + if (error) { + BHND_NV_LOG("error initializing value: %d\n", error); + return (EINVAL); } - /* Sort the index table */ - qsort_r(sc->idx->cookiep, sc->idx->num_cookiep, - sizeof(sc->idx->cookiep[0]), sc, bhnd_nvram_sort_idx); - - return (0); + BHND_NVSTORE_LOCK(sc); + error = bhnd_nvram_store_setval_common(sc, name, &val); + BHND_NVSTORE_UNLOCK(sc); -bad_index: - /* Fall back on non-indexed access */ - BHND_NV_LOG("reverting to non-indexed variable lookup\n"); - if (sc->idx != NULL) { - bhnd_nv_free(sc->idx); - sc->idx = NULL; - } + bhnd_nvram_val_release(&val); - return (0); + return (error); } - /** - * Perform an index lookup of @p name, returning the associated cookie - * value, or NULL if the variable does not exist. + * Unset an NVRAM variable. + * + * @param sc The NVRAM parser state. + * @param name The NVRAM variable name. * - * @param sc The NVRAM parser state. - * @param name The variable to search for. + * @retval 0 success + * @retval ENOENT The requested variable @p name was not found. + * @retval EINVAL If @p name is read-only. */ -static void * -bhnd_nvram_index_lookup(struct bhnd_nvram_store *sc, const char *name) +int +bhnd_nvram_store_unsetvar(struct bhnd_nvram_store *sc, const char *name) { - void *cookiep; - const char *indexed_name; - size_t min, mid, max; - int order; + int error; - BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); - - if (sc->idx == NULL || sc->idx->num_cookiep == 0) - return (bhnd_nvram_data_find(sc->nv, name)); - - /* - * Locate the requested variable using a binary search. - */ - BHND_NV_ASSERT(sc->idx->num_cookiep > 0, - ("empty array causes underflow")); - min = 0; - max = sc->idx->num_cookiep - 1; - - while (max >= min) { - /* Select midpoint */ - mid = (min + max) / 2; - cookiep = sc->idx->cookiep[mid]; - - /* Determine which side of the partition to search */ - indexed_name = bhnd_nvram_data_getvar_name(sc->nv, cookiep); - order = strcasecmp(indexed_name, name); - - if (order < 0) { - /* Search upper partition */ - min = mid + 1; - } else if (order > 0) { - /* Search (non-empty) lower partition */ - if (mid == 0) - break; - max = mid - 1; - } else if (order == 0) { - /* Match found */ - return (cookiep); - } - } + BHND_NVSTORE_LOCK(sc); + error = bhnd_nvram_store_setval_common(sc, name, BHND_NVRAM_VAL_NULL); + BHND_NVSTORE_UNLOCK(sc); - /* Not found */ - return (NULL); + return (error); } Index: sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c =================================================================== --- /dev/null +++ sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c @@ -0,0 +1,1182 @@ +/*- + * 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 + +#ifdef _KERNEL + +#include +#include + +#include + +#else /* !_KERNEL */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" +#include "bhnd_nvram_datavar.h" + +#include "bhnd_nvram_storevar.h" + +static int bhnd_nvstore_idx_cmp(void *ctx, + const void *lhs, const void *rhs); + +/** + * Allocate and initialize a new path instance. + * + * The caller is responsible for deallocating the instance via + * bhnd_nvstore_path_free(). + * + * @param path_str The path's canonical string representation. + * @param path_len The length of @p path_str. + * + * @retval non-NULL success + * @retval NULL if allocation fails. + */ +bhnd_nvstore_path * +bhnd_nvstore_path_new(const char *path_str, size_t path_len) +{ + bhnd_nvstore_path *path; + + /* Allocate new entry */ + path = bhnd_nv_malloc(sizeof(*path)); + if (path == NULL) + return (NULL); + + path->index = NULL; + path->num_vars = 0; + + path->pending = bhnd_nvram_plist_new(); + if (path->pending == NULL) + goto failed; + + path->path_str = bhnd_nv_strndup(path_str, path_len); + if (path->path_str == NULL) + goto failed; + + return (path); + +failed: + if (path->pending != NULL) + bhnd_nvram_plist_release(path->pending); + + if (path->path_str != NULL) + bhnd_nv_free(path->path_str); + + bhnd_nv_free(path); + + return (NULL); +} + +/** + * Free an NVRAM path instance, releasing all associated resources. + */ +void +bhnd_nvstore_path_free(struct bhnd_nvstore_path *path) +{ + /* Free the per-path index */ + if (path->index != NULL) + bhnd_nvstore_index_free(path->index); + + bhnd_nvram_plist_release(path->pending); + bhnd_nv_free(path->path_str); + bhnd_nv_free(path); +} + +/** + * Allocate and initialize a new index instance with @p capacity. + * + * The caller is responsible for deallocating the instance via + * bhnd_nvstore_index_free(). + * + * @param capacity The maximum number of variables to be indexed. + * + * @retval non-NULL success + * @retval NULL if allocation fails. + */ +bhnd_nvstore_index * +bhnd_nvstore_index_new(size_t capacity) +{ + bhnd_nvstore_index *index; + size_t bytes; + + /* Allocate and populate variable index */ + bytes = sizeof(struct bhnd_nvstore_index) + (sizeof(void *) * capacity); + index = bhnd_nv_malloc(bytes); + if (index == NULL) { + BHND_NV_LOG("error allocating %zu byte index\n", bytes); + return (NULL); + } + + index->count = 0; + index->capacity = capacity; + + return (index); +} + +/** + * Free an index instance, releasing all associated resources. + * + * @param index An index instance previously allocated via + * bhnd_nvstore_index_new(). + */ +void +bhnd_nvstore_index_free(bhnd_nvstore_index *index) +{ + bhnd_nv_free(index); +} + +/** + * Append a new NVRAM variable's @p cookiep value to @p index. + * + * After one or more append requests, the index must be prepared via + * bhnd_nvstore_index_prepare() before any indexed lookups are performed. + * + * @param sc The NVRAM store from which NVRAM values will be queried. + * @param index The index to be modified. + * @param cookiep The cookiep value (as provided by the backing NVRAM + * data instance of @p sc) to be included in @p index. + * + * @retval 0 success + * @retval ENOMEM if appending an additional entry would exceed the + * capacity of @p index. + */ +int +bhnd_nvstore_index_append(struct bhnd_nvram_store *sc, + bhnd_nvstore_index *index, void *cookiep) +{ + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + if (index->count >= index->capacity) + return (ENOMEM); + + index->cookiep[index->count] = cookiep; + index->count++; + return (0); +} + +/* sort function for bhnd_nvstore_index_prepare() */ +static int +bhnd_nvstore_idx_cmp(void *ctx, const void *lhs, const void *rhs) +{ + struct bhnd_nvram_store *sc; + void *l_cookiep, *r_cookiep; + const char *l_str, *r_str; + const char *l_name, *r_name; + int order; + + sc = ctx; + l_cookiep = *(void * const *)lhs; + r_cookiep = *(void * const *)rhs; + + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + /* Fetch string pointers from the cookiep values */ + l_str = bhnd_nvram_data_getvar_name(sc->data, l_cookiep); + r_str = bhnd_nvram_data_getvar_name(sc->data, r_cookiep); + + /* Trim device path prefixes */ + if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) { + l_name = bhnd_nvram_trim_path_name(l_str); + r_name = bhnd_nvram_trim_path_name(r_str); + } else { + l_name = l_str; + r_name = r_str; + } + + /* Perform comparison */ + order = strcmp(l_name, r_name); + if (order != 0 || lhs == rhs) + return (order); + + /* If the backing data incorrectly contains variables with duplicate + * names, we need a sort order that provides stable behavior. + * + * Since Broadcom's own code varies wildly on this question, we just + * use a simple precedence rule: The first declaration of a variable + * takes precedence. */ + return (bhnd_nvram_data_getvar_order(sc->data, l_cookiep, r_cookiep)); +} + +/** + * Prepare @p index for querying via bhnd_nvstore_index_lookup(). + * + * After one or more append requests, the index must be prepared via + * bhnd_nvstore_index_prepare() before any indexed lookups are performed. + * + * @param sc The NVRAM store from which NVRAM values will be queried. + * @param index The index to be prepared. + * + * @retval 0 success + * @retval non-zero if preparing @p index otherwise fails, a regular unix + * error code will be returned. + */ +int +bhnd_nvstore_index_prepare(struct bhnd_nvram_store *sc, + bhnd_nvstore_index *index) +{ + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + /* Sort the index table */ + qsort_r(index->cookiep, index->count, sizeof(index->cookiep[0]), sc, + bhnd_nvstore_idx_cmp); + + return (0); +} + +/** + * Return a borrowed reference to the root path node. + * + * @param sc The NVRAM store. + */ +bhnd_nvstore_path * +bhnd_nvstore_get_root_path(struct bhnd_nvram_store *sc) +{ + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + return (sc->root_path); +} + +/** + * Return true if @p path is the root path node. + * + * @param sc The NVRAM store. + * @param path The path to query. + */ +bool +bhnd_nvstore_is_root_path(struct bhnd_nvram_store *sc, bhnd_nvstore_path *path) +{ + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + return (sc->root_path == path); +} + +/** + * Return the update entry matching @p name in @p path, or NULL if no entry + * found. + * + * @param sc The NVRAM store. + * @param path The path to query. + * @param name The NVRAM variable name to search for in @p path's update list. + * + * @retval non-NULL success + * @retval NULL if @p name is not found in @p path. + */ +bhnd_nvram_prop * +bhnd_nvstore_path_get_update(struct bhnd_nvram_store *sc, + bhnd_nvstore_path *path, const char *name) +{ + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + return (bhnd_nvram_plist_get_prop(path->pending, name)); +} + +/** + * Register or remove an update record for @p name in @p path. + * + * @param sc The NVRAM store. + * @param path The path to be modified. + * @param name The path-relative variable name to be modified. + * @param value The new value. A value of BHND_NVRAM_TYPE_NULL denotes deletion. + * + * @retval 0 success + * @retval ENOMEM if allocation fails. + * @retval ENOENT if @p name is unknown. + * @retval EINVAL if @p value is NULL, and deletion of @p is not + * supported. + * @retval EINVAL if @p value cannot be converted to a supported value + * type. + */ +int +bhnd_nvstore_path_register_update(struct bhnd_nvram_store *sc, + bhnd_nvstore_path *path, const char *name, bhnd_nvram_val *value) +{ + bhnd_nvram_val *prop_val; + const char *full_name; + void *cookiep; + char *namebuf; + int error; + bool nvram_committed; + + namebuf = NULL; + prop_val = NULL; + + /* Determine whether the variable is currently defined in the + * backing NVRAM data, and derive its full path-prefixed name */ + nvram_committed = false; + cookiep = bhnd_nvstore_index_lookup(sc, path->index, name); + if (cookiep != NULL) { + /* Variable is defined in the backing data */ + nvram_committed = true; + + /* Use the existing variable name */ + full_name = bhnd_nvram_data_getvar_name(sc->data, cookiep); + } else if (path == sc->root_path) { + /* No prefix required for root path */ + full_name = name; + } else { + bhnd_nvstore_alias *alias; + int len; + + /* New variable is being set; we need to determine the + * appropriate path prefix */ + alias = bhnd_nvstore_find_alias(sc, path->path_str); + if (alias != NULL) { + /* Use :name */ + len = bhnd_nv_asprintf(&namebuf, "%lu:%s", alias->alias, + name); + } else { + /* Use path/name */ + len = bhnd_nv_asprintf(&namebuf, "%s/%s", + path->path_str, name); + } + + if (len < 0) + return (ENOMEM); + + full_name = namebuf; + } + + /* Allow the data store to filter the NVRAM operation */ + if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) { + error = bhnd_nvram_data_filter_unsetvar(sc->data, full_name); + if (error) { + BHND_NV_LOG("cannot unset property %s: %d\n", full_name, + error); + goto cleanup; + } + + if ((prop_val = bhnd_nvram_val_copy(value)) == NULL) { + error = ENOMEM; + goto cleanup; + } + } else { + error = bhnd_nvram_data_filter_setvar(sc->data, full_name, + value, &prop_val); + if (error) { + BHND_NV_LOG("cannot set property %s: %d\n", full_name, + error); + goto cleanup; + } + } + + /* Add relative variable name to the per-path update list */ + if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL && + !nvram_committed) + { + /* This is a deletion request for a variable not defined in + * out backing store; we can simply remove the corresponding + * update entry. */ + bhnd_nvram_plist_remove(path->pending, name); + } else { + /* Update or append a pending update entry */ + error = bhnd_nvram_plist_replace_val(path->pending, name, + prop_val); + if (error) + goto cleanup; + } + + /* Success */ + error = 0; + +cleanup: + if (namebuf != NULL) + bhnd_nv_free(namebuf); + + if (prop_val != NULL) + bhnd_nvram_val_release(prop_val); + + return (error); +} + +/** + * Iterate over all variable cookiep values retrievable from the backing + * data store in @p path. + * + * @warning Pending updates in @p path are ignored by this function. + * + * @param sc The NVRAM store. + * @param path The NVRAM path to be iterated. + * @param[in,out] indexp A pointer to an opaque indexp value previously + * returned by bhnd_nvstore_path_data_next(), or a + * NULL value to begin iteration. + * + * @return Returns the next variable name, or NULL if there are no more + * variables defined in @p path. + */ +void * +bhnd_nvstore_path_data_next(struct bhnd_nvram_store *sc, + bhnd_nvstore_path *path, void **indexp) +{ + void **index_ref; + + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + /* No index */ + if (path->index == NULL) { + /* An index is required for all non-empty, non-root path + * instances */ + BHND_NV_ASSERT(bhnd_nvstore_is_root_path(sc, path), + ("missing index for non-root path %s", path->path_str)); + + /* Iterate NVRAM data directly, using the NVRAM data's cookiep + * value as our indexp context */ + if ((bhnd_nvram_data_next(sc->data, indexp)) == NULL) + return (NULL); + + return (*indexp); + } + + /* Empty index */ + if (path->index->count == 0) + return (NULL); + + if (*indexp == NULL) { + /* First index entry */ + index_ref = &path->index->cookiep[0]; + } else { + size_t idxpos; + + /* Advance to next index entry */ + index_ref = *indexp; + index_ref++; + + /* Hit end of index? */ + BHND_NV_ASSERT(index_ref > path->index->cookiep, + ("invalid indexp")); + + idxpos = (index_ref - path->index->cookiep); + if (idxpos >= path->index->count) + return (NULL); + } + + /* Provide new index position */ + *indexp = index_ref; + + /* Return the data's cookiep value */ + return (*index_ref); +} + +/** + * Perform an lookup of @p name in the backing NVRAM data for @p path, + * returning the associated cookiep value, or NULL if the variable is not found + * in the backing NVRAM data. + * + * @warning Pending updates in @p path are ignored by this function. + * + * @param sc The NVRAM store from which NVRAM values will be queried. + * @param path The path to be queried. + * @param name The variable name to be queried. + * + * @retval non-NULL success + * @retval NULL if @p name is not found in @p index. + */ +void * +bhnd_nvstore_path_data_lookup(struct bhnd_nvram_store *sc, + bhnd_nvstore_path *path, const char *name) +{ + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + /* No index */ + if (path->index == NULL) { + /* An index is required for all non-empty, non-root path + * instances */ + BHND_NV_ASSERT(bhnd_nvstore_is_root_path(sc, path), + ("missing index for non-root path %s", path->path_str)); + + /* Look up directly in NVRAM data */ + return (bhnd_nvram_data_find(sc->data, name)); + } + + /* Otherwise, delegate to an index-based lookup */ + return (bhnd_nvstore_index_lookup(sc, path->index, name)); +} + +/** + * Perform an index lookup of @p name, returning the associated cookiep + * value, or NULL if the variable does not exist. + * + * @param sc The NVRAM store from which NVRAM values will be queried. + * @param index The index to be queried. + * @param name The variable name to be queried. + * + * @retval non-NULL success + * @retval NULL if @p name is not found in @p index. + */ +void * +bhnd_nvstore_index_lookup(struct bhnd_nvram_store *sc, + bhnd_nvstore_index *index, const char *name) +{ + void *cookiep; + const char *indexed_name; + size_t min, mid, max; + uint32_t data_caps; + int order; + + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + BHND_NV_ASSERT(index != NULL, ("NULL index")); + + /* + * Locate the requested variable using a binary search. + */ + if (index->count == 0) + return (NULL); + + data_caps = sc->data_caps; + min = 0; + max = index->count - 1; + + while (max >= min) { + /* Select midpoint */ + mid = (min + max) / 2; + cookiep = index->cookiep[mid]; + + /* Fetch variable name */ + indexed_name = bhnd_nvram_data_getvar_name(sc->data, cookiep); + + /* Trim any path prefix */ + if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) + indexed_name = bhnd_nvram_trim_path_name(indexed_name); + + /* Determine which side of the partition to search */ + order = strcmp(indexed_name, name); + if (order < 0) { + /* Search upper partition */ + min = mid + 1; + } else if (order > 0) { + /* Search (non-empty) lower partition */ + if (mid == 0) + break; + max = mid - 1; + } else if (order == 0) { + size_t idx; + + /* + * Match found. + * + * If this happens to be a key with multiple definitions + * in the backing store, we need to find the entry with + * the highest declaration precedence. + * + * Duplicates are sorted in order of descending + * precedence; to find the highest precedence entry, + * we search backwards through the index. + */ + idx = mid; + while (idx > 0) { + void *dup_cookiep; + const char *dup_name; + + /* Fetch preceding index entry */ + idx--; + dup_cookiep = index->cookiep[idx]; + dup_name = bhnd_nvram_data_getvar_name(sc->data, + dup_cookiep); + + /* Trim any path prefix */ + if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) { + dup_name = bhnd_nvram_trim_path_name( + dup_name); + } + + /* If no match, current cookiep is the variable + * definition with the highest precedence */ + if (strcmp(indexed_name, dup_name) != 0) + return (cookiep); + + /* Otherwise, prefer this earlier definition, + * and keep searching for a higher-precedence + * definitions */ + cookiep = dup_cookiep; + } + + return (cookiep); + } + } + + /* Not found */ + return (NULL); +} + +/** + * Return the device path entry registered for @p path, if any. + * + * @param sc The NVRAM store to be queried. + * @param path The device path to search for. + * @param path_len The length of @p path. + * + * @retval non-NULL if found. + * @retval NULL if not found. + */ +bhnd_nvstore_path * +bhnd_nvstore_get_path(struct bhnd_nvram_store *sc, const char *path, + size_t path_len) +{ + bhnd_nvstore_path_list *plist; + bhnd_nvstore_path *p; + uint32_t h; + + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + /* Use hash lookup */ + h = hash32_strn(path, path_len, HASHINIT); + plist = &sc->paths[h % nitems(sc->paths)]; + + LIST_FOREACH(p, plist, np_link) { + /* Check for prefix match */ + if (strncmp(p->path_str, path, path_len) != 0) + continue; + + /* Check for complete match */ + if (strnlen(path, path_len) != strlen(p->path_str)) + continue; + + return (p); + } + + /* Not found */ + return (NULL); +} + +/** + * Resolve @p aval to its corresponding device path entry, if any. + * + * @param sc The NVRAM store to be queried. + * @param aval The device path alias value to search for. + * + * @retval non-NULL if found. + * @retval NULL if not found. + */ +bhnd_nvstore_path * +bhnd_nvstore_resolve_path_alias(struct bhnd_nvram_store *sc, u_long aval) +{ + bhnd_nvstore_alias *alias; + + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + /* Fetch alias entry */ + if ((alias = bhnd_nvstore_get_alias(sc, aval)) == NULL) + return (NULL); + + return (alias->path); +} + +/** + * Register a device path entry for the path referenced by variable name + * @p info, if any. + * + * @param sc The NVRAM store to be updated. + * @param info The NVRAM variable name info. + * @param cookiep The NVRAM variable's cookiep value. + * + * @retval 0 if the path was successfully registered, or an identical + * path or alias entry exists. + * @retval EEXIST if a conflicting entry already exists for the path or + * alias referenced by @p info. + * @retval ENOENT if @p info contains a dangling alias reference. + * @retval EINVAL if @p info contains an unsupported bhnd_nvstore_var_type + * and bhnd_nvstore_path_type combination. + * @retval ENOMEM if allocation fails. + */ +int +bhnd_nvstore_var_register_path(struct bhnd_nvram_store *sc, + bhnd_nvstore_name_info *info, void *cookiep) +{ + switch (info->type) { + case BHND_NVSTORE_VAR: + /* Variable */ + switch (info->path_type) { + case BHND_NVSTORE_PATH_STRING: + /* Variable contains a full path string + * (pci/1/1/varname); register the path */ + return (bhnd_nvstore_register_path(sc, + info->path.str.value, info->path.str.value_len)); + + case BHND_NVSTORE_PATH_ALIAS: + /* Variable contains an alias reference (0:varname). + * There's no path to register */ + return (0); + } + + BHND_NV_PANIC("unsupported path type %d", info->path_type); + break; + + case BHND_NVSTORE_ALIAS_DECL: + /* Alias declaration */ + return (bhnd_nvstore_register_alias(sc, info, cookiep)); + } + + BHND_NV_PANIC("unsupported var type %d", info->type); +} + +/** + * Resolve the device path entry referenced referenced by @p info. + * + * @param sc The NVRAM store to be updated. + * @param info Variable name information descriptor containing + * the path or path alias to be resolved. + * + * @retval non-NULL if found. + * @retval NULL if not found. + */ +bhnd_nvstore_path * +bhnd_nvstore_var_get_path(struct bhnd_nvram_store *sc, + bhnd_nvstore_name_info *info) +{ + switch (info->path_type) { + case BHND_NVSTORE_PATH_STRING: + return (bhnd_nvstore_get_path(sc, info->path.str.value, + info->path.str.value_len)); + case BHND_NVSTORE_PATH_ALIAS: + return (bhnd_nvstore_resolve_path_alias(sc, + info->path.alias.value)); + } + + BHND_NV_PANIC("unsupported path type %d", info->path_type); +} + +/** + * Return the device path alias entry registered for @p alias_val, if any. + * + * @param sc The NVRAM store to be queried. + * @param alias_val The alias value to search for. + * + * @retval non-NULL if found. + * @retval NULL if not found. + */ +bhnd_nvstore_alias * +bhnd_nvstore_get_alias(struct bhnd_nvram_store *sc, u_long alias_val) +{ + bhnd_nvstore_alias_list *alist; + bhnd_nvstore_alias *alias; + + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + /* Can use hash lookup */ + alist = &sc->aliases[alias_val % nitems(sc->aliases)]; + LIST_FOREACH(alias, alist, na_link) { + if (alias->alias == alias_val) + return (alias); + } + + /* Not found */ + return (NULL); +} + +/** + * Return the device path alias entry registered for @p path, if any. + * + * @param sc The NVRAM store to be queried. + * @param path The alias path to search for. + * + * @retval non-NULL if found. + * @retval NULL if not found. + */ +bhnd_nvstore_alias * +bhnd_nvstore_find_alias(struct bhnd_nvram_store *sc, const char *path) +{ + bhnd_nvstore_alias *alias; + + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + /* Have to scan the full table */ + for (size_t i = 0; i < nitems(sc->aliases); i++) { + LIST_FOREACH(alias, &sc->aliases[i], na_link) { + if (strcmp(alias->path->path_str, path) == 0) + return (alias); + } + } + + /* Not found */ + return (NULL); +} + +/** + * Register a device path entry for @p path. + * + * @param sc The NVRAM store to be updated. + * @param path_str The absolute device path string. + * @param path_len The length of @p path_str. + * + * @retval 0 if the path was successfully registered, or an identical + * path/alias entry already exists. + * @retval ENOMEM if allocation fails. + */ +int +bhnd_nvstore_register_path(struct bhnd_nvram_store *sc, const char *path_str, + size_t path_len) +{ + bhnd_nvstore_path_list *plist; + bhnd_nvstore_path *path; + uint32_t h; + + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + /* Already exists? */ + if (bhnd_nvstore_get_path(sc, path_str, path_len) != NULL) + return (0); + + /* Can't represent more than SIZE_MAX paths */ + if (sc->num_paths == SIZE_MAX) + return (ENOMEM); + + /* Allocate new entry */ + path = bhnd_nvstore_path_new(path_str, path_len); + if (path == NULL) + return (ENOMEM); + + /* Insert in path hash table */ + h = hash32_str(path->path_str, HASHINIT); + plist = &sc->paths[h % nitems(sc->paths)]; + LIST_INSERT_HEAD(plist, path, np_link); + + /* Increment path count */ + sc->num_paths++; + + return (0); +} + +/** + * Register a device path alias for an NVRAM 'devpathX' variable. + * + * The path value for the alias will be fetched from the backing NVRAM data. + * + * @param sc The NVRAM store to be updated. + * @param info The NVRAM variable name info. + * @param cookiep The NVRAM variable's cookiep value. + * + * @retval 0 if the alias was successfully registered, or an + * identical alias entry exists. + * @retval EEXIST if a conflicting alias or path entry already exists. + * @retval EINVAL if @p info is not a BHND_NVSTORE_ALIAS_DECL or does + * not contain a BHND_NVSTORE_PATH_ALIAS entry. + * @retval ENOMEM if allocation fails. + */ +int +bhnd_nvstore_register_alias(struct bhnd_nvram_store *sc, + const bhnd_nvstore_name_info *info, void *cookiep) +{ + bhnd_nvstore_alias_list *alist; + bhnd_nvstore_alias *alias; + bhnd_nvstore_path *path; + char *path_str; + size_t path_len; + int error; + + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + path_str = NULL; + alias = NULL; + + /* Can't represent more than SIZE_MAX aliases */ + if (sc->num_aliases == SIZE_MAX) + return (ENOMEM); + + /* Must be an alias declaration */ + if (info->type != BHND_NVSTORE_ALIAS_DECL) + return (EINVAL); + + if (info->path_type != BHND_NVSTORE_PATH_ALIAS) + return (EINVAL); + + /* Fetch the devpath variable's value length */ + error = bhnd_nvram_data_getvar(sc->data, cookiep, NULL, &path_len, + BHND_NVRAM_TYPE_STRING); + if (error) + return (ENOMEM); + + /* Allocate path string buffer */ + if ((path_str = bhnd_nv_malloc(path_len)) == NULL) + return (ENOMEM); + + /* Decode to our new buffer */ + error = bhnd_nvram_data_getvar(sc->data, cookiep, path_str, &path_len, + BHND_NVRAM_TYPE_STRING); + if (error) + goto failed; + + /* Trim trailing '/' character(s) from the path length */ + path_len = strnlen(path_str, path_len); + while (path_len > 0 && path_str[path_len-1] == '/') { + path_str[path_len-1] = '\0'; + path_len--; + } + + /* Is a conflicting alias entry already registered for this alias + * value? */ + alias = bhnd_nvstore_get_alias(sc, info->path.alias.value); + if (alias != NULL) { + if (alias->cookiep != cookiep || + strcmp(alias->path->path_str, path_str) != 0) + { + error = EEXIST; + goto failed; + } + } + + /* Is a conflicting entry already registered for the alias path? */ + if ((alias = bhnd_nvstore_find_alias(sc, path_str)) != NULL) { + if (alias->alias != info->path.alias.value || + alias->cookiep != cookiep || + strcmp(alias->path->path_str, path_str) != 0) + { + error = EEXIST; + goto failed; + } + } + + /* Get (or register) the target path entry */ + path = bhnd_nvstore_get_path(sc, path_str, path_len); + if (path == NULL) { + error = bhnd_nvstore_register_path(sc, path_str, path_len); + if (error) + goto failed; + + path = bhnd_nvstore_get_path(sc, path_str, path_len); + BHND_NV_ASSERT(path != NULL, ("missing registered path")); + } + + /* Allocate alias entry */ + alias = bhnd_nv_calloc(1, sizeof(*alias)); + if (alias == NULL) { + error = ENOMEM; + goto failed; + } + + alias->path = path; + alias->cookiep = cookiep; + alias->alias = info->path.alias.value; + + /* Insert in alias hash table */ + alist = &sc->aliases[alias->alias % nitems(sc->aliases)]; + LIST_INSERT_HEAD(alist, alias, na_link); + + /* Increment alias count */ + sc->num_aliases++; + + bhnd_nv_free(path_str); + return (0); + +failed: + if (path_str != NULL) + bhnd_nv_free(path_str); + + if (alias != NULL) + bhnd_nv_free(alias); + + return (error); +} + +/** + * If @p child is equal to or a child path of @p parent, return a pointer to + * @p child's path component(s) relative to @p parent; otherwise, return NULL. + */ +const char * +bhnd_nvstore_parse_relpath(const char *parent, const char *child) +{ + size_t prefix_len; + + /* All paths have an implicit leading '/'; this allows us to treat + * our manufactured root path of "/" as a prefix to all NVRAM-defined + * paths (which do not necessarily include a leading '/' */ + if (*parent == '/') + parent++; + + if (*child == '/') + child++; + + /* Is parent a prefix of child? */ + prefix_len = strlen(parent); + if (strncmp(parent, child, prefix_len) != 0) + return (NULL); + + /* A zero-length prefix matches everything */ + if (prefix_len == 0) + return (child); + + /* Is child equal to parent? */ + if (child[prefix_len] == '\0') + return (child + prefix_len); + + /* Is child actually a child of parent? */ + if (child[prefix_len] == '/') + return (child + prefix_len + 1); + + /* No match (e.g. parent=/foo..., child=/fooo...) */ + return (NULL); +} + +/** + * Parse a raw NVRAM variable name and return its @p entry_type, its + * type-specific @p prefix (e.g. '0:', 'pci/1/1', 'devpath'), and its + * type-specific @p suffix (e.g. 'varname', '0'). + * + * @param name The NVRAM variable name to be parsed. This + * value must remain valid for the lifetime of + * @p info. + * @param type The NVRAM name type -- either INTERNAL for names + * parsed from backing NVRAM data, or EXTERNAL for + * names provided by external NVRAM store clients. + * @param data_caps The backing NVRAM data capabilities + * (see bhnd_nvram_data_caps()). + * @param[out] info On success, the parsed variable name info. + * + * @retval 0 success + * @retval non-zero if parsing @p name otherwise fails, a regular unix + * error code will be returned. + */ +int +bhnd_nvstore_parse_name_info(const char *name, bhnd_nvstore_name_type type, + uint32_t data_caps, bhnd_nvstore_name_info *info) +{ + const char *p; + char *endp; + + /* Skip path parsing? */ + if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) { + /* devpath declaration? (devpath0=pci/1/1) */ + if (strncmp(name, "devpath", strlen("devpath")) == 0) { + u_long alias; + + /* Perform standard validation on the relative + * variable name */ + if (type != BHND_NVSTORE_NAME_INTERNAL && + !bhnd_nvram_validate_name(name)) + { + return (ENOENT); + } + + /* Parse alias value that should follow a 'devpath' + * prefix */ + p = name + strlen("devpath"); + alias = strtoul(p, &endp, 10); + if (endp != p && *endp == '\0') { + info->type = BHND_NVSTORE_ALIAS_DECL; + info->path_type = BHND_NVSTORE_PATH_ALIAS; + info->name = name; + info->path.alias.value = alias; + + return (0); + } + } + + /* device aliased variable? (0:varname) */ + if (bhnd_nv_isdigit(*name)) { + u_long alias; + + /* Parse '0:' alias prefix */ + alias = strtoul(name, &endp, 10); + if (endp != name && *endp == ':') { + /* Perform standard validation on the relative + * variable name */ + if (type != BHND_NVSTORE_NAME_INTERNAL && + !bhnd_nvram_validate_name(name)) + { + return (ENOENT); + } + + info->type = BHND_NVSTORE_VAR; + info->path_type = BHND_NVSTORE_PATH_ALIAS; + + /* name follows 0: prefix */ + info->name = endp + 1; + info->path.alias.value = alias; + + return (0); + } + } + + /* device variable? (pci/1/1/varname) */ + if ((p = strrchr(name, '/')) != NULL) { + const char *path, *relative_name; + size_t path_len; + + /* Determine the path length; 'p' points at the last + * path separator in 'name' */ + path_len = p - name; + path = name; + + /* The relative variable name directly follows the + * final path separator '/' */ + relative_name = path + path_len + 1; + + /* Now that we calculated the name offset, exclude all + * trailing '/' characters from the path length */ + while (path_len > 0 && path[path_len-1] == '/') + path_len--; + + /* Perform standard validation on the relative + * variable name */ + if (type != BHND_NVSTORE_NAME_INTERNAL && + !bhnd_nvram_validate_name(relative_name)) + { + return (ENOENT); + } + + /* Initialize result with pointers into the name + * buffer */ + info->type = BHND_NVSTORE_VAR; + info->path_type = BHND_NVSTORE_PATH_STRING; + info->name = relative_name; + info->path.str.value = path; + info->path.str.value_len = path_len; + + return (0); + } + } + + /* If all other parsing fails, the result is a simple variable with + * an implicit path of "/" */ + if (type != BHND_NVSTORE_NAME_INTERNAL && + !bhnd_nvram_validate_name(name)) + { + /* Invalid relative name */ + return (ENOENT); + } + + info->type = BHND_NVSTORE_VAR; + info->path_type = BHND_NVSTORE_PATH_STRING; + info->name = name; + info->path.str.value = BHND_NVSTORE_ROOT_PATH; + info->path.str.value_len = BHND_NVSTORE_ROOT_PATH_LEN; + + return (0); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_storevar.h =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_storevar.h +++ sys/dev/bhnd/nvram/bhnd_nvram_storevar.h @@ -38,49 +38,246 @@ #include #endif +#include "bhnd_nvram_plist.h" + #include "bhnd_nvram_store.h" /** Index is only generated if minimum variable count is met */ -#define NVRAM_IDX_VAR_THRESH 15 +#define BHND_NV_IDX_VAR_THRESHOLD 15 + +#define BHND_NVSTORE_ROOT_PATH "/" +#define BHND_NVSTORE_ROOT_PATH_LEN sizeof(BHND_NVSTORE_ROOT_PATH) + +#define BHND_NVSTORE_GET_FLAG(_value, _flag) \ + (((_value) & BHND_NVSTORE_ ## _flag) != 0) +#define BHND_NVSTORE_GET_BITS(_value, _field) \ + ((_value) & BHND_NVSTORE_ ## _field ## _MASK) -#define BHND_NVSTORE_PATH_ALIAS_NONE ULONG_MAX +/* Forward declarations */ +typedef struct bhnd_nvstore_name_info bhnd_nvstore_name_info; +typedef struct bhnd_nvstore_index bhnd_nvstore_index; +typedef struct bhnd_nvstore_path bhnd_nvstore_path; -LIST_HEAD(bhnd_nvstore_paths, bhnd_nvstore_path); +typedef struct bhnd_nvstore_alias bhnd_nvstore_alias; + +typedef struct bhnd_nvstore_alias_list bhnd_nvstore_alias_list; +typedef struct bhnd_nvstore_update_list bhnd_nvstore_update_list; +typedef struct bhnd_nvstore_path_list bhnd_nvstore_path_list; + +LIST_HEAD(bhnd_nvstore_alias_list, bhnd_nvstore_alias); +LIST_HEAD(bhnd_nvstore_update_list, bhnd_nvstore_update); +LIST_HEAD(bhnd_nvstore_path_list, bhnd_nvstore_path); /** - * NVRAM store path. + * NVRAM store variable entry types. */ -struct bhnd_nvstore_path { - char *path; /** relative path */ - u_long index; /** aliased path index, or - BHND_NVSTORE_PATH_IDX_INVALID */ +typedef enum { + BHND_NVSTORE_VAR = 0, /**< simple variable (var=...) */ + BHND_NVSTORE_ALIAS_DECL = 1, /**< alias declaration ('devpath0=pci/1/1') */ +} bhnd_nvstore_var_type; + +/** + * NVRAM path descriptor types. + */ +typedef enum { + BHND_NVSTORE_PATH_STRING = 0, /**< path is a string value */ + BHND_NVSTORE_PATH_ALIAS = 1 /**< path is an alias reference */ +} bhnd_nvstore_path_type; + +/** + * NVRAM variable namespaces. + */ +typedef enum { + BHND_NVSTORE_NAME_INTERNAL = 1, /**< internal namespace. permits + use of reserved devpath and + alias name prefixes. */ + BHND_NVSTORE_NAME_EXTERNAL = 2, /**< external namespace. forbids + use of name prefixes used + for device path handling */ +} bhnd_nvstore_name_type; + +bhnd_nvstore_path *bhnd_nvstore_path_new(const char *path_str, + size_t path_len); +void bhnd_nvstore_path_free(struct bhnd_nvstore_path *path); + +bhnd_nvstore_index *bhnd_nvstore_index_new(size_t capacity); +void bhnd_nvstore_index_free(bhnd_nvstore_index *index); +int bhnd_nvstore_index_append(struct bhnd_nvram_store *sc, + bhnd_nvstore_index *index, + void *cookiep); +int bhnd_nvstore_index_prepare( + struct bhnd_nvram_store *sc, + bhnd_nvstore_index *index); +void *bhnd_nvstore_index_lookup(struct bhnd_nvram_store *sc, + bhnd_nvstore_index *index, const char *name); + +bhnd_nvstore_path *bhnd_nvstore_get_root_path( + struct bhnd_nvram_store *sc); +bool bhnd_nvstore_is_root_path(struct bhnd_nvram_store *sc, + bhnd_nvstore_path *path); + +void *bhnd_nvstore_path_data_next( + struct bhnd_nvram_store *sc, + bhnd_nvstore_path *path, void **indexp); +void *bhnd_nvstore_path_data_lookup( + struct bhnd_nvram_store *sc, + bhnd_nvstore_path *path, const char *name); +bhnd_nvram_prop *bhnd_nvstore_path_get_update( + struct bhnd_nvram_store *sc, + bhnd_nvstore_path *path, const char *name); +int bhnd_nvstore_path_register_update( + struct bhnd_nvram_store *sc, + bhnd_nvstore_path *path, const char *name, + bhnd_nvram_val *value); + +bhnd_nvstore_alias *bhnd_nvstore_find_alias(struct bhnd_nvram_store *sc, + const char *path); +bhnd_nvstore_alias *bhnd_nvstore_get_alias(struct bhnd_nvram_store *sc, + u_long alias_val); + +bhnd_nvstore_path *bhnd_nvstore_get_path(struct bhnd_nvram_store *sc, + const char *path, size_t path_len); +bhnd_nvstore_path *bhnd_nvstore_resolve_path_alias( + struct bhnd_nvram_store *sc, u_long aval); + +bhnd_nvstore_path *bhnd_nvstore_var_get_path(struct bhnd_nvram_store *sc, + bhnd_nvstore_name_info *info); +int bhnd_nvstore_var_register_path( + struct bhnd_nvram_store *sc, + bhnd_nvstore_name_info *info, void *cookiep); + +int bhnd_nvstore_register_path(struct bhnd_nvram_store *sc, + const char *path, size_t path_len); +int bhnd_nvstore_register_alias( + struct bhnd_nvram_store *sc, + const bhnd_nvstore_name_info *info, void *cookiep); + +const char *bhnd_nvstore_parse_relpath(const char *parent, + const char *child); +int bhnd_nvstore_parse_name_info(const char *name, + bhnd_nvstore_name_type name_type, + uint32_t data_caps, bhnd_nvstore_name_info *info); + +/** + * NVRAM variable name descriptor. + * + * For NVRAM data instances supporting BHND_NVRAM_DATA_CAP_DEVPATHS, the + * NVRAM-vended variable name will be in one of four formats: + * + * - Simple Variable: + * 'variable' + * - Device Variable: + * 'pci/1/1/variable' + * - Device Alias Variable: + * '0:variable' + * - Device Path Alias Definition: + * 'devpath0=pci/1/1/variable' + * + * Device Paths: + * + * The device path format is device class-specific; the known supported device + * classes are: + * - sb: BCMA/SIBA SoC core device path. + * - pci: PCI device path (and PCIe on some earlier devices). + * - pcie: PCIe device path. + * - usb: USB device path. + * + * The device path format is loosely defined as '[class]/[domain]/[bus]/[slot]', + * with missing values either assumed to be zero, a value specific to the + * device class, or irrelevant to the device class in question. + * + * Examples: + * sb/1 BCMA/SIBA backplane 0, core 1. + * pc/1/1 PCMCIA bus 1, slot 1 + * pci/1/1 PCI/PCIe domain 0, bus 1, device 1 + * pcie/1/1 PCIe domain 0, bus 1, device 1 + * usb/0xbd17 USB PID 0xbd17 (VID defaults to Broadcom 0x0a5c) + * + * Device Path Aliases: + * + * Device path aliases reduce duplication of device paths in the flash encoding + * of NVRAM data; a single devpath[alias]=[devpath] variable entry is defined, + * and then later variables may reference the device path via its alias: + * devpath1=usb/0xbd17 + * 1:mcs5gpo0=0x1100 + * + * Alias values are always positive, base 10 integers. + */ +struct bhnd_nvstore_name_info { + const char *name; /**< variable name */ + bhnd_nvstore_var_type type; /**< variable type */ + bhnd_nvstore_path_type path_type; /**< path type */ + + /** Path information */ + union { + /* BHND_NVSTORE_PATH_STRING */ + struct { + const char *value; /**< device path */ + size_t value_len; /**< device path length */ + } str; - LIST_ENTRY(bhnd_nvstore_path) dp_link; + /** BHND_NVSTORE_PATH_ALIAS */ + struct { + u_long value; /**< device alias */ + } alias; + } path; }; /** - * NVRAM store index. + * NVRAM variable index. * * Provides effecient name-based lookup by maintaining an array of cached - * cookiep values, sorted lexicographically by variable name. + * cookiep values, sorted lexicographically by relative variable name. */ struct bhnd_nvstore_index { - size_t num_cookiep; /**< cookiep count */ - void *cookiep[]; /**< cookiep values */ + size_t count; /**< entry count */ + size_t capacity; /**< entry capacity */ + void *cookiep[]; /**< cookiep values */ }; +/** + * NVRAM device path. + */ +struct bhnd_nvstore_path { + char *path_str; /**< canonical path string */ + size_t num_vars; /**< per-path count of committed + (non-pending) variables */ + bhnd_nvstore_index *index; /**< per-path index, or NULL if + this is a root path for + which the data source + may be queried directly. */ + bhnd_nvram_plist *pending; /**< pending changes */ + + LIST_ENTRY(bhnd_nvstore_path) np_link; +}; + +/** + * NVRAM device path alias. + */ +struct bhnd_nvstore_alias { + bhnd_nvstore_path *path; /**< borrowed path reference */ + void *cookiep; /**< NVRAM variable's cookiep value */ + u_long alias; /**< alias value */ + + LIST_ENTRY(bhnd_nvstore_alias) na_link; +}; /** bhnd nvram store instance state */ struct bhnd_nvram_store { #ifdef _KERNEL - struct mtx mtx; + struct mtx mtx; #else - pthread_mutex_t mtx; + pthread_mutex_t mtx; #endif - struct bhnd_nvram_data *nv; /**< backing data */ - struct bhnd_nvstore_index *idx; /**< index, or NULL */ - struct bhnd_nvstore_paths paths; /**< paths */ - nvlist_t *pending; /**< uncommitted writes */ + struct bhnd_nvram_data *data; /**< backing data */ + uint32_t data_caps; /**< data capability flags */ + + bhnd_nvstore_alias_list aliases[4]; /**< path alias hash table */ + size_t num_aliases; /**< alias count */ + + bhnd_nvstore_path *root_path; /**< root path instance */ + bhnd_nvstore_path_list paths[4]; /**< path hash table */ + size_t num_paths; /**< path count */ }; #ifdef _KERNEL Index: sys/dev/bhnd/nvram/bhnd_nvram_subr.c =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_subr.c +++ sys/dev/bhnd/nvram/bhnd_nvram_subr.c @@ -614,44 +614,45 @@ * 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) +bhnd_nvram_validate_name(const char *name) { - size_t limit; - - limit = strnlen(name, name_len); - if (limit == 0) + /* Reject path-prefixed variable names */ + if (bhnd_nvram_trim_path_name(name) != name) return (false); - /* Disallow path alias prefixes ([0-9]+:.*) */ - if (limit >= 2 && bhnd_nv_isdigit(*name)) { - for (const char *p = name; (size_t)(p - name) < limit; p++) { - if (bhnd_nv_isdigit(*p)) - continue; - else if (*p == ':') - return (false); - else - break; - } + /* Reject device path alias declarations (devpath[1-9][0-9]*.*\0) */ + if (strncmp(name, "devpath", strlen("devpath")) == 0) { + const char *p; + char *endp; + + /* Check for trailing [1-9][0-9]* */ + p = name + strlen("devpath"); + strtoul(p, &endp, 10); + if (endp != p) + return (false); } - /* Scan for special characters */ - for (const char *p = name; (size_t)(p - name) < limit; p++) { + /* Scan for [^A-Za-z_0-9] */ + for (const char *p = name; *p != '\0'; p++) { switch (*p) { - case '/': /* path delimiter */ - case '=': /* key=value delimiter */ - return (false); + /* [0-9_] */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case '_': + break; + /* [A-Za-z] */ default: - if (!isascii(*p) || bhnd_nv_isspace(*p)) + if (!bhnd_nv_isalpha(*p)) return (false); + break; } } @@ -950,6 +951,46 @@ } /** + * Trim leading path (pci/1/1) or path alias (0:) prefix from @p name, if any, + * returning a pointer to the start of the relative variable name. + * + * @par Examples + * + * - "/foo" -> "foo" + * - "dev/pci/foo" -> "foo" + * - "0:foo" -> "foo" + * - "foo" -> "foo" + * + * @param name The string to be trimmed. + * + * @return A pointer to the start of the relative variable name in @p name. + */ +const char * +bhnd_nvram_trim_path_name(const char *name) +{ + char *endp; + + /* path alias prefix? (0:varname) */ + if (bhnd_nv_isdigit(*name)) { + /* Parse '0...:' alias prefix, if it exists */ + strtoul(name, &endp, 10); + if (endp != name && *endp == ':') { + /* Variable name follows 0: prefix */ + return (endp+1); + } + } + + /* device path prefix? (pci/1/1/varname) */ + if ((endp = strrchr(name, '/')) != NULL) { + /* Variable name follows the final path separator '/' */ + return (endp+1); + } + + /* variable name is not prefixed */ + return (name); +} + +/** * Parse a 'name=value' string. * * @param env The string to be parsed. Index: sys/modules/bhnd/Makefile =================================================================== --- sys/modules/bhnd/Makefile +++ sys/modules/bhnd/Makefile @@ -39,6 +39,7 @@ bhnd_nvram_iores.c \ bhnd_nvram_plist.c \ bhnd_nvram_store.c \ + bhnd_nvram_store_subr.c \ bhnd_nvram_subr.c \ bhnd_nvram_value.c \ bhnd_nvram_value_fmts.c \