Page MenuHomeFreeBSD

D8760.id22830.diff
No OneTemporary

D8760.id22830.diff

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

File Metadata

Mime Type
text/plain
Expires
Mon, Jan 27, 8:47 PM (8 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16212139
Default Alt Text
D8760.id22830.diff (108 KB)

Event Timeline