Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_store.c
39536 views
/*-1* Copyright (c) 2015-2016 Landon Fuller <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer,9* without modification.10* 2. Redistributions in binary form must reproduce at minimum a disclaimer11* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any12* redistribution must be conditioned upon including a substantially13* similar Disclaimer requirement for further binary redistribution.14*15* NO WARRANTY16* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY19* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL20* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,21* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF22* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS23* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER24* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)25* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF26* THE POSSIBILITY OF SUCH DAMAGES.27*/2829#include <sys/param.h>30#include <sys/hash.h>31#include <sys/limits.h>32#include <sys/queue.h>3334#ifdef _KERNEL3536#include <sys/ctype.h>37#include <sys/systm.h>3839#include <machine/_inttypes.h>4041#else /* !_KERNEL */4243#include <ctype.h>44#include <errno.h>45#include <inttypes.h>46#include <stdbool.h>47#include <stdio.h>48#include <stdint.h>49#include <stdlib.h>50#include <string.h>5152#endif /* _KERNEL */5354#include "bhnd_nvram_private.h"55#include "bhnd_nvram_datavar.h"5657#include "bhnd_nvram_storevar.h"5859/*60* BHND NVRAM Store61*62* Manages in-memory and persistent representations of NVRAM data.63*/6465static int bhnd_nvstore_parse_data(66struct bhnd_nvram_store *sc);6768static int bhnd_nvstore_parse_path_entries(69struct bhnd_nvram_store *sc);7071static int bhnd_nvram_store_export_child(72struct bhnd_nvram_store *sc,73bhnd_nvstore_path *top,74bhnd_nvstore_path *child,75bhnd_nvram_plist *plist,76uint32_t flags);7778static int bhnd_nvstore_export_merge(79struct bhnd_nvram_store *sc,80bhnd_nvstore_path *path,81bhnd_nvram_plist *merged,82uint32_t flags);8384static int bhnd_nvstore_export_devpath_alias(85struct bhnd_nvram_store *sc,86bhnd_nvstore_path *path,87const char *devpath,88bhnd_nvram_plist *plist,89u_long *alias_val);9091/**92* Allocate and initialize a new NVRAM data store instance.93*94* The caller is responsible for deallocating the instance via95* bhnd_nvram_store_free().96*97* @param[out] store On success, a pointer to the newly allocated NVRAM data98* instance.99* @param data The NVRAM data to be managed by the returned NVRAM data store100* instance.101*102* @retval 0 success103* @retval non-zero if an error occurs during allocation or initialization, a104* regular unix error code will be returned.105*/106int107bhnd_nvram_store_new(struct bhnd_nvram_store **store,108struct bhnd_nvram_data *data)109{110struct bhnd_nvram_store *sc;111int error;112113/* Allocate new instance */114sc = bhnd_nv_calloc(1, sizeof(*sc));115if (sc == NULL)116return (ENOMEM);117118BHND_NVSTORE_LOCK_INIT(sc);119BHND_NVSTORE_LOCK(sc);120121/* Initialize path hash table */122sc->num_paths = 0;123for (size_t i = 0; i < nitems(sc->paths); i++)124LIST_INIT(&sc->paths[i]);125126/* Initialize alias hash table */127sc->num_aliases = 0;128for (size_t i = 0; i < nitems(sc->aliases); i++)129LIST_INIT(&sc->aliases[i]);130131/* Retain the NVRAM data */132sc->data = bhnd_nvram_data_retain(data);133sc->data_caps = bhnd_nvram_data_caps(data);134sc->data_opts = bhnd_nvram_data_options(data);135if (sc->data_opts != NULL) {136bhnd_nvram_plist_retain(sc->data_opts);137} else {138sc->data_opts = bhnd_nvram_plist_new();139if (sc->data_opts == NULL) {140error = ENOMEM;141goto cleanup;142}143}144145/* Register required root path */146error = bhnd_nvstore_register_path(sc, BHND_NVSTORE_ROOT_PATH,147BHND_NVSTORE_ROOT_PATH_LEN);148if (error)149goto cleanup;150151sc->root_path = bhnd_nvstore_get_path(sc, BHND_NVSTORE_ROOT_PATH,152BHND_NVSTORE_ROOT_PATH_LEN);153BHND_NV_ASSERT(sc->root_path, ("missing root path"));154155/* Parse all variables vended by our backing NVRAM data instance,156* generating all path entries, alias entries, and variable indexes */157if ((error = bhnd_nvstore_parse_data(sc)))158goto cleanup;159160*store = sc;161162BHND_NVSTORE_UNLOCK(sc);163return (0);164165cleanup:166BHND_NVSTORE_UNLOCK(sc);167bhnd_nvram_store_free(sc);168return (error);169}170171/**172* Allocate and initialize a new NVRAM data store instance, parsing the173* NVRAM data from @p io.174*175* The caller is responsible for deallocating the instance via176* bhnd_nvram_store_free().177*178* The NVRAM data mapped by @p io will be copied, and @p io may be safely179* deallocated after bhnd_nvram_store_new() returns.180*181* @param[out] store On success, a pointer to the newly allocated NVRAM data182* instance.183* @param io An I/O context mapping the NVRAM data to be copied and parsed.184* @param cls The NVRAM data class to be used when parsing @p io, or NULL185* to perform runtime identification of the appropriate data class.186*187* @retval 0 success188* @retval non-zero if an error occurs during allocation or initialization, a189* regular unix error code will be returned.190*/191int192bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store,193struct bhnd_nvram_io *io, bhnd_nvram_data_class *cls)194{195struct bhnd_nvram_data *data;196int error;197198/* Try to parse the data */199if ((error = bhnd_nvram_data_new(cls, &data, io)))200return (error);201202/* Try to create our new store instance */203error = bhnd_nvram_store_new(store, data);204bhnd_nvram_data_release(data);205206return (error);207}208209/**210* Free an NVRAM store instance, releasing all associated resources.211*212* @param sc A store instance previously allocated via213* bhnd_nvram_store_new().214*/215void216bhnd_nvram_store_free(struct bhnd_nvram_store *sc)217{218219/* Clean up alias hash table */220for (size_t i = 0; i < nitems(sc->aliases); i++) {221bhnd_nvstore_alias *alias, *anext;222LIST_FOREACH_SAFE(alias, &sc->aliases[i], na_link, anext)223bhnd_nv_free(alias);224}225226/* Clean up path hash table */227for (size_t i = 0; i < nitems(sc->paths); i++) {228bhnd_nvstore_path *path, *pnext;229LIST_FOREACH_SAFE(path, &sc->paths[i], np_link, pnext)230bhnd_nvstore_path_free(path);231}232233if (sc->data != NULL)234bhnd_nvram_data_release(sc->data);235236if (sc->data_opts != NULL)237bhnd_nvram_plist_release(sc->data_opts);238239BHND_NVSTORE_LOCK_DESTROY(sc);240bhnd_nv_free(sc);241}242243/**244* Parse all variables vended by our backing NVRAM data instance,245* generating all path entries, alias entries, and variable indexes.246*247* @param sc The NVRAM store instance to be initialized with248* paths, aliases, and data parsed from its backing249* data.250*251* @retval 0 success252* @retval non-zero if an error occurs during parsing, a regular unix error253* code will be returned.254*/255static int256bhnd_nvstore_parse_data(struct bhnd_nvram_store *sc)257{258const char *name;259void *cookiep;260int error;261262/* Parse and register all device paths and path aliases. This enables263* resolution of _forward_ references to device paths aliases when264* scanning variable entries below */265if ((error = bhnd_nvstore_parse_path_entries(sc)))266return (error);267268/* Calculate the per-path variable counts, and report dangling alias269* references as an error. */270cookiep = NULL;271while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {272bhnd_nvstore_path *path;273bhnd_nvstore_name_info info;274275/* Parse the name info */276error = bhnd_nvstore_parse_name_info(name,277BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);278if (error)279return (error);280281switch (info.type) {282case BHND_NVSTORE_VAR:283/* Fetch referenced path */284path = bhnd_nvstore_var_get_path(sc, &info);285if (path == NULL) {286BHND_NV_LOG("variable '%s' has dangling "287"path reference\n", name);288return (EFTYPE);289}290291/* Increment path variable count */292if (path->num_vars == SIZE_MAX) {293BHND_NV_LOG("more than SIZE_MAX variables in "294"path %s\n", path->path_str);295return (EFTYPE);296}297path->num_vars++;298break;299300case BHND_NVSTORE_ALIAS_DECL:301/* Skip -- path alias already parsed and recorded */302break;303}304}305306/* If the backing NVRAM data instance vends only a single root ("/")307* path, we may be able to skip generating an index for the root308* path */309if (sc->num_paths == 1) {310bhnd_nvstore_path *path;311312/* If the backing instance provides its own name-based lookup313* indexing, we can skip generating a duplicate here */314if (sc->data_caps & BHND_NVRAM_DATA_CAP_INDEXED)315return (0);316317/* If the sole root path contains fewer variables than the318* minimum indexing threshhold, we do not need to generate an319* index */320path = bhnd_nvstore_get_root_path(sc);321if (path->num_vars < BHND_NV_IDX_VAR_THRESHOLD)322return (0);323}324325/* Allocate per-path index instances */326for (size_t i = 0; i < nitems(sc->paths); i++) {327bhnd_nvstore_path *path;328329LIST_FOREACH(path, &sc->paths[i], np_link) {330path->index = bhnd_nvstore_index_new(path->num_vars);331if (path->index == NULL)332return (ENOMEM);333}334}335336/* Populate per-path indexes */337cookiep = NULL;338while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {339bhnd_nvstore_name_info info;340bhnd_nvstore_path *path;341342/* Parse the name info */343error = bhnd_nvstore_parse_name_info(name,344BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);345if (error)346return (error);347348switch (info.type) {349case BHND_NVSTORE_VAR:350/* Fetch referenced path */351path = bhnd_nvstore_var_get_path(sc, &info);352BHND_NV_ASSERT(path != NULL,353("dangling path reference"));354355/* Append to index */356error = bhnd_nvstore_index_append(sc, path->index,357cookiep);358if (error)359return (error);360break;361362case BHND_NVSTORE_ALIAS_DECL:363/* Skip */364break;365}366}367368/* Prepare indexes for querying */369for (size_t i = 0; i < nitems(sc->paths); i++) {370bhnd_nvstore_path *path;371372LIST_FOREACH(path, &sc->paths[i], np_link) {373error = bhnd_nvstore_index_prepare(sc, path->index);374if (error)375return (error);376}377}378379return (0);380}381382/**383* Parse and register path and path alias entries for all declarations found in384* the NVRAM data backing @p nvram.385*386* @param sc The NVRAM store instance.387*388* @retval 0 success389* @retval non-zero If parsing fails, a regular unix error code will be390* returned.391*/392static int393bhnd_nvstore_parse_path_entries(struct bhnd_nvram_store *sc)394{395const char *name;396void *cookiep;397int error;398399BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);400401/* Skip path registration if the data source does not support device402* paths. */403if (!(sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)) {404BHND_NV_ASSERT(sc->root_path != NULL, ("missing root path"));405return (0);406}407408/* Otherwise, parse and register all paths and path aliases */409cookiep = NULL;410while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {411bhnd_nvstore_name_info info;412413/* Parse the name info */414error = bhnd_nvstore_parse_name_info(name,415BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);416if (error)417return (error);418419/* Register the path */420error = bhnd_nvstore_var_register_path(sc, &info, cookiep);421if (error) {422BHND_NV_LOG("failed to register path for %s: %d\n",423name, error);424return (error);425}426}427428return (0);429}430431/**432* Merge exported per-path variables (uncommitted, committed, or both) into433* the empty @p merged property list.434*435* @param sc The NVRAM store instance.436* @param path The NVRAM path to be exported.437* @param merged The property list to populate with the merged results.438* @param flags Export flags. See BHND_NVSTORE_EXPORT_*.439*440* @retval 0 success441* @retval ENOMEM If allocation fails.442* @retval non-zero If merging the variables defined in @p path otherwise443* fails, a regular unix error code will be returned.444*/445static int446bhnd_nvstore_export_merge(struct bhnd_nvram_store *sc,447bhnd_nvstore_path *path, bhnd_nvram_plist *merged, uint32_t flags)448{449void *cookiep, *idxp;450int error;451452/* Populate merged list with all pending variables */453if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) {454bhnd_nvram_prop *prop;455456prop = NULL;457while ((prop = bhnd_nvram_plist_next(path->pending, prop))) {458/* Skip variables marked for deletion */459if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED)) {460if (bhnd_nvram_prop_is_null(prop))461continue;462}463464/* Append to merged list */465error = bhnd_nvram_plist_append(merged, prop);466if (error)467return (error);468}469}470471/* Skip merging committed variables? */472if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED))473return (0);474475/* Merge in the committed NVRAM variables */476idxp = NULL;477while ((cookiep = bhnd_nvstore_path_data_next(sc, path, &idxp))) {478const char *name;479bhnd_nvram_val *val;480481/* Fetch the variable name */482name = bhnd_nvram_data_getvar_name(sc->data, cookiep);483484/* Trim device path prefix */485if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)486name = bhnd_nvram_trim_path_name(name);487488/* Skip if already defined in pending updates */489if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) {490if (bhnd_nvram_plist_contains(path->pending, name))491continue;492}493494/* Skip if higher precedence value was already defined. This495* may occur if the underlying data store contains duplicate496* keys; iteration will always return the definition with497* the highest precedence first */498if (bhnd_nvram_plist_contains(merged, name))499continue;500501/* Fetch the variable's value representation */502if ((error = bhnd_nvram_data_copy_val(sc->data, cookiep, &val)))503return (error);504505/* Add to path variable list */506error = bhnd_nvram_plist_append_val(merged, name, val);507bhnd_nvram_val_release(val);508if (error)509return (error);510}511512return (0);513}514515/**516* Find a free alias value for @p path, and append the devpathXX alias517* declaration to @p plist.518*519* @param sc The NVRAM store instance.520* @param path The NVRAM path for which a devpath alias521* variable should be produced.522* @param devpath The devpathXX path value for @p path.523* @param plist The property list to which @p path's devpath524* variable will be appended.525* @param[out] alias_val On success, will be set to the alias value526* allocated for @p path.527*528* @retval 0 success529* @retval ENOMEM If allocation fails.530* @retval non-zero If merging the variables defined in @p path otherwise531* fails, a regular unix error code will be returned.532*/533static int534bhnd_nvstore_export_devpath_alias(struct bhnd_nvram_store *sc,535bhnd_nvstore_path *path, const char *devpath, bhnd_nvram_plist *plist,536u_long *alias_val)537{538bhnd_nvstore_alias *alias;539char *pathvar;540int error;541542*alias_val = 0;543544/* Prefer alias value already reserved for this path. */545alias = bhnd_nvstore_find_alias(sc, path->path_str);546if (alias != NULL) {547*alias_val = alias->alias;548549/* Allocate devpathXX variable name */550bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);551if (pathvar == NULL)552return (ENOMEM);553554/* Append alias variable to property list */555error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);556557BHND_NV_ASSERT(error != EEXIST, ("reserved alias %lu:%s in use",558* alias_val, path->path_str));559560bhnd_nv_free(pathvar);561return (error);562}563564/* Find the next free devpathXX alias entry */565while (1) {566/* Skip existing reserved alias values */567while (bhnd_nvstore_get_alias(sc, *alias_val) != NULL) {568if (*alias_val == ULONG_MAX)569return (ENOMEM);570571(*alias_val)++;572}573574/* Allocate devpathXX variable name */575bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);576if (pathvar == NULL)577return (ENOMEM);578579/* If not in-use, we can terminate the search */580if (!bhnd_nvram_plist_contains(plist, pathvar))581break;582583/* Keep searching */584bhnd_nv_free(pathvar);585586if (*alias_val == ULONG_MAX)587return (ENOMEM);588589(*alias_val)++;590}591592/* Append alias variable to property list */593error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);594595bhnd_nv_free(pathvar);596return (error);597}598599/**600* Export a single @p child path's properties, appending the result to @p plist.601*602* @param sc The NVRAM store instance.603* @param top The root NVRAM path being exported.604* @param child The NVRAM path to be exported.605* @param plist The property list to which @p child's exported606* properties should be appended.607* @param flags Export flags. See BHND_NVSTORE_EXPORT_*.608*609* @retval 0 success610* @retval ENOMEM If allocation fails.611* @retval non-zero If merging the variables defined in @p path otherwise612* fails, a regular unix error code will be returned.613*/614static int615bhnd_nvram_store_export_child(struct bhnd_nvram_store *sc,616bhnd_nvstore_path *top, bhnd_nvstore_path *child, bhnd_nvram_plist *plist,617uint32_t flags)618{619bhnd_nvram_plist *path_vars;620bhnd_nvram_prop *prop;621const char *relpath;622char *prefix, *namebuf;623size_t prefix_len, relpath_len;624size_t namebuf_size;625bool emit_compact_devpath;626int error;627628BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);629630prefix = NULL;631path_vars = NULL;632namebuf = NULL;633634/* Determine the path relative to the top-level path */635relpath = bhnd_nvstore_parse_relpath(top->path_str, child->path_str);636if (relpath == NULL) {637/* Skip -- not a child of the root path */638return (0);639}640relpath_len = strlen(relpath);641642/* Skip sub-path if export of children was not requested, */643if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_CHILDREN) && relpath_len > 0)644return (0);645646/* Collect all variables to be included in the export */647if ((path_vars = bhnd_nvram_plist_new()) == NULL)648return (ENOMEM);649650if ((error = bhnd_nvstore_export_merge(sc, child, path_vars, flags))) {651bhnd_nvram_plist_release(path_vars);652return (error);653}654655/* Skip if no children are to be exported */656if (bhnd_nvram_plist_count(path_vars) == 0) {657bhnd_nvram_plist_release(path_vars);658return (0);659}660661/* Determine appropriate device path encoding */662emit_compact_devpath = false;663if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS)) {664/* Re-encode as compact (if non-empty path) */665if (relpath_len > 0)666emit_compact_devpath = true;667} else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) {668/* Re-encode with fully expanded device path */669emit_compact_devpath = false;670} else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) {671/* Preserve existing encoding of this path */672if (bhnd_nvstore_find_alias(sc, child->path_str) != NULL)673emit_compact_devpath = true;674} else {675BHND_NV_LOG("invalid device path flag: %#" PRIx32, flags);676error = EINVAL;677goto finished;678}679680/* Allocate variable device path prefix to use for all property names,681* and if using compact encoding, emit the devpathXX= variable */682prefix = NULL;683prefix_len = 0;684if (emit_compact_devpath) {685u_long alias_val;686int len;687688/* Reserve an alias value and append the devpathXX= variable to689* the property list */690error = bhnd_nvstore_export_devpath_alias(sc, child, relpath,691plist, &alias_val);692if (error)693goto finished;694695/* Allocate variable name prefix */696len = bhnd_nv_asprintf(&prefix, "%lu:", alias_val);697if (prefix == NULL) {698error = ENOMEM;699goto finished;700}701702prefix_len = len;703} else if (relpath_len > 0) {704int len;705706/* Allocate the variable name prefix, appending '/' to the707* relative path */708len = bhnd_nv_asprintf(&prefix, "%s/", relpath);709if (prefix == NULL) {710error = ENOMEM;711goto finished;712}713714prefix_len = len;715}716717/* If prefixing of variable names is required, allocate a name718* formatting buffer */719namebuf_size = 0;720if (prefix != NULL) {721size_t maxlen;722723/* Find the maximum name length */724maxlen = 0;725prop = NULL;726while ((prop = bhnd_nvram_plist_next(path_vars, prop))) {727const char *name;728729name = bhnd_nvram_prop_name(prop);730maxlen = bhnd_nv_ummax(strlen(name), maxlen);731}732733/* Allocate name buffer (path-prefix + name + '\0') */734namebuf_size = prefix_len + maxlen + 1;735namebuf = bhnd_nv_malloc(namebuf_size);736if (namebuf == NULL) {737error = ENOMEM;738goto finished;739}740}741742/* Append all path variables to the export plist, prepending the743* device-path prefix to the variable names, if required */744prop = NULL;745while ((prop = bhnd_nvram_plist_next(path_vars, prop)) != NULL) {746const char *name;747748/* Prepend device prefix to the variable name */749name = bhnd_nvram_prop_name(prop);750if (prefix != NULL) {751int len;752753/*754* Write prefixed variable name to our name buffer.755*756* We precalcuate the size when scanning all names757* above, so this should always succeed.758*/759len = snprintf(namebuf, namebuf_size, "%s%s", prefix,760name);761if (len < 0 || (size_t)len >= namebuf_size)762BHND_NV_PANIC("invalid max_name_len");763764name = namebuf;765}766767/* Add property to export plist */768error = bhnd_nvram_plist_append_val(plist, name,769bhnd_nvram_prop_val(prop));770if (error)771goto finished;772}773774/* Success */775error = 0;776777finished:778if (prefix != NULL)779bhnd_nv_free(prefix);780781if (namebuf != NULL)782bhnd_nv_free(namebuf);783784if (path_vars != NULL)785bhnd_nvram_plist_release(path_vars);786787return (error);788}789790/**791* Export a flat, ordered NVRAM property list representation of all NVRAM792* properties at @p path.793*794* @param sc The NVRAM store instance.795* @param path The NVRAM path to export, or NULL to select the root796* path.797* @param[out] cls On success, will be set to the backing data class798* of @p sc. If the data class is are not desired,799* a NULL pointer may be provided.800* @param[out] props On success, will be set to a caller-owned property801* list containing the exported properties. The caller is802* responsible for releasing this value via803* bhnd_nvram_plist_release().804* @param[out] options On success, will be set to a caller-owned property805* list containing the current NVRAM serialization options806* for @p sc. The caller is responsible for releasing this807* value via bhnd_nvram_plist_release().808* @param flags Export flags. See BHND_NVSTORE_EXPORT_*.809*810* @retval 0 success811* @retval EINVAL If @p flags is invalid.812* @retval ENOENT The requested path was not found.813* @retval ENOMEM If allocation fails.814* @retval non-zero If export of @p path otherwise fails, a regular unix815* error code will be returned.816*/817int818bhnd_nvram_store_export(struct bhnd_nvram_store *sc, const char *path,819bhnd_nvram_data_class **cls, bhnd_nvram_plist **props,820bhnd_nvram_plist **options, uint32_t flags)821{822bhnd_nvram_plist *unordered;823bhnd_nvstore_path *top;824bhnd_nvram_prop *prop;825const char *name;826void *cookiep;827size_t num_dpath_flags;828int error;829830*props = NULL;831unordered = NULL;832num_dpath_flags = 0;833if (options != NULL)834*options = NULL;835836/* Default to exporting root path */837if (path == NULL)838path = BHND_NVSTORE_ROOT_PATH;839840/* Default to exporting all properties */841if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED) &&842!BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED))843{844flags |= BHND_NVSTORE_EXPORT_ALL_VARS;845}846847/* Default to preserving the current device path encoding */848if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS) &&849!BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS))850{851flags |= BHND_NVSTORE_EXPORT_PRESERVE_DEVPATHS;852}853854/* Exactly one device path encoding flag must be set */855if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS))856num_dpath_flags++;857858if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS))859num_dpath_flags++;860861if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS))862num_dpath_flags++;863864if (num_dpath_flags != 1)865return (EINVAL);866867/* If EXPORT_DELETED is set, EXPORT_UNCOMMITTED must be set too */868if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED) &&869!BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED))870{871return (EINVAL);872}873874/* Lock internal state before querying paths/properties */875BHND_NVSTORE_LOCK(sc);876877/* Fetch referenced path */878top = bhnd_nvstore_get_path(sc, path, strlen(path));879if (top == NULL) {880error = ENOENT;881goto failed;882}883884/* Allocate new, empty property list */885if ((unordered = bhnd_nvram_plist_new()) == NULL) {886error = ENOMEM;887goto failed;888}889890/* Export the top-level path first */891error = bhnd_nvram_store_export_child(sc, top, top, unordered, flags);892if (error)893goto failed;894895/* Attempt to export any children of the root path */896for (size_t i = 0; i < nitems(sc->paths); i++) {897bhnd_nvstore_path *child;898899LIST_FOREACH(child, &sc->paths[i], np_link) {900/* Top-level path was already exported */901if (child == top)902continue;903904error = bhnd_nvram_store_export_child(sc, top,905child, unordered, flags);906if (error)907goto failed;908}909}910911/* If requested, provide the current class and serialization options */912if (cls != NULL)913*cls = bhnd_nvram_data_get_class(sc->data);914915if (options != NULL)916*options = bhnd_nvram_plist_retain(sc->data_opts);917918/*919* If we're re-encoding device paths, don't bother preserving the920* existing NVRAM variable order; our variable names will not match921* the existing backing NVRAM data.922*/923if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) {924*props = unordered;925unordered = NULL;926927goto finished;928}929930/*931* Re-order the flattened output to match the existing NVRAM variable932* ordering.933*934* We append all new variables at the end of the input; this should935* reduce the delta that needs to be written (e.g. to flash) when936* committing NVRAM updates, and should result in a serialization937* identical to the input serialization if uncommitted updates are938* excluded from the export.939*/940if ((*props = bhnd_nvram_plist_new()) == NULL) {941error = ENOMEM;942goto failed;943}944945/* Using the backing NVRAM data ordering to order all variables946* currently defined in the backing store */947cookiep = NULL;948while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {949prop = bhnd_nvram_plist_get_prop(unordered, name);950if (prop == NULL)951continue;952953/* Append to ordered result */954if ((error = bhnd_nvram_plist_append(*props, prop)))955goto failed;956957/* Remove from unordered list */958bhnd_nvram_plist_remove(unordered, name);959}960961/* Any remaining variables are new, and should be appended to the962* end of the export list */963prop = NULL;964while ((prop = bhnd_nvram_plist_next(unordered, prop)) != NULL) {965if ((error = bhnd_nvram_plist_append(*props, prop)))966goto failed;967}968969/* Export complete */970finished:971BHND_NVSTORE_UNLOCK(sc);972973if (unordered != NULL)974bhnd_nvram_plist_release(unordered);975976return (0);977978failed:979BHND_NVSTORE_UNLOCK(sc);980981if (unordered != NULL)982bhnd_nvram_plist_release(unordered);983984if (options != NULL && *options != NULL)985bhnd_nvram_plist_release(*options);986987if (*props != NULL)988bhnd_nvram_plist_release(*props);989990return (error);991}992993/**994* Encode all NVRAM properties at @p path, using the @p store's current NVRAM995* data format.996*997* @param sc The NVRAM store instance.998* @param path The NVRAM path to export, or NULL to select the root999* path.1000* @param[out] data On success, will be set to the newly serialized value.1001* The caller is responsible for freeing this value1002* via bhnd_nvram_io_free().1003* @param flags Export flags. See BHND_NVSTORE_EXPORT_*.1004*1005* @retval 0 success1006* @retval EINVAL If @p flags is invalid.1007* @retval ENOENT The requested path was not found.1008* @retval ENOMEM If allocation fails.1009* @retval non-zero If serialization of @p path otherwise fails, a regular1010* unix error code will be returned.1011*/1012int1013bhnd_nvram_store_serialize(struct bhnd_nvram_store *sc, const char *path,1014struct bhnd_nvram_io **data, uint32_t flags)1015{1016bhnd_nvram_plist *props;1017bhnd_nvram_plist *options;1018bhnd_nvram_data_class *cls;1019struct bhnd_nvram_io *io;1020void *outp;1021size_t olen;1022int error;10231024props = NULL;1025options = NULL;1026io = NULL;10271028/* Perform requested export */1029error = bhnd_nvram_store_export(sc, path, &cls, &props, &options,1030flags);1031if (error)1032return (error);10331034/* Determine serialized size */1035error = bhnd_nvram_data_serialize(cls, props, options, NULL, &olen);1036if (error)1037goto failed;10381039/* Allocate output buffer */1040if ((io = bhnd_nvram_iobuf_empty(olen, olen)) == NULL) {1041error = ENOMEM;1042goto failed;1043}10441045/* Fetch write pointer */1046if ((error = bhnd_nvram_io_write_ptr(io, 0, &outp, olen, NULL)))1047goto failed;10481049/* Perform serialization */1050error = bhnd_nvram_data_serialize(cls, props, options, outp, &olen);1051if (error)1052goto failed;10531054if ((error = bhnd_nvram_io_setsize(io, olen)))1055goto failed;10561057/* Success */1058bhnd_nvram_plist_release(props);1059bhnd_nvram_plist_release(options);10601061*data = io;1062return (0);10631064failed:1065if (props != NULL)1066bhnd_nvram_plist_release(props);10671068if (options != NULL)1069bhnd_nvram_plist_release(options);10701071if (io != NULL)1072bhnd_nvram_io_free(io);10731074return (error);1075}10761077/**1078* Read an NVRAM variable.1079*1080* @param sc The NVRAM parser state.1081* @param name The NVRAM variable name.1082* @param[out] outp On success, the requested value will be written1083* to this buffer. This argment may be NULL if1084* the value is not desired.1085* @param[in,out] olen The capacity of @p outp. On success, will be set1086* to the actual size of the requested value.1087* @param otype The requested data type to be written to1088* @p outp.1089*1090* @retval 0 success1091* @retval ENOENT The requested variable was not found.1092* @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too1093* small to hold the requested value.1094* @retval non-zero If reading @p name otherwise fails, a regular unix1095* error code will be returned.1096*/1097int1098bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,1099void *outp, size_t *olen, bhnd_nvram_type otype)1100{1101bhnd_nvstore_name_info info;1102bhnd_nvstore_path *path;1103bhnd_nvram_prop *prop;1104void *cookiep;1105int error;11061107BHND_NVSTORE_LOCK(sc);11081109/* Parse the variable name */1110error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,1111sc->data_caps, &info);1112if (error)1113goto finished;11141115/* Fetch the variable's enclosing path entry */1116if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL) {1117error = ENOENT;1118goto finished;1119}11201121/* Search uncommitted updates first */1122prop = bhnd_nvstore_path_get_update(sc, path, info.name);1123if (prop != NULL) {1124if (bhnd_nvram_prop_is_null(prop)) {1125/* NULL denotes a pending deletion */1126error = ENOENT;1127} else {1128error = bhnd_nvram_prop_encode(prop, outp, olen, otype);1129}1130goto finished;1131}11321133/* Search the backing NVRAM data */1134cookiep = bhnd_nvstore_path_data_lookup(sc, path, info.name);1135if (cookiep != NULL) {1136/* Found in backing store */1137error = bhnd_nvram_data_getvar(sc->data, cookiep, outp, olen,1138otype);1139goto finished;1140}11411142/* Not found */1143error = ENOENT;11441145finished:1146BHND_NVSTORE_UNLOCK(sc);1147return (error);1148}11491150/**1151* Common bhnd_nvram_store_set*() and bhnd_nvram_store_unsetvar()1152* implementation.1153*1154* If @p value is NULL, the variable will be marked for deletion.1155*/1156static int1157bhnd_nvram_store_setval_common(struct bhnd_nvram_store *sc, const char *name,1158bhnd_nvram_val *value)1159{1160bhnd_nvstore_path *path;1161bhnd_nvstore_name_info info;1162int error;11631164BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);11651166/* Parse the variable name */1167error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,1168sc->data_caps, &info);1169if (error)1170return (error);11711172/* Fetch the variable's enclosing path entry */1173if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL)1174return (error);11751176/* Register the update entry */1177return (bhnd_nvstore_path_register_update(sc, path, info.name, value));1178}11791180/**1181* Set an NVRAM variable.1182*1183* @param sc The NVRAM parser state.1184* @param name The NVRAM variable name.1185* @param value The new value.1186*1187* @retval 0 success1188* @retval ENOENT The requested variable @p name was not found.1189* @retval EINVAL If @p value is invalid.1190*/1191int1192bhnd_nvram_store_setval(struct bhnd_nvram_store *sc, const char *name,1193bhnd_nvram_val *value)1194{1195int error;11961197BHND_NVSTORE_LOCK(sc);1198error = bhnd_nvram_store_setval_common(sc, name, value);1199BHND_NVSTORE_UNLOCK(sc);12001201return (error);1202}12031204/**1205* Set an NVRAM variable.1206*1207* @param sc The NVRAM parser state.1208* @param name The NVRAM variable name.1209* @param[out] inp The new value.1210* @param[in,out] ilen The size of @p inp.1211* @param itype The data type of @p inp.1212*1213* @retval 0 success1214* @retval ENOENT The requested variable @p name was not found.1215* @retval EINVAL If the new value is invalid.1216* @retval EINVAL If @p name is read-only.1217*/1218int1219bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name,1220const void *inp, size_t ilen, bhnd_nvram_type itype)1221{1222bhnd_nvram_val val;1223int error;12241225error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype,1226BHND_NVRAM_VAL_FIXED|BHND_NVRAM_VAL_BORROW_DATA);1227if (error) {1228BHND_NV_LOG("error initializing value: %d\n", error);1229return (EINVAL);1230}12311232BHND_NVSTORE_LOCK(sc);1233error = bhnd_nvram_store_setval_common(sc, name, &val);1234BHND_NVSTORE_UNLOCK(sc);12351236bhnd_nvram_val_release(&val);12371238return (error);1239}12401241/**1242* Unset an NVRAM variable.1243*1244* @param sc The NVRAM parser state.1245* @param name The NVRAM variable name.1246*1247* @retval 0 success1248* @retval ENOENT The requested variable @p name was not found.1249* @retval EINVAL If @p name is read-only.1250*/1251int1252bhnd_nvram_store_unsetvar(struct bhnd_nvram_store *sc, const char *name)1253{1254int error;12551256BHND_NVSTORE_LOCK(sc);1257error = bhnd_nvram_store_setval_common(sc, name, BHND_NVRAM_VAL_NULL);1258BHND_NVSTORE_UNLOCK(sc);12591260return (error);1261}126212631264