Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F108609369
D8760.id22830.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
108 KB
Referenced Files
None
Subscribers
None
D8760.id22830.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D8760: bhnd(4): NVRAM device path support.
Attached
Detach File
Event Timeline
Log In to Comment