Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.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/queue.h>3233#ifdef _KERNEL3435#include <sys/ctype.h>36#include <sys/systm.h>3738#include <machine/_inttypes.h>3940#else /* !_KERNEL */4142#include <ctype.h>43#include <errno.h>44#include <inttypes.h>45#include <stdbool.h>46#include <stdio.h>47#include <stdint.h>48#include <stdlib.h>49#include <string.h>5051#endif /* _KERNEL */5253#include "bhnd_nvram_private.h"54#include "bhnd_nvram_datavar.h"5556#include "bhnd_nvram_storevar.h"5758static int bhnd_nvstore_idx_cmp(const void *lhs, const void *rhs, void *ctx);5960/**61* Allocate and initialize a new path instance.62*63* The caller is responsible for deallocating the instance via64* bhnd_nvstore_path_free().65*66* @param path_str The path's canonical string representation.67* @param path_len The length of @p path_str.68*69* @retval non-NULL success70* @retval NULL if allocation fails.71*/72bhnd_nvstore_path *73bhnd_nvstore_path_new(const char *path_str, size_t path_len)74{75bhnd_nvstore_path *path;7677/* Allocate new entry */78path = bhnd_nv_malloc(sizeof(*path));79if (path == NULL)80return (NULL);8182path->index = NULL;83path->num_vars = 0;8485path->pending = bhnd_nvram_plist_new();86if (path->pending == NULL)87goto failed;8889path->path_str = bhnd_nv_strndup(path_str, path_len);90if (path->path_str == NULL)91goto failed;9293return (path);9495failed:96if (path->pending != NULL)97bhnd_nvram_plist_release(path->pending);9899if (path->path_str != NULL)100bhnd_nv_free(path->path_str);101102bhnd_nv_free(path);103104return (NULL);105}106107/**108* Free an NVRAM path instance, releasing all associated resources.109*/110void111bhnd_nvstore_path_free(struct bhnd_nvstore_path *path)112{113/* Free the per-path index */114if (path->index != NULL)115bhnd_nvstore_index_free(path->index);116117bhnd_nvram_plist_release(path->pending);118bhnd_nv_free(path->path_str);119bhnd_nv_free(path);120}121122/**123* Allocate and initialize a new index instance with @p capacity.124*125* The caller is responsible for deallocating the instance via126* bhnd_nvstore_index_free().127*128* @param capacity The maximum number of variables to be indexed.129*130* @retval non-NULL success131* @retval NULL if allocation fails.132*/133bhnd_nvstore_index *134bhnd_nvstore_index_new(size_t capacity)135{136bhnd_nvstore_index *index;137size_t bytes;138139/* Allocate and populate variable index */140bytes = sizeof(struct bhnd_nvstore_index) + (sizeof(void *) * capacity);141index = bhnd_nv_malloc(bytes);142if (index == NULL) {143BHND_NV_LOG("error allocating %zu byte index\n", bytes);144return (NULL);145}146147index->count = 0;148index->capacity = capacity;149150return (index);151}152153/**154* Free an index instance, releasing all associated resources.155*156* @param index An index instance previously allocated via157* bhnd_nvstore_index_new().158*/159void160bhnd_nvstore_index_free(bhnd_nvstore_index *index)161{162bhnd_nv_free(index);163}164165/**166* Append a new NVRAM variable's @p cookiep value to @p index.167*168* After one or more append requests, the index must be prepared via169* bhnd_nvstore_index_prepare() before any indexed lookups are performed.170*171* @param sc The NVRAM store from which NVRAM values will be queried.172* @param index The index to be modified.173* @param cookiep The cookiep value (as provided by the backing NVRAM174* data instance of @p sc) to be included in @p index.175*176* @retval 0 success177* @retval ENOMEM if appending an additional entry would exceed the178* capacity of @p index.179*/180int181bhnd_nvstore_index_append(struct bhnd_nvram_store *sc,182bhnd_nvstore_index *index, void *cookiep)183{184BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);185186if (index->count >= index->capacity)187return (ENOMEM);188189index->cookiep[index->count] = cookiep;190index->count++;191return (0);192}193194/* sort function for bhnd_nvstore_index_prepare() */195static int196bhnd_nvstore_idx_cmp(const void *lhs, const void *rhs, void *ctx)197{198struct bhnd_nvram_store *sc;199void *l_cookiep, *r_cookiep;200const char *l_str, *r_str;201const char *l_name, *r_name;202int order;203204sc = ctx;205l_cookiep = *(void * const *)lhs;206r_cookiep = *(void * const *)rhs;207208BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);209210/* Fetch string pointers from the cookiep values */211l_str = bhnd_nvram_data_getvar_name(sc->data, l_cookiep);212r_str = bhnd_nvram_data_getvar_name(sc->data, r_cookiep);213214/* Trim device path prefixes */215if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) {216l_name = bhnd_nvram_trim_path_name(l_str);217r_name = bhnd_nvram_trim_path_name(r_str);218} else {219l_name = l_str;220r_name = r_str;221}222223/* Perform comparison */224order = strcmp(l_name, r_name);225if (order != 0 || lhs == rhs)226return (order);227228/* If the backing data incorrectly contains variables with duplicate229* names, we need a sort order that provides stable behavior.230*231* Since Broadcom's own code varies wildly on this question, we just232* use a simple precedence rule: The first declaration of a variable233* takes precedence. */234return (bhnd_nvram_data_getvar_order(sc->data, l_cookiep, r_cookiep));235}236237/**238* Prepare @p index for querying via bhnd_nvstore_index_lookup().239*240* After one or more append requests, the index must be prepared via241* bhnd_nvstore_index_prepare() before any indexed lookups are performed.242*243* @param sc The NVRAM store from which NVRAM values will be queried.244* @param index The index to be prepared.245*246* @retval 0 success247* @retval non-zero if preparing @p index otherwise fails, a regular unix248* error code will be returned.249*/250int251bhnd_nvstore_index_prepare(struct bhnd_nvram_store *sc,252bhnd_nvstore_index *index)253{254BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);255256/* Sort the index table */257qsort_r(index->cookiep, index->count, sizeof(index->cookiep[0]),258bhnd_nvstore_idx_cmp, sc);259260return (0);261}262263/**264* Return a borrowed reference to the root path node.265*266* @param sc The NVRAM store.267*/268bhnd_nvstore_path *269bhnd_nvstore_get_root_path(struct bhnd_nvram_store *sc)270{271BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);272return (sc->root_path);273}274275/**276* Return true if @p path is the root path node.277*278* @param sc The NVRAM store.279* @param path The path to query.280*/281bool282bhnd_nvstore_is_root_path(struct bhnd_nvram_store *sc, bhnd_nvstore_path *path)283{284BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);285return (sc->root_path == path);286}287288/**289* Return the update entry matching @p name in @p path, or NULL if no entry290* found.291*292* @param sc The NVRAM store.293* @param path The path to query.294* @param name The NVRAM variable name to search for in @p path's update list.295*296* @retval non-NULL success297* @retval NULL if @p name is not found in @p path.298*/299bhnd_nvram_prop *300bhnd_nvstore_path_get_update(struct bhnd_nvram_store *sc,301bhnd_nvstore_path *path, const char *name)302{303BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);304return (bhnd_nvram_plist_get_prop(path->pending, name));305}306307/**308* Register or remove an update record for @p name in @p path.309*310* @param sc The NVRAM store.311* @param path The path to be modified.312* @param name The path-relative variable name to be modified.313* @param value The new value. A value of BHND_NVRAM_TYPE_NULL denotes deletion.314*315* @retval 0 success316* @retval ENOMEM if allocation fails.317* @retval ENOENT if @p name is unknown.318* @retval EINVAL if @p value is NULL, and deletion of @p is not319* supported.320* @retval EINVAL if @p value cannot be converted to a supported value321* type.322*/323int324bhnd_nvstore_path_register_update(struct bhnd_nvram_store *sc,325bhnd_nvstore_path *path, const char *name, bhnd_nvram_val *value)326{327bhnd_nvram_val *prop_val;328const char *full_name;329void *cookiep;330char *namebuf;331int error;332bool nvram_committed;333334namebuf = NULL;335prop_val = NULL;336337/* Determine whether the variable is currently defined in the338* backing NVRAM data, and derive its full path-prefixed name */339nvram_committed = false;340cookiep = bhnd_nvstore_path_data_lookup(sc, path, name);341if (cookiep != NULL) {342/* Variable is defined in the backing data */343nvram_committed = true;344345/* Use the existing variable name */346full_name = bhnd_nvram_data_getvar_name(sc->data, cookiep);347} else if (path == sc->root_path) {348/* No prefix required for root path */349full_name = name;350} else {351bhnd_nvstore_alias *alias;352int len;353354/* New variable is being set; we need to determine the355* appropriate path prefix */356alias = bhnd_nvstore_find_alias(sc, path->path_str);357if (alias != NULL) {358/* Use <alias>:name */359len = bhnd_nv_asprintf(&namebuf, "%lu:%s", alias->alias,360name);361} else {362/* Use path/name */363len = bhnd_nv_asprintf(&namebuf, "%s/%s",364path->path_str, name);365}366367if (len < 0)368return (ENOMEM);369370full_name = namebuf;371}372373/* Allow the data store to filter the NVRAM operation */374if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) {375error = bhnd_nvram_data_filter_unsetvar(sc->data, full_name);376if (error) {377BHND_NV_LOG("cannot unset property %s: %d\n", full_name,378error);379goto cleanup;380}381382if ((prop_val = bhnd_nvram_val_copy(value)) == NULL) {383error = ENOMEM;384goto cleanup;385}386} else {387error = bhnd_nvram_data_filter_setvar(sc->data, full_name,388value, &prop_val);389if (error) {390BHND_NV_LOG("cannot set property %s: %d\n", full_name,391error);392goto cleanup;393}394}395396/* Add relative variable name to the per-path update list */397if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL &&398!nvram_committed)399{400/* This is a deletion request for a variable not defined in401* out backing store; we can simply remove the corresponding402* update entry. */403bhnd_nvram_plist_remove(path->pending, name);404} else {405/* Update or append a pending update entry */406error = bhnd_nvram_plist_replace_val(path->pending, name,407prop_val);408if (error)409goto cleanup;410}411412/* Success */413error = 0;414415cleanup:416if (namebuf != NULL)417bhnd_nv_free(namebuf);418419if (prop_val != NULL)420bhnd_nvram_val_release(prop_val);421422return (error);423}424425/**426* Iterate over all variable cookiep values retrievable from the backing427* data store in @p path.428*429* @warning Pending updates in @p path are ignored by this function.430*431* @param sc The NVRAM store.432* @param path The NVRAM path to be iterated.433* @param[in,out] indexp A pointer to an opaque indexp value previously434* returned by bhnd_nvstore_path_data_next(), or a435* NULL value to begin iteration.436*437* @return Returns the next variable name, or NULL if there are no more438* variables defined in @p path.439*/440void *441bhnd_nvstore_path_data_next(struct bhnd_nvram_store *sc,442bhnd_nvstore_path *path, void **indexp)443{444void **index_ref;445446BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);447448/* No index */449if (path->index == NULL) {450/* An index is required for all non-empty, non-root path451* instances */452BHND_NV_ASSERT(bhnd_nvstore_is_root_path(sc, path),453("missing index for non-root path %s", path->path_str));454455/* Iterate NVRAM data directly, using the NVRAM data's cookiep456* value as our indexp context */457if ((bhnd_nvram_data_next(sc->data, indexp)) == NULL)458return (NULL);459460return (*indexp);461}462463/* Empty index */464if (path->index->count == 0)465return (NULL);466467if (*indexp == NULL) {468/* First index entry */469index_ref = &path->index->cookiep[0];470} else {471size_t idxpos;472473/* Advance to next index entry */474index_ref = *indexp;475index_ref++;476477/* Hit end of index? */478BHND_NV_ASSERT(index_ref > path->index->cookiep,479("invalid indexp"));480481idxpos = (index_ref - path->index->cookiep);482if (idxpos >= path->index->count)483return (NULL);484}485486/* Provide new index position */487*indexp = index_ref;488489/* Return the data's cookiep value */490return (*index_ref);491}492493/**494* Perform an lookup of @p name in the backing NVRAM data for @p path,495* returning the associated cookiep value, or NULL if the variable is not found496* in the backing NVRAM data.497*498* @warning Pending updates in @p path are ignored by this function.499*500* @param sc The NVRAM store from which NVRAM values will be queried.501* @param path The path to be queried.502* @param name The variable name to be queried.503*504* @retval non-NULL success505* @retval NULL if @p name is not found in @p index.506*/507void *508bhnd_nvstore_path_data_lookup(struct bhnd_nvram_store *sc,509bhnd_nvstore_path *path, const char *name)510{511BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);512513/* No index */514if (path->index == NULL) {515/* An index is required for all non-empty, non-root path516* instances */517BHND_NV_ASSERT(bhnd_nvstore_is_root_path(sc, path),518("missing index for non-root path %s", path->path_str));519520/* Look up directly in NVRAM data */521return (bhnd_nvram_data_find(sc->data, name));522}523524/* Otherwise, delegate to an index-based lookup */525return (bhnd_nvstore_index_lookup(sc, path->index, name));526}527528/**529* Perform an index lookup of @p name, returning the associated cookiep530* value, or NULL if the variable does not exist.531*532* @param sc The NVRAM store from which NVRAM values will be queried.533* @param index The index to be queried.534* @param name The variable name to be queried.535*536* @retval non-NULL success537* @retval NULL if @p name is not found in @p index.538*/539void *540bhnd_nvstore_index_lookup(struct bhnd_nvram_store *sc,541bhnd_nvstore_index *index, const char *name)542{543void *cookiep;544const char *indexed_name;545size_t min, mid, max;546uint32_t data_caps;547int order;548549BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);550BHND_NV_ASSERT(index != NULL, ("NULL index"));551552/*553* Locate the requested variable using a binary search.554*/555if (index->count == 0)556return (NULL);557558data_caps = sc->data_caps;559min = 0;560max = index->count - 1;561562while (max >= min) {563/* Select midpoint */564mid = (min + max) / 2;565cookiep = index->cookiep[mid];566567/* Fetch variable name */568indexed_name = bhnd_nvram_data_getvar_name(sc->data, cookiep);569570/* Trim any path prefix */571if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)572indexed_name = bhnd_nvram_trim_path_name(indexed_name);573574/* Determine which side of the partition to search */575order = strcmp(indexed_name, name);576if (order < 0) {577/* Search upper partition */578min = mid + 1;579} else if (order > 0) {580/* Search (non-empty) lower partition */581if (mid == 0)582break;583max = mid - 1;584} else if (order == 0) {585size_t idx;586587/*588* Match found.589*590* If this happens to be a key with multiple definitions591* in the backing store, we need to find the entry with592* the highest declaration precedence.593*594* Duplicates are sorted in order of descending595* precedence; to find the highest precedence entry,596* we search backwards through the index.597*/598idx = mid;599while (idx > 0) {600void *dup_cookiep;601const char *dup_name;602603/* Fetch preceding index entry */604idx--;605dup_cookiep = index->cookiep[idx];606dup_name = bhnd_nvram_data_getvar_name(sc->data,607dup_cookiep);608609/* Trim any path prefix */610if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) {611dup_name = bhnd_nvram_trim_path_name(612dup_name);613}614615/* If no match, current cookiep is the variable616* definition with the highest precedence */617if (strcmp(indexed_name, dup_name) != 0)618return (cookiep);619620/* Otherwise, prefer this earlier definition,621* and keep searching for a higher-precedence622* definitions */623cookiep = dup_cookiep;624}625626return (cookiep);627}628}629630/* Not found */631return (NULL);632}633634/**635* Return the device path entry registered for @p path, if any.636*637* @param sc The NVRAM store to be queried.638* @param path The device path to search for.639* @param path_len The length of @p path.640*641* @retval non-NULL if found.642* @retval NULL if not found.643*/644bhnd_nvstore_path *645bhnd_nvstore_get_path(struct bhnd_nvram_store *sc, const char *path,646size_t path_len)647{648bhnd_nvstore_path_list *plist;649bhnd_nvstore_path *p;650uint32_t h;651652BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);653654/* Use hash lookup */655h = hash32_strn(path, path_len, HASHINIT);656plist = &sc->paths[h % nitems(sc->paths)];657658LIST_FOREACH(p, plist, np_link) {659/* Check for prefix match */660if (strncmp(p->path_str, path, path_len) != 0)661continue;662663/* Check for complete match */664if (strnlen(path, path_len) != strlen(p->path_str))665continue;666667return (p);668}669670/* Not found */671return (NULL);672}673674/**675* Resolve @p aval to its corresponding device path entry, if any.676*677* @param sc The NVRAM store to be queried.678* @param aval The device path alias value to search for.679*680* @retval non-NULL if found.681* @retval NULL if not found.682*/683bhnd_nvstore_path *684bhnd_nvstore_resolve_path_alias(struct bhnd_nvram_store *sc, u_long aval)685{686bhnd_nvstore_alias *alias;687688BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);689690/* Fetch alias entry */691if ((alias = bhnd_nvstore_get_alias(sc, aval)) == NULL)692return (NULL);693694return (alias->path);695}696697/**698* Register a device path entry for the path referenced by variable name699* @p info, if any.700*701* @param sc The NVRAM store to be updated.702* @param info The NVRAM variable name info.703* @param cookiep The NVRAM variable's cookiep value.704*705* @retval 0 if the path was successfully registered, or an identical706* path or alias entry exists.707* @retval EEXIST if a conflicting entry already exists for the path or708* alias referenced by @p info.709* @retval ENOENT if @p info contains a dangling alias reference.710* @retval EINVAL if @p info contains an unsupported bhnd_nvstore_var_type711* and bhnd_nvstore_path_type combination.712* @retval ENOMEM if allocation fails.713*/714int715bhnd_nvstore_var_register_path(struct bhnd_nvram_store *sc,716bhnd_nvstore_name_info *info, void *cookiep)717{718switch (info->type) {719case BHND_NVSTORE_VAR:720/* Variable */721switch (info->path_type) {722case BHND_NVSTORE_PATH_STRING:723/* Variable contains a full path string724* (pci/1/1/varname); register the path */725return (bhnd_nvstore_register_path(sc,726info->path.str.value, info->path.str.value_len));727728case BHND_NVSTORE_PATH_ALIAS:729/* Variable contains an alias reference (0:varname).730* There's no path to register */731return (0);732}733734BHND_NV_PANIC("unsupported path type %d", info->path_type);735break;736737case BHND_NVSTORE_ALIAS_DECL:738/* Alias declaration */739return (bhnd_nvstore_register_alias(sc, info, cookiep));740}741742BHND_NV_PANIC("unsupported var type %d", info->type);743}744745/**746* Resolve the device path entry referenced by @p info.747*748* @param sc The NVRAM store to be updated.749* @param info Variable name information descriptor containing750* the path or path alias to be resolved.751*752* @retval non-NULL if found.753* @retval NULL if not found.754*/755bhnd_nvstore_path *756bhnd_nvstore_var_get_path(struct bhnd_nvram_store *sc,757bhnd_nvstore_name_info *info)758{759switch (info->path_type) {760case BHND_NVSTORE_PATH_STRING:761return (bhnd_nvstore_get_path(sc, info->path.str.value,762info->path.str.value_len));763case BHND_NVSTORE_PATH_ALIAS:764return (bhnd_nvstore_resolve_path_alias(sc,765info->path.alias.value));766}767768BHND_NV_PANIC("unsupported path type %d", info->path_type);769}770771/**772* Return the device path alias entry registered for @p alias_val, if any.773*774* @param sc The NVRAM store to be queried.775* @param alias_val The alias value to search for.776*777* @retval non-NULL if found.778* @retval NULL if not found.779*/780bhnd_nvstore_alias *781bhnd_nvstore_get_alias(struct bhnd_nvram_store *sc, u_long alias_val)782{783bhnd_nvstore_alias_list *alist;784bhnd_nvstore_alias *alias;785786BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);787788/* Can use hash lookup */789alist = &sc->aliases[alias_val % nitems(sc->aliases)];790LIST_FOREACH(alias, alist, na_link) {791if (alias->alias == alias_val)792return (alias);793}794795/* Not found */796return (NULL);797}798799/**800* Return the device path alias entry registered for @p path, if any.801*802* @param sc The NVRAM store to be queried.803* @param path The alias path to search for.804*805* @retval non-NULL if found.806* @retval NULL if not found.807*/808bhnd_nvstore_alias *809bhnd_nvstore_find_alias(struct bhnd_nvram_store *sc, const char *path)810{811bhnd_nvstore_alias *alias;812813BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);814815/* Have to scan the full table */816for (size_t i = 0; i < nitems(sc->aliases); i++) {817LIST_FOREACH(alias, &sc->aliases[i], na_link) {818if (strcmp(alias->path->path_str, path) == 0)819return (alias);820}821}822823/* Not found */824return (NULL);825}826827/**828* Register a device path entry for @p path.829*830* @param sc The NVRAM store to be updated.831* @param path_str The absolute device path string.832* @param path_len The length of @p path_str.833*834* @retval 0 if the path was successfully registered, or an identical835* path/alias entry already exists.836* @retval ENOMEM if allocation fails.837*/838int839bhnd_nvstore_register_path(struct bhnd_nvram_store *sc, const char *path_str,840size_t path_len)841{842bhnd_nvstore_path_list *plist;843bhnd_nvstore_path *path;844uint32_t h;845846BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);847848/* Already exists? */849if (bhnd_nvstore_get_path(sc, path_str, path_len) != NULL)850return (0);851852/* Can't represent more than SIZE_MAX paths */853if (sc->num_paths == SIZE_MAX)854return (ENOMEM);855856/* Allocate new entry */857path = bhnd_nvstore_path_new(path_str, path_len);858if (path == NULL)859return (ENOMEM);860861/* Insert in path hash table */862h = hash32_str(path->path_str, HASHINIT);863plist = &sc->paths[h % nitems(sc->paths)];864LIST_INSERT_HEAD(plist, path, np_link);865866/* Increment path count */867sc->num_paths++;868869return (0);870}871872/**873* Register a device path alias for an NVRAM 'devpathX' variable.874*875* The path value for the alias will be fetched from the backing NVRAM data.876*877* @param sc The NVRAM store to be updated.878* @param info The NVRAM variable name info.879* @param cookiep The NVRAM variable's cookiep value.880*881* @retval 0 if the alias was successfully registered, or an882* identical alias entry exists.883* @retval EEXIST if a conflicting alias or path entry already exists.884* @retval EINVAL if @p info is not a BHND_NVSTORE_ALIAS_DECL or does885* not contain a BHND_NVSTORE_PATH_ALIAS entry.886* @retval ENOMEM if allocation fails.887*/888int889bhnd_nvstore_register_alias(struct bhnd_nvram_store *sc,890const bhnd_nvstore_name_info *info, void *cookiep)891{892bhnd_nvstore_alias_list *alist;893bhnd_nvstore_alias *alias;894bhnd_nvstore_path *path;895char *path_str;896size_t path_len;897int error;898899BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);900901path_str = NULL;902alias = NULL;903904/* Can't represent more than SIZE_MAX aliases */905if (sc->num_aliases == SIZE_MAX)906return (ENOMEM);907908/* Must be an alias declaration */909if (info->type != BHND_NVSTORE_ALIAS_DECL)910return (EINVAL);911912if (info->path_type != BHND_NVSTORE_PATH_ALIAS)913return (EINVAL);914915/* Fetch the devpath variable's value length */916error = bhnd_nvram_data_getvar(sc->data, cookiep, NULL, &path_len,917BHND_NVRAM_TYPE_STRING);918if (error)919return (ENOMEM);920921/* Allocate path string buffer */922if ((path_str = bhnd_nv_malloc(path_len)) == NULL)923return (ENOMEM);924925/* Decode to our new buffer */926error = bhnd_nvram_data_getvar(sc->data, cookiep, path_str, &path_len,927BHND_NVRAM_TYPE_STRING);928if (error)929goto failed;930931/* Trim trailing '/' character(s) from the path length */932path_len = strnlen(path_str, path_len);933while (path_len > 0 && path_str[path_len-1] == '/') {934path_str[path_len-1] = '\0';935path_len--;936}937938/* Is a conflicting alias entry already registered for this alias939* value? */940alias = bhnd_nvstore_get_alias(sc, info->path.alias.value);941if (alias != NULL) {942if (alias->cookiep != cookiep ||943strcmp(alias->path->path_str, path_str) != 0)944{945error = EEXIST;946goto failed;947}948}949950/* Is a conflicting entry already registered for the alias path? */951if ((alias = bhnd_nvstore_find_alias(sc, path_str)) != NULL) {952if (alias->alias != info->path.alias.value ||953alias->cookiep != cookiep ||954strcmp(alias->path->path_str, path_str) != 0)955{956error = EEXIST;957goto failed;958}959}960961/* Get (or register) the target path entry */962path = bhnd_nvstore_get_path(sc, path_str, path_len);963if (path == NULL) {964error = bhnd_nvstore_register_path(sc, path_str, path_len);965if (error)966goto failed;967968path = bhnd_nvstore_get_path(sc, path_str, path_len);969BHND_NV_ASSERT(path != NULL, ("missing registered path"));970}971972/* Allocate alias entry */973alias = bhnd_nv_calloc(1, sizeof(*alias));974if (alias == NULL) {975error = ENOMEM;976goto failed;977}978979alias->path = path;980alias->cookiep = cookiep;981alias->alias = info->path.alias.value;982983/* Insert in alias hash table */984alist = &sc->aliases[alias->alias % nitems(sc->aliases)];985LIST_INSERT_HEAD(alist, alias, na_link);986987/* Increment alias count */988sc->num_aliases++;989990bhnd_nv_free(path_str);991return (0);992993failed:994if (path_str != NULL)995bhnd_nv_free(path_str);996997if (alias != NULL)998bhnd_nv_free(alias);9991000return (error);1001}10021003/**1004* If @p child is equal to or a child path of @p parent, return a pointer to1005* @p child's path component(s) relative to @p parent; otherwise, return NULL.1006*/1007const char *1008bhnd_nvstore_parse_relpath(const char *parent, const char *child)1009{1010size_t prefix_len;10111012/* All paths have an implicit leading '/'; this allows us to treat1013* our manufactured root path of "/" as a prefix to all NVRAM-defined1014* paths (which do not necessarily include a leading '/' */1015if (*parent == '/')1016parent++;10171018if (*child == '/')1019child++;10201021/* Is parent a prefix of child? */1022prefix_len = strlen(parent);1023if (strncmp(parent, child, prefix_len) != 0)1024return (NULL);10251026/* A zero-length prefix matches everything */1027if (prefix_len == 0)1028return (child);10291030/* Is child equal to parent? */1031if (child[prefix_len] == '\0')1032return (child + prefix_len);10331034/* Is child actually a child of parent? */1035if (child[prefix_len] == '/')1036return (child + prefix_len + 1);10371038/* No match (e.g. parent=/foo..., child=/fooo...) */1039return (NULL);1040}10411042/**1043* Parse a raw NVRAM variable name and return its @p entry_type, its1044* type-specific @p prefix (e.g. '0:', 'pci/1/1', 'devpath'), and its1045* type-specific @p suffix (e.g. 'varname', '0').1046*1047* @param name The NVRAM variable name to be parsed. This1048* value must remain valid for the lifetime of1049* @p info.1050* @param type The NVRAM name type -- either INTERNAL for names1051* parsed from backing NVRAM data, or EXTERNAL for1052* names provided by external NVRAM store clients.1053* @param data_caps The backing NVRAM data capabilities1054* (see bhnd_nvram_data_caps()).1055* @param[out] info On success, the parsed variable name info.1056*1057* @retval 0 success1058* @retval non-zero if parsing @p name otherwise fails, a regular unix1059* error code will be returned.1060*/1061int1062bhnd_nvstore_parse_name_info(const char *name, bhnd_nvstore_name_type type,1063uint32_t data_caps, bhnd_nvstore_name_info *info)1064{1065const char *p;1066char *endp;10671068/* Skip path parsing? */1069if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) {1070/* devpath declaration? (devpath0=pci/1/1) */1071if (strncmp(name, "devpath", strlen("devpath")) == 0) {1072u_long alias;10731074/* Perform standard validation on the relative1075* variable name */1076if (type != BHND_NVSTORE_NAME_INTERNAL &&1077!bhnd_nvram_validate_name(name))1078{1079return (ENOENT);1080}10811082/* Parse alias value that should follow a 'devpath'1083* prefix */1084p = name + strlen("devpath");1085alias = strtoul(p, &endp, 10);1086if (endp != p && *endp == '\0') {1087info->type = BHND_NVSTORE_ALIAS_DECL;1088info->path_type = BHND_NVSTORE_PATH_ALIAS;1089info->name = name;1090info->path.alias.value = alias;10911092return (0);1093}1094}10951096/* device aliased variable? (0:varname) */1097if (bhnd_nv_isdigit(*name)) {1098u_long alias;10991100/* Parse '0:' alias prefix */1101alias = strtoul(name, &endp, 10);1102if (endp != name && *endp == ':') {1103/* Perform standard validation on the relative1104* variable name */1105if (type != BHND_NVSTORE_NAME_INTERNAL &&1106!bhnd_nvram_validate_name(name))1107{1108return (ENOENT);1109}11101111info->type = BHND_NVSTORE_VAR;1112info->path_type = BHND_NVSTORE_PATH_ALIAS;11131114/* name follows 0: prefix */1115info->name = endp + 1;1116info->path.alias.value = alias;11171118return (0);1119}1120}11211122/* device variable? (pci/1/1/varname) */1123if ((p = strrchr(name, '/')) != NULL) {1124const char *path, *relative_name;1125size_t path_len;11261127/* Determine the path length; 'p' points at the last1128* path separator in 'name' */1129path_len = p - name;1130path = name;11311132/* The relative variable name directly follows the1133* final path separator '/' */1134relative_name = path + path_len + 1;11351136/* Now that we calculated the name offset, exclude all1137* trailing '/' characters from the path length */1138while (path_len > 0 && path[path_len-1] == '/')1139path_len--;11401141/* Perform standard validation on the relative1142* variable name */1143if (type != BHND_NVSTORE_NAME_INTERNAL &&1144!bhnd_nvram_validate_name(relative_name))1145{1146return (ENOENT);1147}11481149/* Initialize result with pointers into the name1150* buffer */1151info->type = BHND_NVSTORE_VAR;1152info->path_type = BHND_NVSTORE_PATH_STRING;1153info->name = relative_name;1154info->path.str.value = path;1155info->path.str.value_len = path_len;11561157return (0);1158}1159}11601161/* If all other parsing fails, the result is a simple variable with1162* an implicit path of "/" */1163if (type != BHND_NVSTORE_NAME_INTERNAL &&1164!bhnd_nvram_validate_name(name))1165{1166/* Invalid relative name */1167return (ENOENT);1168}11691170info->type = BHND_NVSTORE_VAR;1171info->path_type = BHND_NVSTORE_PATH_STRING;1172info->name = name;1173info->path.str.value = BHND_NVSTORE_ROOT_PATH;1174info->path.str.value_len = BHND_NVSTORE_ROOT_PATH_LEN;11751176return (0);1177}117811791180