bhnd(4): NVRAM device path support.
Implements bhnd_nvram_store support for parsing and operating over NVRAM device paths, and device path aliases, as well as tracking per-path NVRAM variable writes. Approved by: adrian (mentor) Differential Revision: https://reviews.freebsd.org/D8760
This commit is contained in:
parent
b13a0eca7a
commit
9f101c9c7c
@ -1244,6 +1244,7 @@ dev/bhnd/nvram/bhnd_nvram_ioptr.c optional bhnd
|
||||
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
|
||||
|
@ -350,7 +350,26 @@ bhnd_nvram_data_caps(struct bhnd_nvram_data *nv)
|
||||
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 @@ bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv, const char *name)
|
||||
|
||||
cookiep = NULL;
|
||||
while ((next = bhnd_nvram_data_next(nv, &cookiep))) {
|
||||
if (strcasecmp(name, next) == 0)
|
||||
if (strcmp(name, next) == 0)
|
||||
return (cookiep);
|
||||
}
|
||||
|
||||
@ -396,6 +415,37 @@ bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv, const char *name)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -423,6 +473,58 @@ bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
|
||||
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 @@ bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
|
||||
* 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_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, void *cookiep,
|
||||
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);
|
||||
@ -486,6 +570,63 @@ bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, void *cookiep,
|
||||
return (error);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ bhnd_nvram_data_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
@ -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 @@ void bhnd_nvram_data_release(struct bhnd_nvram_data *nv);
|
||||
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 @@ uint32_t bhnd_nvram_data_caps(struct bhnd_nvram_data *nv);
|
||||
|
||||
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 void *bhnd_nvram_data_getvar_ptr(struct bhnd_nvram_data *nv,
|
||||
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_ */
|
||||
|
@ -618,7 +618,7 @@ bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
*cookiep = (void *)(uintptr_t)envp;
|
||||
*cookiep = __DECONST(void *, envp);
|
||||
return (envp);
|
||||
}
|
||||
|
||||
@ -628,6 +628,39 @@ bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name)
|
||||
return (bhnd_nvram_data_generic_find(nv, name));
|
||||
}
|
||||
|
||||
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)
|
||||
@ -635,6 +668,13 @@ bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
|
||||
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 @@ bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
|
||||
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.
|
||||
|
@ -350,6 +350,19 @@ bhnd_nvram_bcmraw_find(struct bhnd_nvram_data *nv, const char *name)
|
||||
return (bhnd_nvram_data_generic_find(nv, name));
|
||||
}
|
||||
|
||||
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)
|
||||
@ -357,6 +370,13 @@ bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
|
||||
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 @@ bhnd_nvram_bcmraw_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
|
||||
/* 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);
|
||||
}
|
||||
|
@ -77,8 +77,10 @@ union bhnd_nvram_btxt_ident {
|
||||
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 @@ bhnd_nvram_btxt_next(struct bhnd_nvram_data *nv, void **cookiep)
|
||||
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) {
|
||||
@ -362,6 +369,19 @@ bhnd_nvram_btxt_next(struct bhnd_nvram_data *nv, void **cookiep)
|
||||
return (nptr);
|
||||
}
|
||||
|
||||
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)
|
||||
@ -369,6 +389,13 @@ bhnd_nvram_btxt_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
|
||||
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 @@ bhnd_nvram_btxt_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
|
||||
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 @@ bhnd_nvram_btxt_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
|
||||
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 @@ bhnd_nvram_btxt_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
|
||||
return (ptr);
|
||||
}
|
||||
|
||||
/* Convert cookie back to an I/O offset */
|
||||
static size_t
|
||||
bhnd_nvram_btxt_io_offset(struct bhnd_nvram_btxt *btxt, void *cookiep)
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
size_t io_size;
|
||||
uintptr_t cval;
|
||||
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_cookiep_to_offset(struct bhnd_nvram_btxt *btxt, void *cookiep)
|
||||
{
|
||||
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 @@ bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io, size_t *offset)
|
||||
*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);
|
||||
}
|
||||
|
@ -666,25 +666,34 @@ bhnd_nvram_sprom_read_offset(struct bhnd_nvram_sprom *sp,
|
||||
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 @@ bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
|
||||
* 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 @@ bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
|
||||
}
|
||||
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 @@ bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
|
||||
/*
|
||||
* 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 @@ bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
|
||||
|
||||
/* 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 @@ bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
|
||||
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);
|
||||
}
|
||||
|
||||
/* Perform value coercion from our local representation */
|
||||
error = bhnd_nvram_val_init(&val, var->fmt, inp, ilen, var->type,
|
||||
BHND_NVRAM_VAL_BORROW_DATA);
|
||||
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;
|
||||
|
||||
/* 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 @@ bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
|
||||
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 @@ bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
|
||||
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.
|
||||
*
|
||||
|
@ -82,6 +82,10 @@ struct bhnd_nvram_tlv_env {
|
||||
(((_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,
|
||||
@ -367,6 +371,19 @@ bhnd_nvram_tlv_find(struct bhnd_nvram_data *nv, const char *name)
|
||||
return (bhnd_nvram_data_generic_find(nv, name));
|
||||
}
|
||||
|
||||
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)
|
||||
@ -374,6 +391,13 @@ bhnd_nvram_tlv_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
|
||||
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 @@ bhnd_nvram_tlv_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
|
||||
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 @@ bhnd_nvram_tlv_get_env(struct bhnd_nvram_tlv *tlv, void *cookiep)
|
||||
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);
|
||||
|
||||
return ((size_t)cval);
|
||||
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)offset);
|
||||
}
|
||||
|
@ -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 @@ typedef uint32_t (bhnd_nvram_data_op_caps)(struct bhnd_nvram_data *nv);
|
||||
|
||||
/** @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 @@ typedef const void *(bhnd_nvram_data_op_getvar_ptr)(
|
||||
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 @@ struct bhnd_nvram_data {
|
||||
_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
|
||||
|
@ -73,14 +73,43 @@ MALLOC_DECLARE(M_BHND_NVRAM);
|
||||
#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 @@ MALLOC_DECLARE(M_BHND_NVRAM);
|
||||
#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_parse_field(const char **inp,
|
||||
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
|
||||
|
@ -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 @@ __FBSDID("$FreeBSD$");
|
||||
* 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 @@ bhnd_nvram_store_new(struct bhnd_nvram_store **store,
|
||||
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;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Generate all indices */
|
||||
if ((error = bhnd_nvram_generate_index(sc)))
|
||||
/* Register required root path */
|
||||
error = bhnd_nvstore_register_path(sc, BHND_NVSTORE_ROOT_PATH,
|
||||
BHND_NVSTORE_ROOT_PATH_LEN);
|
||||
if (error)
|
||||
goto cleanup;
|
||||
|
||||
BHND_NVSTORE_LOCK_INIT(sc);
|
||||
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"));
|
||||
|
||||
/* 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,97 +189,339 @@ bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store,
|
||||
void
|
||||
bhnd_nvram_store_free(struct bhnd_nvram_store *sc)
|
||||
{
|
||||
struct bhnd_nvstore_path *dpath, *dnext;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
LIST_FOREACH_SAFE(dpath, &sc->paths, dp_link, dnext) {
|
||||
bhnd_nv_free(dpath->path);
|
||||
bhnd_nv_free(dpath);
|
||||
}
|
||||
/* 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->pending != NULL)
|
||||
nvlist_destroy(sc->pending);
|
||||
if (sc->data != NULL)
|
||||
bhnd_nvram_data_release(sc->data);
|
||||
|
||||
if (sc->idx != NULL)
|
||||
bhnd_nv_free(sc->idx);
|
||||
|
||||
if (sc->nv != NULL)
|
||||
bhnd_nvram_data_release(sc->nv);
|
||||
|
||||
BHND_NVSTORE_LOCK_DESTROY(sc);
|
||||
bhnd_nv_free(sc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
int error;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Parse the name info */
|
||||
error = bhnd_nvstore_parse_name_info(name,
|
||||
BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* Allocate per-path index instances */
|
||||
for (size_t i = 0; i < nitems(sc->paths); i++) {
|
||||
bhnd_nvstore_path *path;
|
||||
|
||||
LIST_FOREACH(path, &sc->paths[i], np_link) {
|
||||
path->index = bhnd_nvstore_index_new(path->num_vars);
|
||||
if (path->index == NULL)
|
||||
return (ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
/* Populate per-path indexes */
|
||||
cookiep = NULL;
|
||||
while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
|
||||
bhnd_nvstore_name_info info;
|
||||
bhnd_nvstore_path *path;
|
||||
|
||||
/* Parse the name info */
|
||||
error = bhnd_nvstore_parse_name_info(name,
|
||||
BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse and register path and path alias entries for all declarations found in
|
||||
* the NVRAM data backing @p nvram.
|
||||
*
|
||||
* @param sc The NVRAM store instance.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval non-zero If parsing fails, a regular unix error code will be
|
||||
* returned.
|
||||
*/
|
||||
static int
|
||||
bhnd_nvstore_parse_path_entries(struct bhnd_nvram_store *sc)
|
||||
{
|
||||
const char *name;
|
||||
void *cookiep;
|
||||
int error;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* Otherwise, parse and register all paths and path aliases */
|
||||
cookiep = NULL;
|
||||
while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
|
||||
bhnd_nvstore_name_info info;
|
||||
|
||||
/* Parse the name info */
|
||||
error = bhnd_nvstore_parse_name_info(name,
|
||||
BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* Register the path */
|
||||
error = bhnd_nvstore_var_register_path(sc, &info, cookiep);
|
||||
if (error) {
|
||||
BHND_NV_LOG("failed to register path for %s: %d\n",
|
||||
name, error);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
* @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 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.
|
||||
* @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 *buf, size_t *len, bhnd_nvram_type type)
|
||||
void *outp, size_t *olen, bhnd_nvram_type otype)
|
||||
{
|
||||
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_name_info info;
|
||||
bhnd_nvstore_path *path;
|
||||
bhnd_nvram_prop *prop;
|
||||
void *cookiep;
|
||||
int error;
|
||||
|
||||
BHND_NVSTORE_LOCK(sc);
|
||||
|
||||
/* Is variable marked for deletion? */
|
||||
if (nvlist_exists_null(sc->pending, name)) {
|
||||
BHND_NVSTORE_UNLOCK(sc);
|
||||
return (ENOENT);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Coerce borrowed data reference before releasing
|
||||
* our lock. */
|
||||
error = bhnd_nvram_value_coerce(inp, ilen, itype, buf, len,
|
||||
type);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
BHND_NVSTORE_UNLOCK(sc);
|
||||
/* Not found */
|
||||
error = ENOENT;
|
||||
|
||||
finished:
|
||||
BHND_NVSTORE_UNLOCK(sc);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common bhnd_nvram_store_set*() and bhnd_nvram_store_unsetvar()
|
||||
* implementation.
|
||||
*
|
||||
* If @p value is NULL, the variable will be marked for deletion.
|
||||
*/
|
||||
static int
|
||||
bhnd_nvram_store_setval_common(struct bhnd_nvram_store *sc, const char *name,
|
||||
bhnd_nvram_val *value)
|
||||
{
|
||||
bhnd_nvstore_path *path;
|
||||
bhnd_nvstore_name_info info;
|
||||
int error;
|
||||
|
||||
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);
|
||||
} else if (nvlist_exists(sc->pending, name)) {
|
||||
BHND_NV_PANIC("invalid value type for pending change %s", name);
|
||||
}
|
||||
|
||||
/* Fetch variable from parsed NVRAM data. */
|
||||
if ((cookiep = bhnd_nvram_index_lookup(sc, name)) == NULL) {
|
||||
BHND_NVSTORE_UNLOCK(sc);
|
||||
return (ENOENT);
|
||||
}
|
||||
/* Fetch the variable's enclosing path entry */
|
||||
if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL)
|
||||
return (error);
|
||||
|
||||
/* Let the parser itself perform value coercion */
|
||||
error = bhnd_nvram_data_getvar(sc->nv, cookiep, buf, len, type);
|
||||
/* Register the update entry */
|
||||
return (bhnd_nvstore_path_register_update(sc, path, info.name, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
BHND_NVSTORE_LOCK(sc);
|
||||
error = bhnd_nvram_store_setval_common(sc, name, value);
|
||||
BHND_NVSTORE_UNLOCK(sc);
|
||||
|
||||
return (error);
|
||||
@ -267,310 +532,56 @@ bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,
|
||||
*
|
||||
* @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.
|
||||
* @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 was not found.
|
||||
* @retval EINVAL If @p len does not match the expected variable size.
|
||||
* @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 *buf, size_t len, bhnd_nvram_type type)
|
||||
const void *inp, size_t ilen, bhnd_nvram_type itype)
|
||||
{
|
||||
const char *inp;
|
||||
char vbuf[512];
|
||||
bhnd_nvram_val val;
|
||||
int error;
|
||||
|
||||
/* Verify name validity */
|
||||
if (!bhnd_nvram_validate_name(name, strlen(name)))
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
BHND_NVSTORE_LOCK(sc);
|
||||
error = bhnd_nvram_store_setval_common(sc, name, &val);
|
||||
BHND_NVSTORE_UNLOCK(sc);
|
||||
|
||||
/* 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;
|
||||
bhnd_nvram_val_release(&val);
|
||||
|
||||
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));
|
||||
return (error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and register all device paths and path aliases in @p nvram.
|
||||
* Unset an NVRAM variable.
|
||||
*
|
||||
* @param sc The NVRAM parser state.
|
||||
* @param sc The NVRAM parser state.
|
||||
* @param name The NVRAM variable name.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval non-zero If registering device paths fails, a regular unix
|
||||
* error code will be returned.
|
||||
* @retval ENOENT The requested variable @p name was not found.
|
||||
* @retval EINVAL If @p name is read-only.
|
||||
*/
|
||||
static int
|
||||
bhnd_nvram_register_devpaths(struct bhnd_nvram_store *sc)
|
||||
int
|
||||
bhnd_nvram_store_unsetvar(struct bhnd_nvram_store *sc, const char *name)
|
||||
{
|
||||
const char *name;
|
||||
void *cookiep;
|
||||
int error;
|
||||
int error;
|
||||
|
||||
/* Skip if backing parser does not support device paths */
|
||||
if (!(bhnd_nvram_data_caps(sc->nv) & BHND_NVRAM_DATA_CAP_DEVPATHS))
|
||||
return (0);
|
||||
BHND_NVSTORE_LOCK(sc);
|
||||
error = bhnd_nvram_store_setval_common(sc, name, BHND_NVRAM_VAL_NULL);
|
||||
BHND_NVSTORE_UNLOCK(sc);
|
||||
|
||||
/* Parse and register all device 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;
|
||||
}
|
||||
|
||||
/* Determine path value length */
|
||||
error = bhnd_nvram_data_getvar(sc->nv, cookiep, NULL, &path_len,
|
||||
BHND_NVRAM_TYPE_STRING);
|
||||
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);
|
||||
if (error) {
|
||||
bhnd_nv_free(path);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Register path alias */
|
||||
devpath = bhnd_nv_malloc(sizeof(*devpath));
|
||||
if (devpath == NULL) {
|
||||
bhnd_nv_free(path);
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
devpath->index = index;
|
||||
devpath->path = path;
|
||||
LIST_INSERT_HEAD(&sc->paths, devpath, dp_link);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate all indices for the NVRAM data backing @p nvram.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
static int
|
||||
bhnd_nvram_generate_index(struct bhnd_nvram_store *sc)
|
||||
{
|
||||
const char *name;
|
||||
void *cookiep;
|
||||
size_t idx_bytes;
|
||||
size_t num_vars;
|
||||
int error;
|
||||
|
||||
/* Parse and register all device path aliases */
|
||||
if ((error = bhnd_nvram_register_devpaths(sc)))
|
||||
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);
|
||||
|
||||
/* ... or if the backing data instance implements indexed lookup
|
||||
* internally */
|
||||
if (bhnd_nvram_data_caps(sc->nv) & BHND_NVRAM_DATA_CAP_INDEXED)
|
||||
return (0);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
sc->idx->num_cookiep = num_vars;
|
||||
|
||||
#ifdef _KERNEL
|
||||
if (bootverbose) {
|
||||
BHND_NV_LOG("allocated %zu byte index for %zu variables\n",
|
||||
idx_bytes, num_vars);
|
||||
}
|
||||
#endif /* _KERNEL */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Save the variable's cookiep */
|
||||
sc->idx->cookiep[i] = cookiep;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform an index lookup of @p name, returning the associated cookie
|
||||
* value, or NULL if the variable does not exist.
|
||||
*
|
||||
* @param sc The NVRAM parser state.
|
||||
* @param name The variable to search for.
|
||||
*/
|
||||
static void *
|
||||
bhnd_nvram_index_lookup(struct bhnd_nvram_store *sc, const char *name)
|
||||
{
|
||||
void *cookiep;
|
||||
const char *indexed_name;
|
||||
size_t min, mid, max;
|
||||
int order;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return (NULL);
|
||||
return (error);
|
||||
}
|
||||
|
@ -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 @@ int bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store,
|
||||
|
||||
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_ */
|
||||
|
1182
sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c
Normal file
1182
sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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_PATH_ALIAS_NONE ULONG_MAX
|
||||
#define BHND_NVSTORE_ROOT_PATH "/"
|
||||
#define BHND_NVSTORE_ROOT_PATH_LEN sizeof(BHND_NVSTORE_ROOT_PATH)
|
||||
|
||||
LIST_HEAD(bhnd_nvstore_paths, bhnd_nvstore_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)
|
||||
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
|
||||
LIST_ENTRY(bhnd_nvstore_path) dp_link;
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/** 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
|
||||
|
@ -614,44 +614,45 @@ bhnd_nvram_get_vardefn(size_t id)
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -949,6 +950,46 @@ bhnd_nvram_parse_int(const char *str, size_t maxlen, u_int base,
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -39,6 +39,7 @@ SRCS+= bhnd_nvram_data.c \
|
||||
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 \
|
||||
|
Loading…
Reference in New Issue
Block a user