The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer,
   10  *    without modification.
   11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
   12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
   13  *    redistribution must be conditioned upon including a substantially
   14  *    similar Disclaimer requirement for further binary redistribution.
   15  *
   16  * NO WARRANTY
   17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
   20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
   21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
   22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
   25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   27  * THE POSSIBILITY OF SUCH DAMAGES.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 #include <sys/param.h>
   34 #include <sys/hash.h>
   35 #include <sys/queue.h>
   36 
   37 #ifdef _KERNEL
   38 
   39 #include <sys/ctype.h>
   40 #include <sys/systm.h>
   41 
   42 #include <machine/_inttypes.h>
   43 
   44 #else /* !_KERNEL */
   45 
   46 #include <ctype.h>
   47 #include <errno.h>
   48 #include <inttypes.h>
   49 #include <stdbool.h>
   50 #include <stdio.h>
   51 #include <stdint.h>
   52 #include <stdlib.h>
   53 #include <string.h>
   54 
   55 #endif /* _KERNEL */
   56 
   57 #include "bhnd_nvram_private.h"
   58 #include "bhnd_nvram_datavar.h"
   59 
   60 #include "bhnd_nvram_storevar.h"
   61 
   62 static int bhnd_nvstore_idx_cmp(const void *lhs, const void *rhs, void *ctx);
   63 
   64 /**
   65  * Allocate and initialize a new path instance.
   66  * 
   67  * The caller is responsible for deallocating the instance via
   68  * bhnd_nvstore_path_free().
   69  * 
   70  * @param       path_str        The path's canonical string representation.
   71  * @param       path_len        The length of @p path_str.
   72  * 
   73  * @retval non-NULL     success
   74  * @retval NULL         if allocation fails.
   75  */
   76 bhnd_nvstore_path *
   77 bhnd_nvstore_path_new(const char *path_str, size_t path_len)
   78 {
   79         bhnd_nvstore_path *path;
   80 
   81         /* Allocate new entry */
   82         path = bhnd_nv_malloc(sizeof(*path));
   83         if (path == NULL)
   84                 return (NULL);
   85 
   86         path->index = NULL;
   87         path->num_vars = 0;
   88 
   89         path->pending = bhnd_nvram_plist_new();
   90         if (path->pending == NULL)
   91                 goto failed;
   92 
   93         path->path_str = bhnd_nv_strndup(path_str, path_len);
   94         if (path->path_str == NULL)
   95                 goto failed;
   96 
   97         return (path);
   98 
   99 failed:
  100         if (path->pending != NULL)
  101                 bhnd_nvram_plist_release(path->pending);
  102 
  103         if (path->path_str != NULL)
  104                 bhnd_nv_free(path->path_str);
  105 
  106         bhnd_nv_free(path);
  107 
  108         return (NULL);
  109 }
  110 
  111 /**
  112  * Free an NVRAM path instance, releasing all associated resources.
  113  */
  114 void
  115 bhnd_nvstore_path_free(struct bhnd_nvstore_path *path)
  116 {
  117         /* Free the per-path index */
  118         if (path->index != NULL)
  119                 bhnd_nvstore_index_free(path->index);
  120 
  121         bhnd_nvram_plist_release(path->pending);
  122         bhnd_nv_free(path->path_str);
  123         bhnd_nv_free(path);
  124 }
  125 
  126 /**
  127  * Allocate and initialize a new index instance with @p capacity.
  128  * 
  129  * The caller is responsible for deallocating the instance via
  130  * bhnd_nvstore_index_free().
  131  * 
  132  * @param       capacity        The maximum number of variables to be indexed.
  133  * 
  134  * @retval non-NULL     success
  135  * @retval NULL         if allocation fails.
  136  */
  137 bhnd_nvstore_index *
  138 bhnd_nvstore_index_new(size_t capacity)
  139 {
  140         bhnd_nvstore_index      *index;
  141         size_t                   bytes;
  142 
  143         /* Allocate and populate variable index */
  144         bytes = sizeof(struct bhnd_nvstore_index) + (sizeof(void *) * capacity);
  145         index = bhnd_nv_malloc(bytes);
  146         if (index == NULL) {
  147                 BHND_NV_LOG("error allocating %zu byte index\n", bytes);
  148                 return (NULL);
  149         }
  150 
  151         index->count = 0;
  152         index->capacity = capacity;
  153 
  154         return (index);
  155 }
  156 
  157 /**
  158  * Free an index instance, releasing all associated resources.
  159  * 
  160  * @param       index   An index instance previously allocated via
  161  *                      bhnd_nvstore_index_new().
  162  */
  163 void
  164 bhnd_nvstore_index_free(bhnd_nvstore_index *index)
  165 {
  166         bhnd_nv_free(index);
  167 }
  168 
  169 /**
  170  * Append a new NVRAM variable's @p cookiep value to @p index.
  171  * 
  172  * After one or more append requests, the index must be prepared via
  173  * bhnd_nvstore_index_prepare() before any indexed lookups are performed.
  174  *
  175  * @param       sc      The NVRAM store from which NVRAM values will be queried.
  176  * @param       index   The index to be modified.
  177  * @param       cookiep The cookiep value (as provided by the backing NVRAM
  178  *                      data instance of @p sc) to be included in @p index.
  179  * 
  180  * @retval 0            success
  181  * @retval ENOMEM       if appending an additional entry would exceed the
  182  *                      capacity of @p index.
  183  */
  184 int
  185 bhnd_nvstore_index_append(struct bhnd_nvram_store *sc,
  186     bhnd_nvstore_index *index, void *cookiep)
  187 {
  188         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  189 
  190         if (index->count >= index->capacity)
  191                 return (ENOMEM);
  192 
  193         index->cookiep[index->count] = cookiep;
  194         index->count++;
  195         return (0);
  196 }
  197 
  198 /* sort function for bhnd_nvstore_index_prepare() */
  199 static int
  200 bhnd_nvstore_idx_cmp(const void *lhs, const void *rhs, void *ctx)
  201 {
  202         struct bhnd_nvram_store *sc;
  203         void                    *l_cookiep, *r_cookiep;
  204         const char              *l_str, *r_str;
  205         const char              *l_name, *r_name;
  206         int                      order;
  207 
  208         sc = ctx;
  209         l_cookiep = *(void * const *)lhs;
  210         r_cookiep = *(void * const *)rhs;
  211 
  212         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  213 
  214         /* Fetch string pointers from the cookiep values */
  215         l_str = bhnd_nvram_data_getvar_name(sc->data, l_cookiep);
  216         r_str = bhnd_nvram_data_getvar_name(sc->data, r_cookiep);
  217 
  218         /* Trim device path prefixes */
  219         if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) {
  220                 l_name = bhnd_nvram_trim_path_name(l_str);
  221                 r_name = bhnd_nvram_trim_path_name(r_str);
  222         } else {
  223                 l_name = l_str;
  224                 r_name = r_str;
  225         }
  226 
  227         /* Perform comparison */
  228         order = strcmp(l_name, r_name);
  229         if (order != 0 || lhs == rhs)
  230                 return (order);
  231 
  232         /* If the backing data incorrectly contains variables with duplicate
  233          * names, we need a sort order that provides stable behavior.
  234          * 
  235          * Since Broadcom's own code varies wildly on this question, we just
  236          * use a simple precedence rule: The first declaration of a variable
  237          * takes precedence. */
  238         return (bhnd_nvram_data_getvar_order(sc->data, l_cookiep, r_cookiep));
  239 }
  240 
  241 /**
  242  * Prepare @p index for querying via bhnd_nvstore_index_lookup().
  243  * 
  244  * After one or more append requests, the index must be prepared via
  245  * bhnd_nvstore_index_prepare() before any indexed lookups are performed.
  246  *
  247  * @param       sc      The NVRAM store from which NVRAM values will be queried.
  248  * @param       index   The index to be prepared.
  249  * 
  250  * @retval 0            success
  251  * @retval non-zero     if preparing @p index otherwise fails, a regular unix
  252  *                      error code will be returned.
  253  */
  254 int
  255 bhnd_nvstore_index_prepare(struct bhnd_nvram_store *sc,
  256     bhnd_nvstore_index *index)
  257 {
  258         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  259 
  260         /* Sort the index table */
  261         qsort_r(index->cookiep, index->count, sizeof(index->cookiep[0]),
  262             bhnd_nvstore_idx_cmp, sc);
  263 
  264         return (0);
  265 }
  266 
  267 /**
  268  * Return a borrowed reference to the root path node.
  269  * 
  270  * @param       sc      The NVRAM store.
  271  */
  272 bhnd_nvstore_path *
  273 bhnd_nvstore_get_root_path(struct bhnd_nvram_store *sc)
  274 {
  275         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  276         return (sc->root_path);
  277 }
  278 
  279 /**
  280  * Return true if @p path is the root path node.
  281  * 
  282  * @param       sc      The NVRAM store.
  283  * @param       path    The path to query.
  284  */
  285 bool
  286 bhnd_nvstore_is_root_path(struct bhnd_nvram_store *sc, bhnd_nvstore_path *path)
  287 {
  288         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  289         return (sc->root_path == path);
  290 }
  291 
  292 /**
  293  * Return the update entry matching @p name in @p path, or NULL if no entry
  294  * found.
  295  * 
  296  * @param sc    The NVRAM store.
  297  * @param path  The path to query.
  298  * @param name  The NVRAM variable name to search for in @p path's update list.
  299  * 
  300  * @retval non-NULL     success
  301  * @retval NULL         if @p name is not found in @p path.
  302  */
  303 bhnd_nvram_prop *
  304 bhnd_nvstore_path_get_update(struct bhnd_nvram_store *sc,
  305     bhnd_nvstore_path *path, const char *name)
  306 {
  307         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  308         return (bhnd_nvram_plist_get_prop(path->pending, name));
  309 }
  310 
  311 /**
  312  * Register or remove an update record for @p name in @p path.
  313  * 
  314  * @param sc    The NVRAM store.
  315  * @param path  The path to be modified.
  316  * @param name  The path-relative variable name to be modified.
  317  * @param value The new value. A value of BHND_NVRAM_TYPE_NULL denotes deletion.
  318  * 
  319  * @retval 0            success
  320  * @retval ENOMEM       if allocation fails.
  321  * @retval ENOENT       if @p name is unknown.
  322  * @retval EINVAL       if @p value is NULL, and deletion of @p is not
  323  *                      supported.
  324  * @retval EINVAL       if @p value cannot be converted to a supported value
  325  *                      type.
  326  */
  327 int
  328 bhnd_nvstore_path_register_update(struct bhnd_nvram_store *sc,
  329     bhnd_nvstore_path *path, const char *name, bhnd_nvram_val *value)
  330 {
  331         bhnd_nvram_val          *prop_val;
  332         const char              *full_name;
  333         void                    *cookiep;
  334         char                    *namebuf;
  335         int                      error;
  336         bool                     nvram_committed;
  337 
  338         namebuf = NULL;
  339         prop_val = NULL;
  340 
  341         /* Determine whether the variable is currently defined in the
  342          * backing NVRAM data, and derive its full path-prefixed name */
  343         nvram_committed = false;
  344         cookiep = bhnd_nvstore_path_data_lookup(sc, path, name);
  345         if (cookiep != NULL) {
  346                 /* Variable is defined in the backing data */
  347                 nvram_committed = true;
  348 
  349                 /* Use the existing variable name */
  350                 full_name = bhnd_nvram_data_getvar_name(sc->data, cookiep);
  351         } else if (path == sc->root_path) {
  352                 /* No prefix required for root path */
  353                 full_name = name;
  354         } else {
  355                 bhnd_nvstore_alias      *alias;
  356                 int                      len;
  357 
  358                 /* New variable is being set; we need to determine the
  359                  * appropriate path prefix */
  360                 alias = bhnd_nvstore_find_alias(sc, path->path_str);
  361                 if (alias != NULL) {
  362                         /* Use <alias>:name */
  363                         len = bhnd_nv_asprintf(&namebuf, "%lu:%s", alias->alias,
  364                             name);
  365                 } else {
  366                         /* Use path/name */
  367                         len = bhnd_nv_asprintf(&namebuf, "%s/%s",
  368                             path->path_str, name);
  369                 }
  370 
  371                 if (len < 0)
  372                         return (ENOMEM);
  373 
  374                 full_name = namebuf;
  375         }
  376 
  377         /* Allow the data store to filter the NVRAM operation */
  378         if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) {
  379                 error = bhnd_nvram_data_filter_unsetvar(sc->data, full_name);
  380                 if (error) {
  381                         BHND_NV_LOG("cannot unset property %s: %d\n", full_name,
  382                             error);
  383                         goto cleanup;
  384                 }
  385 
  386                 if ((prop_val = bhnd_nvram_val_copy(value)) == NULL) {
  387                         error = ENOMEM;
  388                         goto cleanup;
  389                 }
  390         } else {
  391                 error = bhnd_nvram_data_filter_setvar(sc->data, full_name,
  392                     value,  &prop_val);
  393                 if (error) {
  394                         BHND_NV_LOG("cannot set property %s: %d\n", full_name,
  395                             error);
  396                         goto cleanup;
  397                 }
  398         }
  399 
  400         /* Add relative variable name to the per-path update list */
  401         if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL &&
  402             !nvram_committed)
  403         {
  404                 /* This is a deletion request for a variable not defined in
  405                  * out backing store; we can simply remove the corresponding
  406                  * update entry. */
  407                 bhnd_nvram_plist_remove(path->pending, name);
  408         } else {
  409                 /* Update or append a pending update entry */
  410                 error = bhnd_nvram_plist_replace_val(path->pending, name,
  411                     prop_val);
  412                 if (error)
  413                         goto cleanup;
  414         }
  415 
  416         /* Success */
  417         error = 0;
  418 
  419 cleanup:
  420         if (namebuf != NULL)
  421                 bhnd_nv_free(namebuf);
  422 
  423         if (prop_val != NULL)
  424                 bhnd_nvram_val_release(prop_val);
  425 
  426         return (error);
  427 }
  428 
  429 /**
  430  * Iterate over all variable cookiep values retrievable from the backing
  431  * data store in @p path.
  432  * 
  433  * @warning Pending updates in @p path are ignored by this function.
  434  *
  435  * @param               sc      The NVRAM store.
  436  * @param               path    The NVRAM path to be iterated.
  437  * @param[in,out]       indexp  A pointer to an opaque indexp value previously
  438  *                              returned by bhnd_nvstore_path_data_next(), or a
  439  *                              NULL value to begin iteration.
  440  *
  441  * @return Returns the next variable name, or NULL if there are no more
  442  * variables defined in @p path.
  443  */
  444 void *
  445 bhnd_nvstore_path_data_next(struct bhnd_nvram_store *sc,
  446      bhnd_nvstore_path *path, void **indexp)
  447 {
  448         void **index_ref;
  449 
  450         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  451 
  452         /* No index */
  453         if (path->index == NULL) {
  454                 /* An index is required for all non-empty, non-root path
  455                  * instances */
  456                 BHND_NV_ASSERT(bhnd_nvstore_is_root_path(sc, path),
  457                     ("missing index for non-root path %s", path->path_str));
  458 
  459                 /* Iterate NVRAM data directly, using the NVRAM data's cookiep
  460                  * value as our indexp context */
  461                 if ((bhnd_nvram_data_next(sc->data, indexp)) == NULL)
  462                         return (NULL);
  463 
  464                 return (*indexp);
  465         }
  466 
  467         /* Empty index */
  468         if (path->index->count == 0)
  469                 return (NULL);
  470 
  471         if (*indexp == NULL) {
  472                 /* First index entry */
  473                 index_ref = &path->index->cookiep[0];
  474         } else {
  475                 size_t idxpos;
  476 
  477                 /* Advance to next index entry */
  478                 index_ref = *indexp;
  479                 index_ref++;
  480 
  481                 /* Hit end of index? */
  482                 BHND_NV_ASSERT(index_ref > path->index->cookiep,
  483                     ("invalid indexp"));
  484 
  485                 idxpos = (index_ref - path->index->cookiep);
  486                 if (idxpos >= path->index->count)
  487                         return (NULL);
  488         }
  489 
  490         /* Provide new index position */
  491         *indexp = index_ref;
  492 
  493         /* Return the data's cookiep value */
  494         return (*index_ref);
  495 }
  496 
  497 /**
  498  * Perform an lookup of @p name in the backing NVRAM data for @p path,
  499  * returning the associated cookiep value, or NULL if the variable is not found
  500  * in the backing NVRAM data.
  501  * 
  502  * @warning Pending updates in @p path are ignored by this function.
  503  * 
  504  * @param       sc      The NVRAM store from which NVRAM values will be queried.
  505  * @param       path    The path to be queried.
  506  * @param       name    The variable name to be queried.
  507  * 
  508  * @retval non-NULL     success
  509  * @retval NULL         if @p name is not found in @p index.
  510  */
  511 void *
  512 bhnd_nvstore_path_data_lookup(struct bhnd_nvram_store *sc,
  513     bhnd_nvstore_path *path, const char *name)
  514 {
  515         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  516 
  517         /* No index */
  518         if (path->index == NULL) {
  519                 /* An index is required for all non-empty, non-root path
  520                  * instances */
  521                 BHND_NV_ASSERT(bhnd_nvstore_is_root_path(sc, path),
  522                     ("missing index for non-root path %s", path->path_str));
  523 
  524                 /* Look up directly in NVRAM data */
  525                 return (bhnd_nvram_data_find(sc->data, name));
  526         }
  527 
  528         /* Otherwise, delegate to an index-based lookup */
  529         return (bhnd_nvstore_index_lookup(sc, path->index, name));
  530 }
  531 
  532 /**
  533  * Perform an index lookup of @p name, returning the associated cookiep
  534  * value, or NULL if the variable does not exist.
  535  * 
  536  * @param       sc      The NVRAM store from which NVRAM values will be queried.
  537  * @param       index   The index to be queried.
  538  * @param       name    The variable name to be queried.
  539  * 
  540  * @retval non-NULL     success
  541  * @retval NULL         if @p name is not found in @p index.
  542  */
  543 void *
  544 bhnd_nvstore_index_lookup(struct bhnd_nvram_store *sc,
  545     bhnd_nvstore_index *index, const char *name)
  546 {
  547         void            *cookiep;
  548         const char      *indexed_name;
  549         size_t           min, mid, max;
  550         uint32_t         data_caps;
  551         int              order;
  552 
  553         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  554         BHND_NV_ASSERT(index != NULL, ("NULL index"));
  555 
  556         /*
  557          * Locate the requested variable using a binary search.
  558          */
  559         if (index->count == 0)
  560                 return (NULL);
  561 
  562         data_caps = sc->data_caps;
  563         min = 0;
  564         max = index->count - 1;
  565 
  566         while (max >= min) {
  567                 /* Select midpoint */
  568                 mid = (min + max) / 2;
  569                 cookiep = index->cookiep[mid];
  570 
  571                 /* Fetch variable name */
  572                 indexed_name = bhnd_nvram_data_getvar_name(sc->data, cookiep);
  573 
  574                 /* Trim any path prefix */
  575                 if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)
  576                         indexed_name = bhnd_nvram_trim_path_name(indexed_name);
  577 
  578                 /* Determine which side of the partition to search */
  579                 order = strcmp(indexed_name, name);
  580                 if (order < 0) {
  581                         /* Search upper partition */
  582                         min = mid + 1;
  583                 } else if (order > 0) {
  584                         /* Search (non-empty) lower partition */
  585                         if (mid == 0)
  586                                 break;
  587                         max = mid - 1;
  588                 } else if (order == 0) {
  589                         size_t  idx;
  590 
  591                         /*
  592                          * Match found.
  593                          * 
  594                          * If this happens to be a key with multiple definitions
  595                          * in the backing store, we need to find the entry with
  596                          * the highest declaration precedence.
  597                          * 
  598                          * Duplicates are sorted in order of descending
  599                          * precedence; to find the highest precedence entry,
  600                          * we search backwards through the index.
  601                          */
  602                         idx = mid;
  603                         while (idx > 0) {
  604                                 void            *dup_cookiep;
  605                                 const char      *dup_name;
  606 
  607                                 /* Fetch preceding index entry */
  608                                 idx--;
  609                                 dup_cookiep = index->cookiep[idx];
  610                                 dup_name = bhnd_nvram_data_getvar_name(sc->data,
  611                                     dup_cookiep);
  612 
  613                                 /* Trim any path prefix */
  614                                 if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) {
  615                                         dup_name = bhnd_nvram_trim_path_name(
  616                                             dup_name);
  617                                 }
  618 
  619                                 /* If no match, current cookiep is the variable
  620                                  * definition with the highest precedence */
  621                                 if (strcmp(indexed_name, dup_name) != 0)
  622                                         return (cookiep);
  623 
  624                                 /* Otherwise, prefer this earlier definition,
  625                                  * and keep searching for a higher-precedence
  626                                  * definitions */
  627                                 cookiep = dup_cookiep;
  628                         }
  629 
  630                         return (cookiep);
  631                 }
  632         }
  633 
  634         /* Not found */
  635         return (NULL);
  636 }
  637 
  638 /**
  639  * Return the device path entry registered for @p path, if any.
  640  * 
  641  * @param       sc              The NVRAM store to be queried.
  642  * @param       path            The device path to search for.
  643  * @param       path_len        The length of @p path.
  644  *
  645  * @retval non-NULL     if found.
  646  * @retval NULL         if not found.
  647  */
  648 bhnd_nvstore_path *
  649 bhnd_nvstore_get_path(struct bhnd_nvram_store *sc, const char *path,
  650     size_t path_len)
  651 {
  652         bhnd_nvstore_path_list  *plist;
  653         bhnd_nvstore_path       *p;
  654         uint32_t                 h;
  655 
  656         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  657 
  658         /* Use hash lookup */
  659         h = hash32_strn(path, path_len, HASHINIT);
  660         plist = &sc->paths[h % nitems(sc->paths)];
  661 
  662         LIST_FOREACH(p, plist, np_link) {
  663                 /* Check for prefix match */
  664                 if (strncmp(p->path_str, path, path_len) != 0)
  665                         continue;
  666 
  667                 /* Check for complete match */
  668                 if (strnlen(path, path_len) != strlen(p->path_str))
  669                         continue;
  670 
  671                 return (p);
  672         }
  673 
  674         /* Not found */
  675         return (NULL);
  676 }
  677 
  678 /**
  679  * Resolve @p aval to its corresponding device path entry, if any.
  680  * 
  681  * @param       sc              The NVRAM store to be queried.
  682  * @param       aval            The device path alias value to search for.
  683  *
  684  * @retval non-NULL     if found.
  685  * @retval NULL         if not found.
  686  */
  687 bhnd_nvstore_path *
  688 bhnd_nvstore_resolve_path_alias(struct bhnd_nvram_store *sc, u_long aval)
  689 {
  690         bhnd_nvstore_alias *alias;
  691 
  692         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  693 
  694         /* Fetch alias entry */
  695         if ((alias = bhnd_nvstore_get_alias(sc, aval)) == NULL)
  696                 return (NULL);
  697 
  698         return (alias->path);
  699 }
  700 
  701 /**
  702  * Register a device path entry for the path referenced by variable name
  703  * @p info, if any.
  704  *
  705  * @param       sc              The NVRAM store to be updated.
  706  * @param       info            The NVRAM variable name info.
  707  * @param       cookiep         The NVRAM variable's cookiep value.
  708  *
  709  * @retval 0            if the path was successfully registered, or an identical
  710  *                      path or alias entry exists.
  711  * @retval EEXIST       if a conflicting entry already exists for the path or
  712  *                      alias referenced by @p info.
  713  * @retval ENOENT       if @p info contains a dangling alias reference.
  714  * @retval EINVAL       if @p info contains an unsupported bhnd_nvstore_var_type
  715  *                      and bhnd_nvstore_path_type combination.
  716  * @retval ENOMEM       if allocation fails.
  717  */
  718 int
  719 bhnd_nvstore_var_register_path(struct bhnd_nvram_store *sc,
  720     bhnd_nvstore_name_info *info, void *cookiep)
  721 {
  722         switch (info->type) {
  723         case BHND_NVSTORE_VAR:
  724                 /* Variable */
  725                 switch (info->path_type) {
  726                 case BHND_NVSTORE_PATH_STRING:
  727                         /* Variable contains a full path string
  728                          * (pci/1/1/varname); register the path */
  729                         return (bhnd_nvstore_register_path(sc,
  730                             info->path.str.value, info->path.str.value_len));
  731 
  732                 case BHND_NVSTORE_PATH_ALIAS:
  733                         /* Variable contains an alias reference (0:varname).
  734                          * There's no path to register */
  735                         return (0);
  736                 }
  737 
  738                 BHND_NV_PANIC("unsupported path type %d", info->path_type);
  739                 break;
  740 
  741         case BHND_NVSTORE_ALIAS_DECL:
  742                 /* Alias declaration */
  743                 return (bhnd_nvstore_register_alias(sc, info, cookiep));
  744         }
  745 
  746         BHND_NV_PANIC("unsupported var type %d", info->type);
  747 }
  748 
  749 /**
  750  * Resolve the device path entry referenced by @p info.
  751  *
  752  * @param       sc              The NVRAM store to be updated.
  753  * @param       info            Variable name information descriptor containing
  754  *                              the path or path alias to be resolved.
  755  *
  756  * @retval non-NULL     if found.
  757  * @retval NULL         if not found.
  758  */
  759 bhnd_nvstore_path *
  760 bhnd_nvstore_var_get_path(struct bhnd_nvram_store *sc,
  761     bhnd_nvstore_name_info *info)
  762 {
  763         switch (info->path_type) {
  764         case BHND_NVSTORE_PATH_STRING:
  765                 return (bhnd_nvstore_get_path(sc, info->path.str.value,
  766                     info->path.str.value_len));
  767         case BHND_NVSTORE_PATH_ALIAS:
  768                 return (bhnd_nvstore_resolve_path_alias(sc,
  769                     info->path.alias.value));
  770         }
  771 
  772         BHND_NV_PANIC("unsupported path type %d", info->path_type);
  773 }
  774 
  775 /**
  776  * Return the device path alias entry registered for @p alias_val, if any.
  777  * 
  778  * @param       sc              The NVRAM store to be queried.
  779  * @param       alias_val       The alias value to search for.
  780  *
  781  * @retval non-NULL     if found.
  782  * @retval NULL         if not found.
  783  */
  784 bhnd_nvstore_alias *
  785 bhnd_nvstore_get_alias(struct bhnd_nvram_store *sc, u_long alias_val)
  786 {
  787         bhnd_nvstore_alias_list *alist;
  788         bhnd_nvstore_alias      *alias;
  789 
  790         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  791 
  792         /* Can use hash lookup */
  793         alist = &sc->aliases[alias_val % nitems(sc->aliases)];
  794         LIST_FOREACH(alias, alist, na_link) {
  795                 if (alias->alias == alias_val)
  796                         return (alias);                 
  797         }
  798 
  799         /* Not found */
  800         return (NULL);
  801 }
  802 
  803 /**
  804  * Return the device path alias entry registered for @p path, if any.
  805  * 
  806  * @param       sc      The NVRAM store to be queried.
  807  * @param       path    The alias path to search for.
  808  *
  809  * @retval non-NULL     if found.
  810  * @retval NULL         if not found.
  811  */
  812 bhnd_nvstore_alias *
  813 bhnd_nvstore_find_alias(struct bhnd_nvram_store *sc, const char *path)
  814 {
  815         bhnd_nvstore_alias *alias;
  816 
  817         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  818 
  819         /* Have to scan the full table */
  820         for (size_t i = 0; i < nitems(sc->aliases); i++) {
  821                 LIST_FOREACH(alias, &sc->aliases[i], na_link) {
  822                         if (strcmp(alias->path->path_str, path) == 0)
  823                                 return (alias);                 
  824                 }
  825         }
  826 
  827         /* Not found */
  828         return (NULL);
  829 }
  830 
  831 /**
  832  * Register a device path entry for @p path.
  833  * 
  834  * @param       sc              The NVRAM store to be updated.
  835  * @param       path_str        The absolute device path string.
  836  * @param       path_len        The length of @p path_str.
  837  * 
  838  * @retval 0            if the path was successfully registered, or an identical
  839  *                      path/alias entry already exists.
  840  * @retval ENOMEM       if allocation fails.
  841  */
  842 int
  843 bhnd_nvstore_register_path(struct bhnd_nvram_store *sc, const char *path_str,
  844     size_t path_len)
  845 {
  846         bhnd_nvstore_path_list  *plist;
  847         bhnd_nvstore_path       *path;
  848         uint32_t                 h;
  849 
  850         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  851 
  852         /* Already exists? */
  853         if (bhnd_nvstore_get_path(sc, path_str, path_len) != NULL)
  854                 return (0);
  855 
  856         /* Can't represent more than SIZE_MAX paths */
  857         if (sc->num_paths == SIZE_MAX)
  858                 return (ENOMEM);
  859 
  860         /* Allocate new entry */
  861         path = bhnd_nvstore_path_new(path_str, path_len);
  862         if (path == NULL)
  863                 return (ENOMEM);
  864 
  865         /* Insert in path hash table */
  866         h = hash32_str(path->path_str, HASHINIT);
  867         plist = &sc->paths[h % nitems(sc->paths)];
  868         LIST_INSERT_HEAD(plist, path, np_link);
  869 
  870         /* Increment path count */
  871         sc->num_paths++;
  872 
  873         return (0);
  874 }
  875 
  876 /**
  877  * Register a device path alias for an NVRAM 'devpathX' variable.
  878  * 
  879  * The path value for the alias will be fetched from the backing NVRAM data.
  880  * 
  881  * @param       sc      The NVRAM store to be updated.
  882  * @param       info    The NVRAM variable name info.
  883  * @param       cookiep The NVRAM variable's cookiep value.
  884  * 
  885  * @retval 0            if the alias was successfully registered, or an
  886  *                      identical alias entry exists.
  887  * @retval EEXIST       if a conflicting alias or path entry already exists.
  888  * @retval EINVAL       if @p info is not a BHND_NVSTORE_ALIAS_DECL or does
  889  *                      not contain a BHND_NVSTORE_PATH_ALIAS entry.
  890  * @retval ENOMEM       if allocation fails.
  891  */
  892 int
  893 bhnd_nvstore_register_alias(struct bhnd_nvram_store *sc,
  894     const bhnd_nvstore_name_info *info, void *cookiep)
  895 {
  896         bhnd_nvstore_alias_list *alist;
  897         bhnd_nvstore_alias      *alias;
  898         bhnd_nvstore_path       *path;
  899         char                    *path_str;
  900         size_t                   path_len;
  901         int                      error;
  902 
  903         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
  904 
  905         path_str = NULL;
  906         alias = NULL;
  907 
  908         /* Can't represent more than SIZE_MAX aliases */
  909         if (sc->num_aliases == SIZE_MAX)
  910                 return (ENOMEM);
  911 
  912         /* Must be an alias declaration */
  913         if (info->type != BHND_NVSTORE_ALIAS_DECL)
  914                 return (EINVAL);
  915 
  916         if (info->path_type != BHND_NVSTORE_PATH_ALIAS)
  917                 return (EINVAL);
  918 
  919         /* Fetch the devpath variable's value length */
  920         error = bhnd_nvram_data_getvar(sc->data, cookiep, NULL, &path_len,
  921             BHND_NVRAM_TYPE_STRING);
  922         if (error)
  923                 return (ENOMEM);
  924 
  925         /* Allocate path string buffer */
  926         if ((path_str = bhnd_nv_malloc(path_len)) == NULL)
  927                 return (ENOMEM);
  928 
  929         /* Decode to our new buffer */
  930         error = bhnd_nvram_data_getvar(sc->data, cookiep, path_str, &path_len,
  931             BHND_NVRAM_TYPE_STRING);
  932         if (error)
  933                 goto failed;
  934 
  935         /* Trim trailing '/' character(s) from the path length */
  936         path_len = strnlen(path_str, path_len);
  937         while (path_len > 0 && path_str[path_len-1] == '/') {
  938                 path_str[path_len-1] = '\0';
  939                 path_len--;
  940         }
  941 
  942         /* Is a conflicting alias entry already registered for this alias
  943          * value? */
  944         alias = bhnd_nvstore_get_alias(sc, info->path.alias.value);
  945         if (alias != NULL) {
  946                 if (alias->cookiep != cookiep ||
  947                     strcmp(alias->path->path_str, path_str) != 0)
  948                 {
  949                         error = EEXIST;
  950                         goto failed;
  951                 }
  952         }
  953 
  954         /* Is a conflicting entry already registered for the alias path? */
  955         if ((alias = bhnd_nvstore_find_alias(sc, path_str)) != NULL) {
  956                 if (alias->alias != info->path.alias.value ||
  957                     alias->cookiep != cookiep ||
  958                     strcmp(alias->path->path_str, path_str) != 0)
  959                 {
  960                         error = EEXIST;
  961                         goto failed;
  962                 }
  963         }
  964 
  965         /* Get (or register) the target path entry */
  966         path = bhnd_nvstore_get_path(sc, path_str, path_len);
  967         if (path == NULL) {
  968                 error = bhnd_nvstore_register_path(sc, path_str, path_len);
  969                 if (error)
  970                         goto failed;
  971 
  972                 path = bhnd_nvstore_get_path(sc, path_str, path_len);
  973                 BHND_NV_ASSERT(path != NULL, ("missing registered path"));
  974         }
  975 
  976         /* Allocate alias entry */
  977         alias = bhnd_nv_calloc(1, sizeof(*alias));
  978         if (alias == NULL) {
  979                 error = ENOMEM;
  980                 goto failed;
  981         }
  982 
  983         alias->path = path;
  984         alias->cookiep = cookiep;
  985         alias->alias = info->path.alias.value;
  986 
  987         /* Insert in alias hash table */
  988         alist = &sc->aliases[alias->alias % nitems(sc->aliases)];
  989         LIST_INSERT_HEAD(alist, alias, na_link);
  990 
  991         /* Increment alias count */
  992         sc->num_aliases++;
  993 
  994         bhnd_nv_free(path_str);
  995         return (0);
  996 
  997 failed:
  998         if (path_str != NULL)
  999                 bhnd_nv_free(path_str);
 1000 
 1001         if (alias != NULL)
 1002                 bhnd_nv_free(alias);
 1003 
 1004         return (error);
 1005 }
 1006 
 1007 /**
 1008  * If @p child is equal to or a child path of @p parent, return a pointer to
 1009  * @p child's path component(s) relative to @p parent; otherwise, return NULL.
 1010  */
 1011 const char *
 1012 bhnd_nvstore_parse_relpath(const char *parent, const char *child)
 1013 {
 1014         size_t prefix_len;
 1015 
 1016         /* All paths have an implicit leading '/'; this allows us to treat
 1017          * our manufactured root path of "/" as a prefix to all NVRAM-defined
 1018          * paths (which do not necessarily include a leading '/' */
 1019         if (*parent == '/')
 1020                 parent++;
 1021 
 1022         if (*child == '/')
 1023                 child++;
 1024 
 1025         /* Is parent a prefix of child? */
 1026         prefix_len = strlen(parent);
 1027         if (strncmp(parent, child, prefix_len) != 0)
 1028                 return (NULL);
 1029 
 1030         /* A zero-length prefix matches everything */
 1031         if (prefix_len == 0)
 1032                 return (child);
 1033 
 1034         /* Is child equal to parent? */
 1035         if (child[prefix_len] == '\0')
 1036                 return (child + prefix_len);
 1037 
 1038         /* Is child actually a child of parent? */
 1039         if (child[prefix_len] == '/')
 1040                 return (child + prefix_len + 1);
 1041 
 1042         /* No match (e.g. parent=/foo..., child=/fooo...) */
 1043         return (NULL);
 1044 }
 1045 
 1046 /**
 1047  * Parse a raw NVRAM variable name and return its @p entry_type, its
 1048  * type-specific @p prefix (e.g. '0:', 'pci/1/1', 'devpath'), and its
 1049  * type-specific @p suffix (e.g. 'varname', '').
 1050  * 
 1051  * @param       name            The NVRAM variable name to be parsed. This
 1052  *                              value must remain valid for the lifetime of
 1053  *                              @p info.
 1054  * @param       type            The NVRAM name type -- either INTERNAL for names
 1055  *                              parsed from backing NVRAM data, or EXTERNAL for
 1056  *                              names provided by external NVRAM store clients.
 1057  * @param       data_caps       The backing NVRAM data capabilities
 1058  *                              (see bhnd_nvram_data_caps()).
 1059  * @param[out]  info            On success, the parsed variable name info.
 1060  * 
 1061  * @retval 0            success
 1062  * @retval non-zero     if parsing @p name otherwise fails, a regular unix
 1063  *                      error code will be returned.
 1064  */
 1065 int
 1066 bhnd_nvstore_parse_name_info(const char *name, bhnd_nvstore_name_type type,
 1067     uint32_t data_caps, bhnd_nvstore_name_info *info)
 1068 {
 1069         const char      *p;
 1070         char            *endp;
 1071 
 1072         /* Skip path parsing? */
 1073         if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) {
 1074                 /* devpath declaration? (devpath0=pci/1/1) */
 1075                 if (strncmp(name, "devpath", strlen("devpath")) == 0) {
 1076                         u_long alias;
 1077 
 1078                         /* Perform standard validation on the relative
 1079                          * variable name */
 1080                         if (type != BHND_NVSTORE_NAME_INTERNAL &&
 1081                             !bhnd_nvram_validate_name(name))
 1082                         {
 1083                                 return (ENOENT);
 1084                         }
 1085 
 1086                         /* Parse alias value that should follow a 'devpath'
 1087                          * prefix */
 1088                         p = name + strlen("devpath");
 1089                         alias = strtoul(p, &endp, 10);
 1090                         if (endp != p && *endp == '\0') {
 1091                                 info->type = BHND_NVSTORE_ALIAS_DECL;
 1092                                 info->path_type = BHND_NVSTORE_PATH_ALIAS;
 1093                                 info->name = name;
 1094                                 info->path.alias.value = alias;
 1095 
 1096                                 return (0);
 1097                         }
 1098                 }
 1099 
 1100                 /* device aliased variable? (0:varname) */
 1101                 if (bhnd_nv_isdigit(*name)) {
 1102                         u_long alias;
 1103 
 1104                         /* Parse '0:' alias prefix */
 1105                         alias = strtoul(name, &endp, 10);
 1106                         if (endp != name && *endp == ':') {
 1107                                 /* Perform standard validation on the relative
 1108                                  * variable name */
 1109                                 if (type != BHND_NVSTORE_NAME_INTERNAL &&
 1110                                     !bhnd_nvram_validate_name(name))
 1111                                 {
 1112                                         return (ENOENT);
 1113                                 }
 1114 
 1115                                 info->type = BHND_NVSTORE_VAR;
 1116                                 info->path_type = BHND_NVSTORE_PATH_ALIAS;
 1117 
 1118                                 /* name follows 0: prefix */
 1119                                 info->name = endp + 1;
 1120                                 info->path.alias.value = alias;
 1121 
 1122                                 return (0);
 1123                         }
 1124                 }
 1125 
 1126                 /* device variable? (pci/1/1/varname) */
 1127                 if ((p = strrchr(name, '/')) != NULL) {
 1128                         const char      *path, *relative_name;
 1129                         size_t           path_len;
 1130 
 1131                         /* Determine the path length; 'p' points at the last
 1132                          * path separator in 'name' */
 1133                         path_len = p - name;
 1134                         path = name;
 1135 
 1136                         /* The relative variable name directly follows the
 1137                          * final path separator '/' */
 1138                         relative_name = path + path_len + 1;
 1139 
 1140                         /* Now that we calculated the name offset, exclude all
 1141                          * trailing '/' characters from the path length */
 1142                         while (path_len > 0 && path[path_len-1] == '/')
 1143                                 path_len--;
 1144 
 1145                         /* Perform standard validation on the relative
 1146                          * variable name */
 1147                         if (type != BHND_NVSTORE_NAME_INTERNAL &&
 1148                             !bhnd_nvram_validate_name(relative_name))
 1149                         {
 1150                                 return (ENOENT);
 1151                         }
 1152 
 1153                         /* Initialize result with pointers into the name
 1154                          * buffer */
 1155                         info->type = BHND_NVSTORE_VAR;
 1156                         info->path_type = BHND_NVSTORE_PATH_STRING;
 1157                         info->name = relative_name;
 1158                         info->path.str.value = path;
 1159                         info->path.str.value_len = path_len;
 1160 
 1161                         return (0);
 1162                 }
 1163         }
 1164 
 1165         /* If all other parsing fails, the result is a simple variable with
 1166          * an implicit path of "/" */
 1167         if (type != BHND_NVSTORE_NAME_INTERNAL &&
 1168             !bhnd_nvram_validate_name(name))
 1169         {
 1170                 /* Invalid relative name */
 1171                 return (ENOENT);
 1172         }
 1173 
 1174         info->type = BHND_NVSTORE_VAR;
 1175         info->path_type = BHND_NVSTORE_PATH_STRING;
 1176         info->name = name;
 1177         info->path.str.value = BHND_NVSTORE_ROOT_PATH;
 1178         info->path.str.value_len = BHND_NVSTORE_ROOT_PATH_LEN;
 1179 
 1180         return (0);
 1181 }

Cache object: 70bd13f088e56a6a34649d49cd368e20


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.