Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data.h =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data.h +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data.h @@ -44,6 +44,7 @@ #include "bhnd_nvram.h" #include "bhnd_nvram_io.h" +#include "bhnd_nvram_plist.h" #include "bhnd_nvram_value.h" /* NVRAM data class */ @@ -115,6 +116,7 @@ int bhnd_nvram_data_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len); +bhnd_nvram_plist *bhnd_nvram_data_options(struct bhnd_nvram_data *nv); uint32_t bhnd_nvram_data_caps(struct bhnd_nvram_data *nv); const char *bhnd_nvram_data_next(struct bhnd_nvram_data *nv, Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data.c @@ -281,6 +281,18 @@ } /** + * Return a borrowed reference to the serialization options for @p nv, + * suitable for use with bhnd_nvram_data_serialize(), or NULL if none. + * + * @param nv The NVRAM data to be queried. + */ +bhnd_nvram_plist * +bhnd_nvram_data_options(struct bhnd_nvram_data *nv) +{ + return (nv->cls->op_options(nv)); +} + +/** * Compute the size of the serialized form of @p nv. * * Serialization may be performed via bhnd_nvram_data_serialize(). Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c @@ -121,6 +121,7 @@ struct bhnd_nvram_bcm { struct bhnd_nvram_data nv; /**< common instance state */ struct bhnd_nvram_io *data; /**< backing buffer */ + bhnd_nvram_plist *opts; /**< serialization options */ /** BCM header values */ struct bhnd_nvram_bcm_hvar hvars[nitems(bhnd_nvram_bcm_hvars)]; @@ -157,7 +158,7 @@ uint8_t *p; void *ptr; size_t io_offset, io_size; - uint8_t crc, valid; + uint8_t crc, valid, bcm_ver; int error; if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr)))) @@ -344,6 +345,14 @@ bcm->count++; } + /* Populate serialization options from our header */ + bcm_ver = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER); + error = bhnd_nvram_plist_append_bytes(bcm->opts, + BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver, sizeof(bcm_ver), + BHND_NVRAM_TYPE_UINT8); + if (error) + return (error); + return (0); } @@ -360,6 +369,12 @@ "hvar declarations must match bhnd_nvram_bcm_hvars template"); memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars)); + /* Allocate (empty) option list, to be populated by + * bhnd_nvram_bcm_init() */ + bcm->opts = bhnd_nvram_plist_new(); + if (bcm->opts == NULL) + return (ENOMEM); + /* Parse the BCM input data and initialize our backing * data representation */ if ((error = bhnd_nvram_bcm_init(bcm, io))) { @@ -377,6 +392,9 @@ if (bcm->data != NULL) bhnd_nvram_io_free(bcm->data); + + if (bcm->opts != NULL) + bhnd_nvram_plist_release(bcm->opts); } size_t @@ -386,6 +404,13 @@ return (bcm->count); } +static bhnd_nvram_plist * +bhnd_nvram_bcm_options(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv; + return (bcm->opts); +} + static int bhnd_nvram_bcm_size(struct bhnd_nvram_data *nv, size_t *size) { Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c @@ -79,19 +79,19 @@ { char envp[16]; size_t envp_len; + size_t io_size; int error; + io_size = bhnd_nvram_io_getsize(io); + /* - * Fetch the initial bytes to try to identify BCM data. - * - * We always assert a low probe priority, as we only scan the initial - * bytes of the file. + * Fetch initial bytes */ - envp_len = bhnd_nv_ummin(sizeof(envp), bhnd_nvram_io_getsize(io)); + envp_len = bhnd_nv_ummin(sizeof(envp), io_size); if ((error = bhnd_nvram_io_read(io, 0x0, envp, envp_len))) return (error); - /* A zero-length BCM-RAW buffer should contain a single terminating + /* An empty BCM-RAW buffer should still contain a single terminating * NUL */ if (envp_len == 0) return (ENXIO); @@ -103,21 +103,33 @@ return (BHND_NVRAM_DATA_PROBE_MAYBE); } - /* Don't match on non-ASCII, non-printable data */ + /* Must contain only printable ASCII characters delimited + * by NUL record delimiters */ for (size_t i = 0; i < envp_len; i++) { char c = envp[i]; - if (envp[i] == '\0') - break; - if (!bhnd_nv_isprint(c)) + /* If we hit a newline, this is probably BCM-TXT */ + if (c == '\n') return (ENXIO); + + if (c == '\0' && !bhnd_nv_isprint(c)) + continue; } - /* The first character should be a valid key char */ - if (!bhnd_nv_isalpha(envp[0])) + /* A valid BCM-RAW buffer should contain a terminating NUL for + * the last record, followed by a final empty record terminated by + * NUL */ + envp_len = 2; + if (io_size < envp_len) + return (ENXIO); + + if ((error = bhnd_nvram_io_read(io, io_size-envp_len, envp, envp_len))) + return (error); + + if (envp[0] != '\0' || envp[1] != '\0') return (ENXIO); - return (BHND_NVRAM_DATA_PROBE_MAYBE); + return (BHND_NVRAM_DATA_PROBE_MAYBE + 1); } /** @@ -245,6 +257,12 @@ return (bcm->count); } +static bhnd_nvram_plist * +bhnd_nvram_bcmraw_options(struct bhnd_nvram_data *nv) +{ + return (NULL); +} + static int bhnd_nvram_bcmraw_size(struct bhnd_nvram_data *nv, size_t *size) { Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h @@ -32,6 +32,8 @@ #ifndef _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ #define _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ +#define BCM_NVRAM_ENCODE_OPT_VERSION "bcm_version" + /** * BCM NVRAM header value data. */ Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c @@ -255,6 +255,12 @@ return (btxt->count); } +static bhnd_nvram_plist * +bhnd_nvram_btxt_options(struct bhnd_nvram_data *nv) +{ + return (NULL); +} + static int bhnd_nvram_btxt_size(struct bhnd_nvram_data *nv, size_t *size) { Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c @@ -416,6 +416,12 @@ bhnd_nv_free(sp->idx); } +static bhnd_nvram_plist * +bhnd_nvram_sprom_options(struct bhnd_nvram_data *nv) +{ + return (NULL); +} + size_t bhnd_nvram_sprom_count(struct bhnd_nvram_data *nv) { Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c @@ -256,6 +256,12 @@ return (tlv->count); } +static bhnd_nvram_plist * +bhnd_nvram_tlv_options(struct bhnd_nvram_data *nv) +{ + return (NULL); +} + static int bhnd_nvram_tlv_size(struct bhnd_nvram_data *nv, size_t *size) { @@ -350,17 +356,26 @@ tlv = (struct bhnd_nvram_tlv *)nv; - /* Seek past the TLV_ENV record referenced by cookiep */ - io_offset = bhnd_nvram_tlv_to_offset(tlv, *cookiep); - if (bhnd_nvram_tlv_next_env(tlv, &io_offset, NULL) == NULL) - BHND_NV_PANIC("invalid cookiep: %p\n", cookiep); - - /* Fetch the next TLV_ENV record */ - if ((env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep)) == NULL) { - /* No remaining ENV records */ - return (NULL); + /* Find next readable TLV record */ + if (*cookiep == NULL) { + /* Start search at offset 0x0 */ + io_offset = 0x0; + env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep); + } else { + /* Seek past the previous env record */ + io_offset = bhnd_nvram_tlv_to_offset(tlv, *cookiep); + env = bhnd_nvram_tlv_next_env(tlv, &io_offset, NULL); + if (env == NULL) + BHND_NV_PANIC("invalid cookiep; record missing"); + + /* Advance to next env record, update the caller's cookiep */ + env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep); } + /* Check for EOF */ + if (env == NULL) + return (NULL); + /* Return the NUL terminated name */ return (env->envp); } Index: head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h +++ head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h @@ -75,6 +75,10 @@ struct bhnd_nvram_data *nv, void *buf, size_t *len); +/** @see bhnd_nvram_data_options() */ +typedef bhnd_nvram_plist*(bhnd_nvram_data_op_options)( + struct bhnd_nvram_data *nv); + /** @see bhnd_nvram_data_caps() */ typedef uint32_t (bhnd_nvram_data_op_caps)(struct bhnd_nvram_data *nv); @@ -133,6 +137,7 @@ bhnd_nvram_data_op_count *op_count; bhnd_nvram_data_op_size *op_size; bhnd_nvram_data_op_serialize *op_serialize; + bhnd_nvram_data_op_options *op_options; bhnd_nvram_data_op_caps *op_caps; bhnd_nvram_data_op_next *op_next; bhnd_nvram_data_op_find *op_find; @@ -186,6 +191,7 @@ _macro(_cname, count) \ _macro(_cname, size) \ _macro(_cname, serialize) \ + _macro(_cname, options) \ _macro(_cname, caps) \ _macro(_cname, next) \ _macro(_cname, find) \ Index: head/sys/dev/bhnd/nvram/bhnd_nvram_store.h =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_store.h +++ head/sys/dev/bhnd/nvram/bhnd_nvram_store.h @@ -45,10 +45,24 @@ #include "bhnd_nvram_data.h" #include "bhnd_nvram_io.h" -#include "bhnd_nvram_value.h" struct bhnd_nvram_store; +/** + * NVRAM export flags. + */ +enum { + BHND_NVSTORE_EXPORT_CHILDREN = (1<<0), /**< Include all subpaths */ + BHND_NVSTORE_EXPORT_PRESERVE_DEVPATHS = (1<<1), /**< Preserve existing device path definitions (default) */ + BHND_NVSTORE_EXPORT_COMPACT_DEVPATHS = (1<<2), /**< Re-encode all device paths using compact syntax */ + BHND_NVSTORE_EXPORT_EXPAND_DEVPATHS = (1<<3), /**< Re-encode all device paths using non-compact syntax */ + BHND_NVSTORE_EXPORT_ALL_VARS = (1<<6|1<<7), /**< Include all variables (default) */ + BHND_NVSTORE_EXPORT_COMMITTED = (1<<6), /**< Include all committed changes */ + BHND_NVSTORE_EXPORT_UNCOMMITTED = (1<<7), /**< Include all uncommitted changes (not including deletions) */ + BHND_NVSTORE_EXPORT_DELETED = (1<<8), /**< Include all uncommitted deltions (as + properties of type BHND_NVRAM_TYPE_NULL) */ +}; + int bhnd_nvram_store_new(struct bhnd_nvram_store **store, struct bhnd_nvram_data *data); @@ -57,6 +71,10 @@ void bhnd_nvram_store_free(struct bhnd_nvram_store *store); +int bhnd_nvram_store_export(struct bhnd_nvram_store *store, + const char *path, bhnd_nvram_data_class **cls, + bhnd_nvram_plist **props, bhnd_nvram_plist **options, + uint32_t flags); int bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name, void *outp, size_t *olen, bhnd_nvram_type otype); Index: head/sys/dev/bhnd/nvram/bhnd_nvram_store.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_store.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_store.c @@ -71,6 +71,26 @@ static int bhnd_nvstore_parse_path_entries( struct bhnd_nvram_store *sc); +static int bhnd_nvram_store_export_child( + struct bhnd_nvram_store *sc, + bhnd_nvstore_path *top, + bhnd_nvstore_path *child, + bhnd_nvram_plist *plist, + uint32_t flags); + +static int bhnd_nvstore_export_merge( + struct bhnd_nvram_store *sc, + bhnd_nvstore_path *path, + bhnd_nvram_plist *merged, + uint32_t flags); + +static int bhnd_nvstore_export_devpath_alias( + struct bhnd_nvram_store *sc, + bhnd_nvstore_path *path, + const char *devpath, + bhnd_nvram_plist *plist, + u_long *alias_val); + /** * Allocate and initialize a new NVRAM data store instance. * @@ -114,6 +134,16 @@ /* Retain the NVRAM data */ sc->data = bhnd_nvram_data_retain(data); sc->data_caps = bhnd_nvram_data_caps(data); + sc->data_opts = bhnd_nvram_data_options(data); + if (sc->data_opts != NULL) { + bhnd_nvram_plist_retain(sc->data_opts); + } else { + sc->data_opts = bhnd_nvram_plist_new(); + if (sc->data_opts == NULL) { + error = ENOMEM; + goto cleanup; + } + } /* Register required root path */ error = bhnd_nvstore_register_path(sc, BHND_NVSTORE_ROOT_PATH, @@ -207,6 +237,8 @@ if (sc->data != NULL) bhnd_nvram_data_release(sc->data); + if (sc->data_opts != NULL) + bhnd_nvram_plist_release(sc->data_opts); BHND_NVSTORE_LOCK_DESTROY(sc); bhnd_nv_free(sc); @@ -400,6 +432,571 @@ return (0); } + + +/** + * Merge exported per-path variables (uncommitted, committed, or both) into + * the empty @p merged property list. + * + * @param sc The NVRAM store instance. + * @param path The NVRAM path to be exported. + * @param merged The property list to populate with the merged results. + * @param flags Export flags. See BHND_NVSTORE_EXPORT_*. + * + * @retval 0 success + * @retval ENOMEM If allocation fails. + * @retval non-zero If merging the variables defined in @p path otherwise + * fails, a regular unix error code will be returned. + */ +static int +bhnd_nvstore_export_merge(struct bhnd_nvram_store *sc, + bhnd_nvstore_path *path, bhnd_nvram_plist *merged, uint32_t flags) +{ + void *cookiep, *idxp; + int error; + + /* Populate merged list with all pending variables */ + if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) { + bhnd_nvram_prop *prop; + + prop = NULL; + while ((prop = bhnd_nvram_plist_next(path->pending, prop))) { + /* Skip variables marked for deletion */ + if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED)) { + if (bhnd_nvram_prop_is_null(prop)) + continue; + } + + /* Append to merged list */ + error = bhnd_nvram_plist_append(merged, prop); + if (error) + return (error); + } + } + + /* Skip merging committed variables? */ + if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED)) + return (0); + + /* Merge in the committed NVRAM variables */ + idxp = NULL; + while ((cookiep = bhnd_nvstore_path_data_next(sc, path, &idxp))) { + const char *name; + bhnd_nvram_val *val; + + /* Fetch the variable name */ + name = bhnd_nvram_data_getvar_name(sc->data, cookiep); + + /* Trim device path prefix */ + if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) + name = bhnd_nvram_trim_path_name(name); + + /* Skip if already defined in pending updates */ + if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) { + if (bhnd_nvram_plist_contains(path->pending, name)) + continue; + } + + /* Skip if higher precedence value was already defined. This + * may occur if the underlying data store contains duplicate + * keys; iteration will always return the definition with + * the highest precedence first */ + if (bhnd_nvram_plist_contains(merged, name)) + continue; + + /* Fetch the variable's value representation */ + if ((error = bhnd_nvram_data_copy_val(sc->data, cookiep, &val))) + return (error); + + /* Add to path variable list */ + error = bhnd_nvram_plist_append_val(merged, name, val); + bhnd_nvram_val_release(val); + if (error) + return (error); + } + + return (0); +} + +/** + * Find a free alias value for @p path, and append the devpathXX alias + * declaration to @p plist. + * + * @param sc The NVRAM store instance. + * @param path The NVRAM path for which a devpath alias + * variable should be produced. + * @param devpath The devpathXX path value for @p path. + * @param plist The property list to which @p path's devpath + * variable will be appended. + * @param[out] alias_val On success, will be set to the alias value + * allocated for @p path. + * + * @retval 0 success + * @retval ENOMEM If allocation fails. + * @retval non-zero If merging the variables defined in @p path otherwise + * fails, a regular unix error code will be returned. + */ +static int +bhnd_nvstore_export_devpath_alias(struct bhnd_nvram_store *sc, + bhnd_nvstore_path *path, const char *devpath, bhnd_nvram_plist *plist, + u_long *alias_val) +{ + bhnd_nvstore_alias *alias; + char *pathvar; + int error; + + *alias_val = 0; + + /* Prefer alias value already reserved for this path. */ + alias = bhnd_nvstore_find_alias(sc, path->path_str); + if (alias != NULL) { + *alias_val = alias->alias; + + /* Allocate devpathXX variable name */ + bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val); + if (pathvar == NULL) + return (ENOMEM); + + /* Append alias variable to property list */ + error = bhnd_nvram_plist_append_string(plist, pathvar, devpath); + + BHND_NV_ASSERT(error != EEXIST, ("reserved alias %lu:%s in use", + * alias_val, path->path_str)); + + bhnd_nv_free(pathvar); + return (error); + } + + /* Find the next free devpathXX alias entry */ + while (1) { + /* Skip existing reserved alias values */ + while (bhnd_nvstore_get_alias(sc, *alias_val) != NULL) { + if (*alias_val == ULONG_MAX) + return (ENOMEM); + + (*alias_val)++; + } + + /* Allocate devpathXX variable name */ + bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val); + if (pathvar == NULL) + return (ENOMEM); + + /* If not in-use, we can terminate the search */ + if (!bhnd_nvram_plist_contains(plist, pathvar)) + break; + + /* Keep searching */ + bhnd_nv_free(pathvar); + + if (*alias_val == ULONG_MAX) + return (ENOMEM); + + (*alias_val)++; + } + + /* Append alias variable to property list */ + error = bhnd_nvram_plist_append_string(plist, pathvar, devpath); + + bhnd_nv_free(pathvar); + return (error); +} + +/** + * Export a single @p child path's properties, appending the result to @p plist. + * + * @param sc The NVRAM store instance. + * @param top The root NVRAM path being exported. + * @param child The NVRAM path to be exported. + * @param plist The property list to which @p child's exported + * properties should be appended. + * @param flags Export flags. See BHND_NVSTORE_EXPORT_*. + * + * @retval 0 success + * @retval ENOMEM If allocation fails. + * @retval non-zero If merging the variables defined in @p path otherwise + * fails, a regular unix error code will be returned. + */ +static int +bhnd_nvram_store_export_child(struct bhnd_nvram_store *sc, + bhnd_nvstore_path *top, bhnd_nvstore_path *child, bhnd_nvram_plist *plist, + uint32_t flags) +{ + bhnd_nvram_plist *path_vars; + bhnd_nvram_prop *prop; + const char *relpath; + char *prefix, *namebuf; + size_t prefix_len, relpath_len; + size_t namebuf_size, num_props; + bool emit_compact_devpath; + int error; + + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + prefix = NULL; + num_props = 0; + path_vars = NULL; + namebuf = NULL; + + /* Determine the path relative to the top-level path */ + relpath = bhnd_nvstore_parse_relpath(top->path_str, child->path_str); + if (relpath == NULL) { + /* Skip -- not a child of the root path */ + return (0); + } + relpath_len = strlen(relpath); + + /* Skip sub-path if export of children was not requested, */ + if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_CHILDREN) && relpath_len > 0) + return (0); + + /* Collect all variables to be included in the export */ + if ((path_vars = bhnd_nvram_plist_new()) == NULL) + return (ENOMEM); + + if ((error = bhnd_nvstore_export_merge(sc, child, path_vars, flags))) { + bhnd_nvram_plist_release(path_vars); + return (error); + } + + /* Skip if no children are to be exported */ + if (bhnd_nvram_plist_count(path_vars) == 0) { + bhnd_nvram_plist_release(path_vars); + return (0); + } + + /* Determine appropriate device path encoding */ + emit_compact_devpath = false; + if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS)) { + /* Re-encode as compact (if non-empty path) */ + if (relpath_len > 0) + emit_compact_devpath = true; + } else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) { + /* Re-encode with fully expanded device path */ + emit_compact_devpath = false; + } else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) { + /* Preserve existing encoding of this path */ + if (bhnd_nvstore_find_alias(sc, child->path_str) != NULL) + emit_compact_devpath = true; + } else { + BHND_NV_LOG("invalid device path flag: %#" PRIx32, flags); + error = EINVAL; + goto finished; + } + + /* Allocate variable device path prefix to use for all property names, + * and if using compact encoding, emit the devpathXX= variable */ + prefix = NULL; + prefix_len = 0; + if (emit_compact_devpath) { + u_long alias_val; + int len; + + /* Reserve an alias value and append the devpathXX= variable to + * the property list */ + error = bhnd_nvstore_export_devpath_alias(sc, child, relpath, + plist, &alias_val); + if (error) + goto finished; + + /* Allocate variable name prefix */ + len = bhnd_nv_asprintf(&prefix, "%lu:", alias_val); + if (prefix == NULL) { + error = ENOMEM; + goto finished; + } + + prefix_len = len; + } else if (relpath_len > 0) { + int len; + + /* Allocate the variable name prefix, appending '/' to the + * relative path */ + len = bhnd_nv_asprintf(&prefix, "%s/", relpath); + if (prefix == NULL) { + error = ENOMEM; + goto finished; + } + + prefix_len = len; + } + + /* If prefixing of variable names is required, allocate a name + * formatting buffer */ + namebuf_size = 0; + if (prefix != NULL) { + size_t maxlen; + + /* Find the maximum name length */ + maxlen = 0; + prop = NULL; + while ((prop = bhnd_nvram_plist_next(path_vars, prop))) { + const char *name; + + name = bhnd_nvram_prop_name(prop); + maxlen = bhnd_nv_ummax(strlen(name), maxlen); + } + + /* Allocate name buffer (path-prefix + name + '\0') */ + namebuf_size = prefix_len + maxlen + 1; + namebuf = bhnd_nv_malloc(namebuf_size); + if (namebuf == NULL) { + error = ENOMEM; + goto finished; + } + } + + /* Append all path variables to the export plist, prepending the + * device-path prefix to the variable names, if required */ + prop = NULL; + while ((prop = bhnd_nvram_plist_next(path_vars, prop)) != NULL) { + const char *name; + + /* Prepend device prefix to the variable name */ + name = bhnd_nvram_prop_name(prop); + if (prefix != NULL) { + int len; + + /* + * Write prefixed variable name to our name buffer. + * + * We precalcuate the size when scanning all names + * above, so this should always succeed. + */ + len = snprintf(namebuf, namebuf_size, "%s%s", prefix, + name); + if (len < 0 || (size_t)len >= namebuf_size) + BHND_NV_PANIC("invalid max_name_len"); + + name = namebuf; + } + + /* Add property to export plist */ + error = bhnd_nvram_plist_append_val(plist, name, + bhnd_nvram_prop_val(prop)); + if (error) + goto finished; + } + + /* Success */ + error = 0; + +finished: + if (prefix != NULL) + bhnd_nv_free(prefix); + + if (namebuf != NULL) + bhnd_nv_free(namebuf); + + if (path_vars != NULL) + bhnd_nvram_plist_release(path_vars); + + return (error); +} + +/** + * Export a flat, ordered NVRAM property list representation of all NVRAM + * properties at @p path. + * + * @param sc The NVRAM store instance. + * @param path The NVRAM path to export, or NULL to select the root + * path. + * @param[out] cls On success, will be set to the backing data class + * of @p sc. If the data class is are not desired, + * a NULL pointer may be provided. + * @param[out] props On success, will be set to a caller-owned property + * list containing the exported properties. The caller is + * responsible for releasing this value via + * bhnd_nvram_plist_release(). + * @param[out] options On success, will be set to a caller-owned property + * list containing the current NVRAM serialization options + * for @p sc. The caller is responsible for releasing this + * value via bhnd_nvram_plist_release(). + * @param flags Export flags. See BHND_NVSTORE_EXPORT_*. + * + * @retval 0 success + * @retval EINVAL If @p flags is invalid. + * @retval ENOENT The requested path was not found. + * @retval ENOMEM If allocation fails. + * @retval non-zero If export of @p path otherwise fails, a regular unix + * error code will be returned. + */ +int +bhnd_nvram_store_export(struct bhnd_nvram_store *sc, const char *path, + bhnd_nvram_data_class **cls, bhnd_nvram_plist **props, + bhnd_nvram_plist **options, uint32_t flags) +{ + bhnd_nvram_plist *unordered; + bhnd_nvstore_path *top; + bhnd_nvram_prop *prop; + const char *name; + void *cookiep; + size_t num_dpath_flags; + int error; + + *props = NULL; + unordered = NULL; + num_dpath_flags = 0; + if (options != NULL) + *options = NULL; + + /* Default to exporting root path */ + if (path == NULL) + path = BHND_NVSTORE_ROOT_PATH; + + /* Default to exporting all properties */ + if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED) && + !BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) + { + flags |= BHND_NVSTORE_EXPORT_ALL_VARS; + } + + /* Default to preserving the current device path encoding */ + if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS) && + !BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) + { + flags |= BHND_NVSTORE_EXPORT_PRESERVE_DEVPATHS; + } + + /* Exactly one device path encoding flag must be set */ + if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS)) + num_dpath_flags++; + + if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) + num_dpath_flags++; + + if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) + num_dpath_flags++; + + if (num_dpath_flags != 1) + return (EINVAL); + + /* If EXPORT_DELETED is set, EXPORT_UNCOMMITTED must be set too */ + if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED) && + !BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED)) + { + return (EINVAL); + } + + /* Lock internal state before querying paths/properties */ + BHND_NVSTORE_LOCK(sc); + + /* Fetch referenced path */ + top = bhnd_nvstore_get_path(sc, path, strlen(path)); + if (top == NULL) { + error = ENOENT; + goto failed; + } + + /* Allocate new, empty property list */ + if ((unordered = bhnd_nvram_plist_new()) == NULL) { + error = ENOMEM; + goto failed; + } + + /* Export the top-level path first */ + error = bhnd_nvram_store_export_child(sc, top, top, unordered, flags); + if (error) + goto failed; + + /* Attempt to export any children of the root path */ + for (size_t i = 0; i < nitems(sc->paths); i++) { + bhnd_nvstore_path *child; + + LIST_FOREACH(child, &sc->paths[i], np_link) { + /* Top-level path was already exported */ + if (child == top) + continue; + + error = bhnd_nvram_store_export_child(sc, top, + child, unordered, flags); + if (error) + goto failed; + } + } + + /* If requested, provide the current class and serialization options */ + if (cls != NULL) + *cls = bhnd_nvram_data_get_class(sc->data); + + if (options != NULL) + *options = bhnd_nvram_plist_retain(sc->data_opts); + + /* + * If we're re-encoding device paths, don't bother preserving the + * existing NVRAM variable order; our variable names will not match + * the existing backing NVRAM data. + */ + if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) { + *props = unordered; + unordered = NULL; + + goto finished; + } + + /* + * Re-order the flattened output to match the existing NVRAM variable + * ordering. + * + * We append all new variables at the end of the input; this should + * reduce the delta that needs to be written (e.g. to flash) when + * committing NVRAM updates, and should result in a serialization + * identical to the input serialization if uncommitted updates are + * excluded from the export. + */ + if ((*props = bhnd_nvram_plist_new()) == NULL) { + error = ENOMEM; + goto failed; + } + + /* Using the backing NVRAM data ordering to order all variables + * currently defined in the backing store */ + cookiep = NULL; + while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) { + prop = bhnd_nvram_plist_get_prop(unordered, name); + if (prop == NULL) + continue; + + /* Append to ordered result */ + if ((error = bhnd_nvram_plist_append(*props, prop))) + goto failed; + + /* Remove from unordered list */ + bhnd_nvram_plist_remove(unordered, name); + } + + /* Any remaining variables are new, and should be appended to the + * end of the export list */ + prop = NULL; + while ((prop = bhnd_nvram_plist_next(unordered, prop)) != NULL) { + if ((error = bhnd_nvram_plist_append(*props, prop))) + goto failed; + } + + /* Export complete */ +finished: + BHND_NVSTORE_UNLOCK(sc); + + if (unordered != NULL) + bhnd_nvram_plist_release(unordered); + + return (0); + +failed: + BHND_NVSTORE_UNLOCK(sc); + + if (unordered != NULL) + bhnd_nvram_plist_release(unordered); + + if (options != NULL && *options != NULL) + bhnd_nvram_plist_release(*options); + + if (*props != NULL) + bhnd_nvram_plist_release(*props); + + return (error); +} + /** * Read an NVRAM variable. * Index: head/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c @@ -342,7 +342,7 @@ /* 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); + cookiep = bhnd_nvstore_path_data_lookup(sc, path, name); if (cookiep != NULL) { /* Variable is defined in the backing data */ nvram_committed = true; Index: head/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h +++ head/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h @@ -271,6 +271,7 @@ #endif struct bhnd_nvram_data *data; /**< backing data */ uint32_t data_caps; /**< data capability flags */ + bhnd_nvram_plist *data_opts; /**< data serialization options */ bhnd_nvstore_alias_list aliases[4]; /**< path alias hash table */ size_t num_aliases; /**< alias count */