From owner-svn-src-all@freebsd.org Mon Dec 19 20:28:29 2016 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id AD0B5C88A27; Mon, 19 Dec 2016 20:28:29 +0000 (UTC) (envelope-from landonf@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 755991BE9; Mon, 19 Dec 2016 20:28:29 +0000 (UTC) (envelope-from landonf@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id uBJKSS30056599; Mon, 19 Dec 2016 20:28:28 GMT (envelope-from landonf@FreeBSD.org) Received: (from landonf@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id uBJKSRs4056590; Mon, 19 Dec 2016 20:28:27 GMT (envelope-from landonf@FreeBSD.org) Message-Id: <201612192028.uBJKSRs4056590@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: landonf set sender to landonf@FreeBSD.org using -f From: "Landon J. Fuller" Date: Mon, 19 Dec 2016 20:28:27 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r310295 - in head/sys: conf dev/bhnd/nvram modules/bhnd X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 19 Dec 2016 20:28:29 -0000 Author: landonf Date: Mon Dec 19 20:28:27 2016 New Revision: 310295 URL: https://svnweb.freebsd.org/changeset/base/310295 Log: 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 Added: head/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c (contents, props changed) Modified: head/sys/conf/files head/sys/dev/bhnd/nvram/bhnd_nvram_data.c head/sys/dev/bhnd/nvram/bhnd_nvram_data.h head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h head/sys/dev/bhnd/nvram/bhnd_nvram_private.h head/sys/dev/bhnd/nvram/bhnd_nvram_store.c head/sys/dev/bhnd/nvram/bhnd_nvram_store.h head/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h head/sys/dev/bhnd/nvram/bhnd_nvram_subr.c head/sys/modules/bhnd/Makefile Modified: head/sys/conf/files ============================================================================== --- head/sys/conf/files Mon Dec 19 20:26:10 2016 (r310294) +++ head/sys/conf/files Mon Dec 19 20:28:27 2016 (r310295) @@ -1244,6 +1244,7 @@ dev/bhnd/nvram/bhnd_nvram_ioptr.c option 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 Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data.c ============================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data.c Mon Dec 19 20:26:10 2016 (r310294) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data.c Mon Dec 19 20:28:27 2016 (r310295) @@ -350,7 +350,26 @@ bhnd_nvram_data_caps(struct bhnd_nvram_d 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 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 @@ bhnd_nvram_data_generic_find(struct bhnd } /** + * 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 @@ bhnd_nvram_data_getvar(struct bhnd_nvram 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 * 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_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 @@ bhnd_nvram_data_generic_rp_getvar(struct } /** + * 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_ { 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)); +} Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data.h ============================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data.h Mon Dec 19 20:26:10 2016 (r310294) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data.h Mon Dec 19 20:28:27 2016 (r310295) @@ -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 b 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 b 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( 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_ */ Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c ============================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c Mon Dec 19 20:26:10 2016 (r310294) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c Mon Dec 19 20:28:27 2016 (r310295) @@ -618,7 +618,7 @@ bhnd_nvram_bcm_next(struct bhnd_nvram_da return (NULL); } - *cookiep = (void *)(uintptr_t)envp; + *cookiep = __DECONST(void *, envp); return (envp); } @@ -629,12 +629,52 @@ bhnd_nvram_bcm_find(struct bhnd_nvram_da } 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 @@ bhnd_nvram_bcm_getvar_name(struct bhnd_n 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. Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c ============================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c Mon Dec 19 20:26:10 2016 (r310294) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c Mon Dec 19 20:28:27 2016 (r310295) @@ -351,12 +351,32 @@ bhnd_nvram_bcmraw_find(struct bhnd_nvram } 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 @@ bhnd_nvram_bcmraw_getvar_name(struct bhn /* 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); +} Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c ============================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c Mon Dec 19 20:26:10 2016 (r310294) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c Mon Dec 19 20:28:27 2016 (r310295) @@ -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_d 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 @@ bhnd_nvram_btxt_next(struct bhnd_nvram_d } 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 @@ bhnd_nvram_btxt_getvar_ptr(struct bhnd_n 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_ 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_ 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 @@ bhnd_nvram_btxt_seek_next(struct bhnd_nv *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); +} Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c ============================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c Mon Dec 19 20:26:10 2016 (r310294) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c Mon Dec 19 20:28:27 2016 (r310295) @@ -666,25 +666,34 @@ bhnd_nvram_sprom_read_offset(struct bhnd 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_nvra * 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_nvra } 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_nvra /* * 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_nvra /* 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_nvra 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; - /* Perform value coercion from our local representation */ - error = bhnd_nvram_val_init(&val, var->fmt, inp, ilen, var->type, - BHND_NVRAM_VAL_BORROW_DATA); + /* 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_nvra 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 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. * Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c ============================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c Mon Dec 19 20:26:10 2016 (r310294) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c Mon Dec 19 20:28:27 2016 (r310295) @@ -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, @@ -368,12 +372,32 @@ bhnd_nvram_tlv_find(struct bhnd_nvram_da } 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 @@ bhnd_nvram_tlv_getvar_name(struct bhnd_n 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; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***