Index: head/sys/dev/bhnd/nvram/bhnd_nvram.h =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram.h (revision 310292) +++ head/sys/dev/bhnd/nvram/bhnd_nvram.h (revision 310293) @@ -1,126 +1,142 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _BHND_NVRAM_BHND_NVRAM_H_ #define _BHND_NVRAM_BHND_NVRAM_H_ #ifdef _KERNEL #include #else /* !_KERNEL */ #include #include #endif /* _KERNEL */ /** + * BHND NVRAM boolean type; guaranteed to be exactly 8-bits, representing + * true as integer constant 1, and false as integer constant 0. + * + * Compatible with stdbool constants (true, false). + */ +typedef uint8_t bhnd_nvram_bool_t; + +/** * NVRAM data sources supported by bhnd(4) devices. */ typedef enum { BHND_NVRAM_SRC_OTP, /**< On-chip one-time-programmable * memory. */ BHND_NVRAM_SRC_FLASH, /**< External flash */ BHND_NVRAM_SRC_SPROM, /**< External serial EEPROM. */ BHND_NVRAM_SRC_UNKNOWN /**< No NVRAM source is directly * attached. * * This will be returned by ChipCommon * revisions (rev <= 31) used in early * chipsets that vend SPROM/OTP via the * native host bridge interface. * * For example, PCMCIA cards may vend * Broadcom NVRAM data via their standard CIS * table, and earlier PCI(e) devices map * SPROM statically into PCI BARs, and the * control registers into PCI config space. * This will also be returned on later * devices that are attached via PCI(e) to * BHND SoCs, but do not include an attached * SPROM, or programmed OTP. On such SoCs, * NVRAM configuration for individual devices * is provided by a common platform NVRAM * device. */ } bhnd_nvram_src; /** * NVRAM data types. * * @internal * * All primitive (non-array) constants should be representable as a 4-bit * integer (e.g. 0-15) to support SPROM_OPCODE_TYPE_IMM encoding as used by * nvram_map_gen.awk. */ typedef enum { BHND_NVRAM_TYPE_UINT8 = 0, /**< unsigned 8-bit integer */ BHND_NVRAM_TYPE_UINT16 = 1, /**< unsigned 16-bit integer */ BHND_NVRAM_TYPE_UINT32 = 2, /**< unsigned 32-bit integer */ BHND_NVRAM_TYPE_UINT64 = 3, /**< signed 64-bit integer */ BHND_NVRAM_TYPE_INT8 = 4, /**< signed 8-bit integer */ BHND_NVRAM_TYPE_INT16 = 5, /**< signed 16-bit integer */ BHND_NVRAM_TYPE_INT32 = 6, /**< signed 32-bit integer */ BHND_NVRAM_TYPE_INT64 = 7, /**< signed 64-bit integer */ BHND_NVRAM_TYPE_CHAR = 8, /**< ASCII/UTF-8 character */ BHND_NVRAM_TYPE_STRING = 9, /**< ASCII/UTF-8 NUL-terminated string */ + BHND_NVRAM_TYPE_BOOL = 10, /**< uint8 boolean value. see + bhnd_nvram_bool_t. */ + BHND_NVRAM_TYPE_NULL = 11, /**< NULL (empty) value */ + BHND_NVRAM_TYPE_DATA = 12, /**< opaque octet string */ /* 10-15 reserved for primitive (non-array) types */ BHND_NVRAM_TYPE_UINT8_ARRAY = 16, /**< array of uint8 integers */ BHND_NVRAM_TYPE_UINT16_ARRAY = 17, /**< array of uint16 integers */ BHND_NVRAM_TYPE_UINT32_ARRAY = 18, /**< array of uint32 integers */ BHND_NVRAM_TYPE_UINT64_ARRAY = 19, /**< array of uint64 integers */ BHND_NVRAM_TYPE_INT8_ARRAY = 20, /**< array of int8 integers */ BHND_NVRAM_TYPE_INT16_ARRAY = 21, /**< array of int16 integers */ BHND_NVRAM_TYPE_INT32_ARRAY = 22, /**< array of int32 integers */ BHND_NVRAM_TYPE_INT64_ARRAY = 23, /**< array of int64 integers */ BHND_NVRAM_TYPE_CHAR_ARRAY = 24, /**< array of ASCII/UTF-8 characters */ BHND_NVRAM_TYPE_STRING_ARRAY = 25, /**< array of ASCII/UTF-8 NUL-terminated strings */ + BHND_NVRAM_TYPE_BOOL_ARRAY = 26, /**< array of uint8 boolean + values */ } bhnd_nvram_type; + bool bhnd_nvram_is_signed_type(bhnd_nvram_type type); bool bhnd_nvram_is_unsigned_type(bhnd_nvram_type type); bool bhnd_nvram_is_int_type(bhnd_nvram_type type); bool bhnd_nvram_is_array_type(bhnd_nvram_type type); bhnd_nvram_type bhnd_nvram_base_type(bhnd_nvram_type type); +bhnd_nvram_type bhnd_nvram_raw_type(bhnd_nvram_type type); const char *bhnd_nvram_type_name(bhnd_nvram_type type); size_t bhnd_nvram_type_width(bhnd_nvram_type type); size_t bhnd_nvram_type_host_align(bhnd_nvram_type type); const char *bhnd_nvram_string_array_next(const char *inp, size_t ilen, const char *prev, size_t *olen); #endif /* _BHND_NVRAM_BHND_NVRAM_H_ */ Index: head/sys/dev/bhnd/nvram/bhnd_nvram_plist.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_plist.c (revision 310292) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_plist.c (revision 310293) @@ -1,947 +1,980 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #ifdef _KERNEL #include #else /* !_KERNEL */ #include #include #include #include #endif /* _KERNEL */ #include "bhnd_nvram_plistvar.h" #include "bhnd_nvram_private.h" static bhnd_nvram_plist_entry *bhnd_nvram_plist_get_entry( bhnd_nvram_plist *plist, const char *name); /** * Allocate and initialize a new, empty property list. * * The caller is responsible for releasing the returned property value * via bhnd_nvram_plist_release(). * * @retval non-NULL success * @retval NULL if allocation fails. */ bhnd_nvram_plist * bhnd_nvram_plist_new(void) { bhnd_nvram_plist *plist; plist = bhnd_nv_calloc(1, sizeof(*plist)); if (plist == NULL) return NULL; /* Implicit caller-owned reference */ plist->refs = 1; /* Initialize entry list */ plist->num_entries = 0; TAILQ_INIT(&plist->entries); /* Initialize entry hash table */ for (size_t i = 0; i < nitems(plist->names); i++) LIST_INIT(&plist->names[i]); return (plist); } /** * Retain a reference and return @p plist to the caller. * * The caller is responsible for releasing their reference ownership via * bhnd_nvram_plist_release(). * * @param plist The property list to be retained. */ bhnd_nvram_plist * bhnd_nvram_plist_retain(bhnd_nvram_plist *plist) { BHND_NV_ASSERT(plist->refs >= 1, ("plist over-released")); refcount_acquire(&plist->refs); return (plist); } /** * Release a reference to @p plist. * * If this is the last reference, all associated resources will be freed. * * @param plist The property list to be released. */ void bhnd_nvram_plist_release(bhnd_nvram_plist *plist) { bhnd_nvram_plist_entry *ple, *ple_next; BHND_NV_ASSERT(plist->refs >= 1, ("plist over-released")); /* Drop reference */ if (!refcount_release(&plist->refs)) return; /* Free all property entries */ TAILQ_FOREACH_SAFE(ple, &plist->entries, pl_link, ple_next) { bhnd_nvram_prop_release(ple->prop); bhnd_nv_free(ple); } /* Free plist instance */ bhnd_nv_free(plist); } /** * Return a shallow copy of @p plist. * * The caller is responsible for releasing the returned property value * via bhnd_nvram_plist_release(). * * @retval non-NULL success * @retval NULL if allocation fails. */ bhnd_nvram_plist * bhnd_nvram_plist_copy(bhnd_nvram_plist *plist) { bhnd_nvram_plist *copy; bhnd_nvram_prop *prop; int error; /* Allocate new, empty plist */ if ((copy = bhnd_nvram_plist_new()) == NULL) return (NULL); /* Append all properties */ prop = NULL; while ((prop = bhnd_nvram_plist_next(plist, prop)) != NULL) { error = bhnd_nvram_plist_append(copy, prop); if (error) { if (error != ENOMEM) { BHND_NV_LOG("error copying property: %d\n", error); } bhnd_nvram_plist_release(copy); return (NULL); } } /* Return ownership of the copy to our caller */ return (copy); } /** * Return the number of properties in @p plist. */ size_t bhnd_nvram_plist_count(bhnd_nvram_plist *plist) { return (plist->num_entries); } /** * Return true if @p plist contains a property name @p name, false otherwise. * * @param plist The property list to be queried. * @param name The property name to be queried. */ bool bhnd_nvram_plist_contains(bhnd_nvram_plist *plist, const char *name) { if (bhnd_nvram_plist_get_entry(plist, name) != NULL) return (true); return (false); } /** * Replace the current property value for a property matching the name * of @p prop, maintaining the property's current order in @p plist. * * If a matching property is not found in @p plist, @p prop will instead be * appended. * * @param plist The property list to be modified. * @param prop The replacement property. * * @retval 0 success * @retval ENOMEM if allocation fails. * @retval non-zero if modifying @p plist otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_plist_replace(bhnd_nvram_plist *plist, bhnd_nvram_prop *prop) { bhnd_nvram_plist_entry *entry; /* Fetch current entry */ entry = bhnd_nvram_plist_get_entry(plist, prop->name); if (entry == NULL) { /* Not found -- append property instead */ return (bhnd_nvram_plist_append(plist, prop)); } /* Replace the current entry's property reference */ bhnd_nvram_prop_release(entry->prop); entry->prop = bhnd_nvram_prop_retain(prop); return (0); } /** * Replace the current property value for a property matching @p name, * maintaining the property's order in @p plist. * * If @p name is not found in @p plist, a new property will be appended. * * @param plist The property list to be modified. * @param name The name of the property to be replaced. * @param val The replacement value for @p name. * * @retval 0 success * @retval ENOMEM if allocation fails. * @retval non-zero if modifying @p plist otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_plist_replace_val(bhnd_nvram_plist *plist, const char *name, bhnd_nvram_val *val) { bhnd_nvram_prop *prop; int error; /* Construct a new property instance for the name and value */ if ((prop = bhnd_nvram_prop_new(name, val)) == NULL) return (ENOMEM); /* Attempt replace */ error = bhnd_nvram_plist_replace(plist, prop); bhnd_nvram_prop_release(prop); return (error); } /** * Replace the current property value for a property matching @p name, copying * the new property value from the given @p inp buffer of @p itype and @p ilen. * * The current property order of @p name in @p plist will be maintained. * * If @p name is not found in @p plist, a new property will be appended. * * @param plist The property list to be modified. * @param name The name of the property to be replaced. * @param inp Input buffer. * @param ilen Input buffer length. * @param itype Input buffer type. * * @retval 0 success * @retval ENOMEM if allocation fails. * @retval non-zero if modifying @p plist otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_plist_replace_bytes(bhnd_nvram_plist *plist, const char *name, const void *inp, size_t ilen, bhnd_nvram_type itype) { bhnd_nvram_prop *prop; int error; if ((prop = bhnd_nvram_prop_bytes_new(name, inp, ilen, itype)) == NULL) return (ENOMEM); error = bhnd_nvram_plist_replace(plist, prop); bhnd_nvram_prop_release(prop); return (error); } /** * Replace the current property value for a property matching @p name, copying * the new property value from @p val. * * The current property order of @p name in @p plist will be maintained. * * If @p name is not found in @p plist, a new property will be appended. * * @param plist The property list to be modified. * @param name The name of the property to be replaced. * @param val The property's replacement string value. * * @retval 0 success * @retval ENOMEM if allocation fails. * @retval non-zero if modifying @p plist otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_plist_replace_string(bhnd_nvram_plist *plist, const char *name, const char *val) { return (bhnd_nvram_plist_replace_bytes(plist, name, val, strlen(val)+1, BHND_NVRAM_TYPE_STRING)); } /** * Remove the property entry for the property @p name, if any. * * @param plist The property list to be modified. * @param name The name of the property to be removed. */ void bhnd_nvram_plist_remove(bhnd_nvram_plist *plist, const char *name) { bhnd_nvram_plist_entry *entry; /* Fetch entry */ entry = bhnd_nvram_plist_get_entry(plist, name); if (entry == NULL) return; /* Remove from entry list and hash table */ TAILQ_REMOVE(&plist->entries, entry, pl_link); LIST_REMOVE(entry, pl_hash_link); /* Free plist entry */ bhnd_nvram_prop_release(entry->prop); bhnd_nv_free(entry); /* Decrement entry count */ BHND_NV_ASSERT(plist->num_entries > 0, ("entry count over-release")); plist->num_entries--; } /** * Fetch the property list entry for @p name, if any. * * @param plist The property list to be queried. * @param name The property name to be queried. * * @retval non-NULL if @p name is found. * @retval NULL if @p name is not found. */ static bhnd_nvram_plist_entry * bhnd_nvram_plist_get_entry(bhnd_nvram_plist *plist, const char *name) { bhnd_nvram_plist_entry_list *hash_list; bhnd_nvram_plist_entry *entry; uint32_t h; h = hash32_str(name, HASHINIT); hash_list = &plist->names[h % nitems(plist->names)]; LIST_FOREACH(entry, hash_list, pl_hash_link) { if (strcmp(entry->prop->name, name) == 0) return (entry); }; /* Not found */ return (NULL); } /** * Append all properties from @p tail to @p plist. * * @param plist The property list to be modified. * @param tail The property list to append. * * @retval 0 success * @retval ENOMEM if allocation fails. * @retval EEXIST an existing property from @p tail was found in @p plist. */ int bhnd_nvram_plist_append_list(bhnd_nvram_plist *plist, bhnd_nvram_plist *tail) { bhnd_nvram_prop *p; int error; p = NULL; while ((p = bhnd_nvram_plist_next(tail, p)) != NULL) { if ((error = bhnd_nvram_plist_append(plist, p))) return (error); } return (0); } /** * Append @p prop to @p plist. * * @param plist The property list to be modified. * @param prop The property to append. * * @retval 0 success * @retval ENOMEM if allocation fails. * @retval EEXIST an existing property with @p name was found in @p plist. */ int bhnd_nvram_plist_append(bhnd_nvram_plist *plist, bhnd_nvram_prop *prop) { bhnd_nvram_plist_entry_list *hash_list; bhnd_nvram_plist_entry *entry; uint32_t h; if (bhnd_nvram_plist_contains(plist, prop->name)) return (EEXIST); /* Have we hit the maximum representable entry count? */ if (plist->num_entries == SIZE_MAX) return (ENOMEM); /* Allocate new entry */ entry = bhnd_nv_malloc(sizeof(*entry)); if (entry == NULL) return (ENOMEM); entry->prop = bhnd_nvram_prop_retain(prop); /* Append to entry list */ TAILQ_INSERT_TAIL(&plist->entries, entry, pl_link); /* Add to name-based hash table */ h = hash32_str(prop->name, HASHINIT); hash_list = &plist->names[h % nitems(plist->names)]; LIST_INSERT_HEAD(hash_list, entry, pl_hash_link); /* Increment entry count */ plist->num_entries++; return (0); } /** * Append a new property to @p plist with @p name and @p val. * * @param plist The property list to be modified. * @param name The name of the property to be appended. * @param val The value of the property to be appended. * * @retval 0 success * @retval ENOMEM if allocation fails. * @retval EEXIST an existing property with @p name was found in @p plist. */ int bhnd_nvram_plist_append_val(bhnd_nvram_plist *plist, const char *name, bhnd_nvram_val *val) { bhnd_nvram_prop *prop; int error; if ((prop = bhnd_nvram_prop_new(name, val)) == NULL) return (ENOMEM); error = bhnd_nvram_plist_append(plist, prop); bhnd_nvram_prop_release(prop); return (error); } /** * Append a new property to @p plist, copying the property value from the * given @p inp buffer of @p itype and @p ilen. * * @param plist The property list to be modified. * @param name The name of the property to be appended. * @param inp Input buffer. * @param ilen Input buffer length. * @param itype Input buffer type. * * @retval 0 success * @retval ENOMEM if allocation fails. * @retval EEXIST an existing property with @p name was found in @p plist. */ int bhnd_nvram_plist_append_bytes(bhnd_nvram_plist *plist, const char *name, const void *inp, size_t ilen, bhnd_nvram_type itype) { bhnd_nvram_prop *prop; int error; if ((prop = bhnd_nvram_prop_bytes_new(name, inp, ilen, itype)) == NULL) return (ENOMEM); error = bhnd_nvram_plist_append(plist, prop); bhnd_nvram_prop_release(prop); return (error); } /** * Append a new string property to @p plist, copying the property value from * @p val. * * @param plist The property list to be modified. * @param name The name of the property to be appended. * @param val The new property's string value. * * @retval 0 success * @retval ENOMEM if allocation fails. * @retval EEXIST an existing property with @p name was found in @p plist. */ int bhnd_nvram_plist_append_string(bhnd_nvram_plist *plist, const char *name, const char *val) { return (bhnd_nvram_plist_append_bytes(plist, name, val, strlen(val)+1, BHND_NVRAM_TYPE_STRING)); } /** * Iterate over all properties in @p plist. * * @param plist The property list to be iterated. * @param prop A property in @p plist, or NULL to return the first * property in @p plist. * * @retval non-NULL A borrowed reference to the next property in @p plist. * @retval NULL If the end of the property list is reached or @p prop * is not found in @p plist. */ bhnd_nvram_prop * bhnd_nvram_plist_next(bhnd_nvram_plist *plist, bhnd_nvram_prop *prop) { bhnd_nvram_plist_entry *entry; if (prop == NULL) { if ((entry = TAILQ_FIRST(&plist->entries)) == NULL) return (NULL); return (entry->prop); } /* Look up previous property entry by name */ if ((entry = bhnd_nvram_plist_get_entry(plist, prop->name)) == NULL) return (NULL); /* The property instance must be identical */ if (entry->prop != prop) return (NULL); /* Fetch next entry */ if ((entry = TAILQ_NEXT(entry, pl_link)) == NULL) return (NULL); return (entry->prop); } /** * Return a borrowed reference to a named property, or NULL if @p name is * not found in @p plist. * * @param plist The property list to be queried. * @param name The name of the property to be returned. * * @retval non-NULL if @p name is found. * @retval NULL if @p name is not found. */ bhnd_nvram_prop * bhnd_nvram_plist_get_prop(bhnd_nvram_plist *plist, const char *name) { bhnd_nvram_plist_entry *entry; if ((entry = bhnd_nvram_plist_get_entry(plist, name)) == NULL) return (NULL); return (entry->prop); } /** * Return a borrowed reference to the named property's value, or NULL if * @p name is not found in @p plist. * * @param plist The property list to be queried. * @param name The name of the property to be returned. * * @retval non-NULL if @p name is found. * @retval NULL if @p name is not found. */ bhnd_nvram_val * bhnd_nvram_plist_get_val(bhnd_nvram_plist *plist, const char *name) { bhnd_nvram_prop *prop; if ((prop = bhnd_nvram_plist_get_prop(plist, name)) == NULL) return (NULL); return (bhnd_nvram_prop_val(prop)); } /** * Attempt to encode a named property's value as @p otype, writing the result * to @p outp. * * @param plist The property list to be queried. * @param name The name of the property value to be returned. * @param[out] outp On success, the 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 data type to be written to @p outp. * * @retval 0 success * @retval ENOENT If @p name is not found in @p plist. * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen * is too small to hold the encoded value. * @retval EFTYPE If value coercion from @p prop to @p otype is * impossible. * @retval ERANGE If value coercion would overflow (or underflow) the * a @p otype representation. */ int bhnd_nvram_plist_get_encoded(bhnd_nvram_plist *plist, const char *name, void *outp, size_t olen, bhnd_nvram_type otype) { bhnd_nvram_prop *prop; if ((prop = bhnd_nvram_plist_get_prop(plist, name)) == NULL) return (ENOENT); return (bhnd_nvram_prop_encode(prop, outp, &olen, otype)); } /** * Return the character representation of a named property's value. * * @param plist The property list to be queried. * @param name The name of the property value to be returned. * @param[out] val On success, the character value of @p name. * * @retval 0 success * @retval ENOENT If @p name is not found in @p plist. * @retval EFTYPE If coercion of the property's value to @p val. * @retval ERANGE If coercion of the property's value would overflow * (or underflow) @p val. */ int bhnd_nvram_plist_get_char(bhnd_nvram_plist *plist, const char *name, u_char *val) { return (bhnd_nvram_plist_get_encoded(plist, name, val, sizeof(*val), BHND_NVRAM_TYPE_CHAR)); } /** * Return the uint8 representation of a named property's value. * * @param plist The property list to be queried. * @param name The name of the property value to be returned. * @param[out] val On success, the uint8 value of @p name. * * @retval 0 success * @retval ENOENT If @p name is not found in @p plist. * @retval EFTYPE If coercion of the property's value to @p val. * @retval ERANGE If coercion of the property's value would overflow * (or underflow) @p val. */ int bhnd_nvram_plist_get_uint8(bhnd_nvram_plist *plist, const char *name, uint8_t *val) { return (bhnd_nvram_plist_get_encoded(plist, name, val, sizeof(*val), BHND_NVRAM_TYPE_UINT8)); } /** * Return the uint16 representation of a named property's value. * * @param plist The property list to be queried. * @param name The name of the property value to be returned. * @param[out] val On success, the uint16 value of @p name. * * @retval 0 success * @retval ENOENT If @p name is not found in @p plist. * @retval EFTYPE If coercion of the property's value to @p val. * @retval ERANGE If coercion of the property's value would overflow * (or underflow) @p val. */ int bhnd_nvram_plist_get_uint16(bhnd_nvram_plist *plist, const char *name, uint16_t *val) { return (bhnd_nvram_plist_get_encoded(plist, name, val, sizeof(*val), BHND_NVRAM_TYPE_UINT16)); } /** * Return the uint32 representation of a named property's value. * * @param plist The property list to be queried. * @param name The name of the property value to be returned. * @param[out] val On success, the uint32 value of @p name. * * @retval 0 success * @retval ENOENT If @p name is not found in @p plist. * @retval EFTYPE If coercion of the property's value to @p val. * @retval ERANGE If coercion of the property's value would overflow * (or underflow) @p val. */ int bhnd_nvram_plist_get_uint32(bhnd_nvram_plist *plist, const char *name, uint32_t *val) { return (bhnd_nvram_plist_get_encoded(plist, name, val, sizeof(*val), BHND_NVRAM_TYPE_UINT32)); } /** * Return the uint64 representation of a named property's value. * * @param plist The property list to be queried. * @param name The name of the property value to be returned. * @param[out] val On success, the uint64 value of @p name. * * @retval 0 success * @retval ENOENT If @p name is not found in @p plist. * @retval EFTYPE If coercion of the property's value to @p val. * @retval ERANGE If coercion of the property's value would overflow * (or underflow) @p val. */ int bhnd_nvram_plist_get_uint64(bhnd_nvram_plist *plist, const char *name, uint64_t *val) { return (bhnd_nvram_plist_get_encoded(plist, name, val, sizeof(*val), BHND_NVRAM_TYPE_UINT64)); } /** + * Return the boolean representation of a named property's value. + * + * @param plist The property list to be queried. + * @param name The name of the property value to be returned. + * @param[out] val On success, the boolean value of @p name. + * + * @retval 0 success + * @retval ENOENT If @p name is not found in @p plist. + * @retval EFTYPE If coercion of the property's value to @p val. + * @retval ERANGE If coercion of the property's value would overflow + * (or underflow) @p val. + */ +int +bhnd_nvram_plist_get_bool(bhnd_nvram_plist *plist, const char *name, + bool *val) +{ + return (bhnd_nvram_plist_get_encoded(plist, name, val, sizeof(*val), + BHND_NVRAM_TYPE_BOOL)); +} + +/** * Allocate and initialize a new property value. * * The caller is responsible for releasing the returned property value * via bhnd_nvram_prop_release(). * * @param name Property name. * @param val Property value. * * @retval non-NULL success * @retval NULL if allocation fails. */ struct bhnd_nvram_prop * bhnd_nvram_prop_new(const char *name, bhnd_nvram_val *val) { struct bhnd_nvram_prop *prop; prop = bhnd_nv_calloc(1, sizeof(*prop)); if (prop == NULL) return NULL; /* Implicit caller-owned reference */ prop->refs = 1; if ((prop->name = bhnd_nv_strdup(name)) == NULL) goto failed; if ((prop->val = bhnd_nvram_val_copy(val)) == NULL) goto failed; return (prop); failed: if (prop->name != NULL) bhnd_nv_free(prop->name); if (prop->val != NULL) bhnd_nvram_val_release(prop->val); bhnd_nv_free(prop); return (NULL); } /** * Allocate a new property value and attempt to initialize its value from * the given @p inp buffer of @p itype and @p ilen. * * The caller is responsible for releasing the returned property value * via bhnd_nvram_prop_release(). * * @param name Property name. * @param inp Input buffer. * @param ilen Input buffer length. * @param itype Input buffer type. * * @retval non-NULL success * @retval NULL if allocation or initialization fails. */ bhnd_nvram_prop * bhnd_nvram_prop_bytes_new(const char *name, const void *inp, size_t ilen, bhnd_nvram_type itype) { bhnd_nvram_prop *prop; bhnd_nvram_val *val; int error; /* Construct new value instance */ error = bhnd_nvram_val_new(&val, NULL, inp, ilen, itype, BHND_NVRAM_VAL_DYNAMIC); if (error) { if (error != ENOMEM) { BHND_NV_LOG("invalid input data; initialization " "failed: %d\n", error); } return (NULL); } /* Delegate to default implementation */ prop = bhnd_nvram_prop_new(name, val); /* Clean up */ bhnd_nvram_val_release(val); return (prop); } /** * Retain a reference and return @p prop to the caller. * * The caller is responsible for releasing their reference ownership via * bhnd_nvram_prop_release(). * * @param prop The property to be retained. */ bhnd_nvram_prop * bhnd_nvram_prop_retain(bhnd_nvram_prop *prop) { BHND_NV_ASSERT(prop->refs >= 1, ("prop over-released")); refcount_acquire(&prop->refs); return (prop); } /** * Release a reference to @p prop. * * If this is the last reference, all associated resources will be freed. * * @param prop The property to be released. */ void bhnd_nvram_prop_release(bhnd_nvram_prop *prop) { BHND_NV_ASSERT(prop->refs >= 1, ("prop over-released")); /* Drop reference */ if (!refcount_release(&prop->refs)) return; /* Free property data */ bhnd_nvram_val_release(prop->val); bhnd_nv_free(prop->name); bhnd_nv_free(prop); } /** * Return a borrowed reference to the property's name. * * @param prop The property to query. */ const char * bhnd_nvram_prop_name(bhnd_nvram_prop *prop) { return (prop->name); } /** * Return a borrowed reference to the property's value. * * @param prop The property to query. */ bhnd_nvram_val * bhnd_nvram_prop_val(bhnd_nvram_prop *prop) { return (prop->val); } /** * Return the property's value type. * * @param prop The property to query. */ bhnd_nvram_type bhnd_nvram_prop_type(bhnd_nvram_prop *prop) { return (bhnd_nvram_val_type(prop->val)); +} + +/** + * Return true if @p prop has a NULL value type (BHND_NVRAM_TYPE_NULL), false + * otherwise. + * + * @param prop The property to query. + */ +bool +bhnd_nvram_prop_is_null(bhnd_nvram_prop *prop) +{ + return (bhnd_nvram_prop_type(prop) == BHND_NVRAM_TYPE_NULL); } /** * Return a borrowed reference to the property's internal value representation. * * @param prop The property to query. * @param[out] olen The returned data's size, in bytes. * @param[out] otype The returned data's type. */ const void * bhnd_nvram_prop_bytes(bhnd_nvram_prop *prop, size_t *olen, bhnd_nvram_type *otype) { const void *bytes; bytes = bhnd_nvram_val_bytes(prop->val, olen, otype); BHND_NV_ASSERT(*otype == bhnd_nvram_prop_type(prop), ("type mismatch")); return (bytes); } /** * Attempt to encode the property's value as @p otype, writing the result * to @p outp. * * @param prop The property to be encoded. * @param[out] outp On success, the 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 data type to be written to @p outp. * * @retval 0 success * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen * is too small to hold the encoded value. * @retval EFTYPE If value coercion from @p prop to @p otype is * impossible. * @retval ERANGE If value coercion would overflow (or underflow) the * a @p otype representation. */ int bhnd_nvram_prop_encode(bhnd_nvram_prop *prop, void *outp, size_t *olen, bhnd_nvram_type otype) { return (bhnd_nvram_val_encode(prop->val, outp, olen, otype)); } Index: head/sys/dev/bhnd/nvram/bhnd_nvram_plist.h =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_plist.h (revision 310292) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_plist.h (revision 310293) @@ -1,126 +1,130 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _BHND_NVRAM_BHND_NVRAM_PLIST_H_ #define _BHND_NVRAM_BHND_NVRAM_PLIST_H_ #ifdef _KERNEL #include #else /* !_KERNEL */ #include #include #endif /* _KERNEL */ #include "bhnd_nvram.h" #include "bhnd_nvram_value.h" typedef struct bhnd_nvram_prop bhnd_nvram_prop; typedef struct bhnd_nvram_plist bhnd_nvram_plist; bhnd_nvram_plist *bhnd_nvram_plist_new(void); bhnd_nvram_plist *bhnd_nvram_plist_retain(bhnd_nvram_plist *plist); void bhnd_nvram_plist_release(bhnd_nvram_plist *plist); bhnd_nvram_plist *bhnd_nvram_plist_copy(bhnd_nvram_plist *plist); size_t bhnd_nvram_plist_count(bhnd_nvram_plist *plist); int bhnd_nvram_plist_append_list(bhnd_nvram_plist *plist, bhnd_nvram_plist *tail); int bhnd_nvram_plist_append(bhnd_nvram_plist *plist, bhnd_nvram_prop *prop); int bhnd_nvram_plist_append_val(bhnd_nvram_plist *plist, const char *name, bhnd_nvram_val *val); int bhnd_nvram_plist_append_bytes(bhnd_nvram_plist *plist, const char *name, const void *inp, size_t ilen, bhnd_nvram_type itype); int bhnd_nvram_plist_append_string(bhnd_nvram_plist *plist, const char *name, const char *val); int bhnd_nvram_plist_replace(bhnd_nvram_plist *plist, bhnd_nvram_prop *prop); int bhnd_nvram_plist_replace_val(bhnd_nvram_plist *plist, const char *name, bhnd_nvram_val *val); int bhnd_nvram_plist_replace_bytes(bhnd_nvram_plist *plist, const char *name, const void *inp, size_t ilen, bhnd_nvram_type itype); int bhnd_nvram_plist_replace_string(bhnd_nvram_plist *plist, const char *name, const char *val); void bhnd_nvram_plist_remove(bhnd_nvram_plist *plist, const char *name); bool bhnd_nvram_plist_contains(bhnd_nvram_plist *plist, const char *name); bhnd_nvram_prop *bhnd_nvram_plist_next(bhnd_nvram_plist *plist, bhnd_nvram_prop *prop); bhnd_nvram_prop *bhnd_nvram_plist_get_prop(bhnd_nvram_plist *plist, const char *name); bhnd_nvram_val *bhnd_nvram_plist_get_val(bhnd_nvram_plist *plist, const char *name); int bhnd_nvram_plist_get_encoded(bhnd_nvram_plist *plist, const char *name, void *outp, size_t olen, bhnd_nvram_type otype); int bhnd_nvram_plist_get_char(bhnd_nvram_plist *plist, const char *name, u_char *val); int bhnd_nvram_plist_get_uint8(bhnd_nvram_plist *plist, const char *name, uint8_t *val); int bhnd_nvram_plist_get_uint16(bhnd_nvram_plist *plist, const char *name, uint16_t *val); int bhnd_nvram_plist_get_uint32(bhnd_nvram_plist *plist, const char *name, uint32_t *val); int bhnd_nvram_plist_get_uint64(bhnd_nvram_plist *plist, const char *name, uint64_t *val); int bhnd_nvram_plist_get_string(bhnd_nvram_plist *plist, const char *name, const char **val); +int bhnd_nvram_plist_get_bool(bhnd_nvram_plist *plist, + const char *name, bool *val); bhnd_nvram_prop *bhnd_nvram_prop_new(const char *name, bhnd_nvram_val *val); bhnd_nvram_prop *bhnd_nvram_prop_bytes_new(const char *name, const void *inp, size_t ilen, bhnd_nvram_type itype); bhnd_nvram_prop *bhnd_nvram_prop_retain(bhnd_nvram_prop *prop); void bhnd_nvram_prop_release(bhnd_nvram_prop *prop); const char *bhnd_nvram_prop_name(bhnd_nvram_prop *prop); bhnd_nvram_val *bhnd_nvram_prop_val(bhnd_nvram_prop *prop); bhnd_nvram_type bhnd_nvram_prop_type(bhnd_nvram_prop *prop); + +bool bhnd_nvram_prop_is_null(bhnd_nvram_prop *prop); const void *bhnd_nvram_prop_bytes(bhnd_nvram_prop *prop, size_t *olen, bhnd_nvram_type *otype); int bhnd_nvram_prop_encode(bhnd_nvram_prop *prop, void *outp, size_t *olen, bhnd_nvram_type otype); #endif /* _BHND_NVRAM_BHND_NVRAM_PLIST_H_ */ Index: head/sys/dev/bhnd/nvram/bhnd_nvram_store.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_store.c (revision 310292) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_store.c (revision 310293) @@ -1,572 +1,576 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #ifdef _KERNEL #include #include #else /* !_KERNEL */ #include #include #include #include #include #include #endif /* _KERNEL */ #include "bhnd_nvram_private.h" #include "bhnd_nvram_datavar.h" #include "bhnd_nvram_storevar.h" /* * BHND NVRAM Store * * 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); /** * Allocate and initialize a new NVRAM data store instance. * * The caller is responsible for deallocating the instance via * bhnd_nvram_store_free(). * * @param[out] store On success, a pointer to the newly allocated NVRAM data * instance. * @param data The NVRAM data to be managed by the returned NVRAM data store * instance. * * @retval 0 success * @retval non-zero if an error occurs during allocation or initialization, a * regular unix error code will be returned. */ int bhnd_nvram_store_new(struct bhnd_nvram_store **store, struct bhnd_nvram_data *data) { struct bhnd_nvram_store *sc; int error; /* Allocate new instance */ sc = bhnd_nv_calloc(1, sizeof(*sc)); if (sc == NULL) return (ENOMEM); LIST_INIT(&sc->paths); /* Retain the NVRAM data */ sc->nv = bhnd_nvram_data_retain(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))) goto cleanup; BHND_NVSTORE_LOCK_INIT(sc); *store = sc; return (0); cleanup: bhnd_nvram_store_free(sc); return (error); } /** * Allocate and initialize a new NVRAM data store instance, parsing the * NVRAM data from @p io. * * The caller is responsible for deallocating the instance via * bhnd_nvram_store_free(). * * The NVRAM data mapped by @p io will be copied, and @p io may be safely * deallocated after bhnd_nvram_store_new() returns. * * @param[out] store On success, a pointer to the newly allocated NVRAM data * instance. * @param io An I/O context mapping the NVRAM data to be copied and parsed. * @param cls The NVRAM data class to be used when parsing @p io, or NULL * to perform runtime identification of the appropriate data class. * * @retval 0 success * @retval non-zero if an error occurs during allocation or initialization, a * regular unix error code will be returned. */ int bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store, struct bhnd_nvram_io *io, bhnd_nvram_data_class *cls) { struct bhnd_nvram_data *data; int error; /* Try to parse the data */ if ((error = bhnd_nvram_data_new(cls, &data, io))) return (error); /* Try to create our new store instance */ error = bhnd_nvram_store_new(store, data); bhnd_nvram_data_release(data); return (error); } /** * Free an NVRAM store instance, releasing all associated resources. * * @param sc A store instance previously allocated via * bhnd_nvram_store_new(). */ void bhnd_nvram_store_free(struct bhnd_nvram_store *sc) { struct bhnd_nvstore_path *dpath, *dnext; LIST_FOREACH_SAFE(dpath, &sc->paths, dp_link, dnext) { bhnd_nv_free(dpath->path); bhnd_nv_free(dpath); } if (sc->pending != NULL) nvlist_destroy(sc->pending); 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); } /** * 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. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too * small to hold the requested value. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ int bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name, void *buf, size_t *len, bhnd_nvram_type type) { void *cookiep; const void *inp; size_t ilen; bhnd_nvram_type itype; int error; /* * Search order: * * - uncommitted changes * - index lookup OR buffer scan */ BHND_NVSTORE_LOCK(sc); /* Is variable marked for deletion? */ if (nvlist_exists_null(sc->pending, name)) { BHND_NVSTORE_UNLOCK(sc); return (ENOENT); } /* Does an uncommitted value exist? */ if (nvlist_exists_string(sc->pending, name)) { /* Uncommited value exists, is not a deletion */ inp = nvlist_get_string(sc->pending, name); ilen = strlen(inp) + 1; itype = BHND_NVRAM_TYPE_STRING; /* Coerce borrowed data reference before releasing * our lock. */ error = bhnd_nvram_value_coerce(inp, ilen, itype, buf, len, type); BHND_NVSTORE_UNLOCK(sc); 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); } /* Let the parser itself perform value coercion */ error = bhnd_nvram_data_getvar(sc->nv, cookiep, buf, len, type); BHND_NVSTORE_UNLOCK(sc); return (error); } /** * Set an NVRAM variable. * * @param sc The NVRAM parser state. * @param name The NVRAM variable name. * @param[out] buf The new value. * @param[in,out] len The size of @p buf. * @param type The data type of @p buf. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval EINVAL If @p len does not match the expected variable size. */ int bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name, const void *buf, size_t len, bhnd_nvram_type type) { const char *inp; char vbuf[512]; /* Verify name validity */ if (!bhnd_nvram_validate_name(name, strlen(name))) 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); } /* sort function for bhnd_nvstore_index cookie values */ static int bhnd_nvram_sort_idx(void *ctx, const void *lhs, const void *rhs) { struct bhnd_nvram_store *sc; const char *l_str, *r_str; sc = ctx; /* Fetch string pointers from the cookiep values */ l_str = bhnd_nvram_data_getvar_name(sc->nv, *(void * const *)lhs); r_str = bhnd_nvram_data_getvar_name(sc->nv, *(void * const *)rhs); /* Perform comparison */ return (strcasecmp(l_str, r_str)); } /** * Parse and register all device paths and path aliases in @p nvram. * * @param sc The NVRAM parser state. * * @retval 0 success * @retval non-zero If registering device paths fails, a regular unix * error code will be returned. */ static int bhnd_nvram_register_devpaths(struct bhnd_nvram_store *sc) { const char *name; void *cookiep; int error; /* Skip if backing parser does not support device paths */ if (!(bhnd_nvram_data_caps(sc->nv) & BHND_NVRAM_DATA_CAP_DEVPATHS)) return (0); /* 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); } Index: head/sys/dev/bhnd/nvram/bhnd_nvram_subr.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_subr.c (revision 310292) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_subr.c (revision 310293) @@ -1,967 +1,1064 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #ifdef _KERNEL #include #include #include #include #include #include #else /* !_KERNEL */ #include #include #include #include #include #include #include #include #include #endif /* _KERNEL */ #include "bhnd_nvram_io.h" #include "bhnd_nvram_private.h" #include "bhnd_nvram_value.h" #include "bhnd_nvram_map_data.h" /* * Common NVRAM/SPROM support, including NVRAM variable map * lookup. */ #ifdef _KERNEL MALLOC_DEFINE(M_BHND_NVRAM, "bhnd_nvram", "bhnd nvram data"); #endif /* * CRC-8 lookup table used to checksum SPROM and NVRAM data via * bhnd_nvram_crc8(). * * Generated with following parameters: * polynomial: CRC-8 (x^8 + x^7 + x^6 + x^4 + x^2 + 1) * reflected bits: false * reversed: true */ const uint8_t bhnd_nvram_crc8_tab[] = { 0x00, 0xf7, 0xb9, 0x4e, 0x25, 0xd2, 0x9c, 0x6b, 0x4a, 0xbd, 0xf3, 0x04, 0x6f, 0x98, 0xd6, 0x21, 0x94, 0x63, 0x2d, 0xda, 0xb1, 0x46, 0x08, 0xff, 0xde, 0x29, 0x67, 0x90, 0xfb, 0x0c, 0x42, 0xb5, 0x7f, 0x88, 0xc6, 0x31, 0x5a, 0xad, 0xe3, 0x14, 0x35, 0xc2, 0x8c, 0x7b, 0x10, 0xe7, 0xa9, 0x5e, 0xeb, 0x1c, 0x52, 0xa5, 0xce, 0x39, 0x77, 0x80, 0xa1, 0x56, 0x18, 0xef, 0x84, 0x73, 0x3d, 0xca, 0xfe, 0x09, 0x47, 0xb0, 0xdb, 0x2c, 0x62, 0x95, 0xb4, 0x43, 0x0d, 0xfa, 0x91, 0x66, 0x28, 0xdf, 0x6a, 0x9d, 0xd3, 0x24, 0x4f, 0xb8, 0xf6, 0x01, 0x20, 0xd7, 0x99, 0x6e, 0x05, 0xf2, 0xbc, 0x4b, 0x81, 0x76, 0x38, 0xcf, 0xa4, 0x53, 0x1d, 0xea, 0xcb, 0x3c, 0x72, 0x85, 0xee, 0x19, 0x57, 0xa0, 0x15, 0xe2, 0xac, 0x5b, 0x30, 0xc7, 0x89, 0x7e, 0x5f, 0xa8, 0xe6, 0x11, 0x7a, 0x8d, 0xc3, 0x34, 0xab, 0x5c, 0x12, 0xe5, 0x8e, 0x79, 0x37, 0xc0, 0xe1, 0x16, 0x58, 0xaf, 0xc4, 0x33, 0x7d, 0x8a, 0x3f, 0xc8, 0x86, 0x71, 0x1a, 0xed, 0xa3, 0x54, 0x75, 0x82, 0xcc, 0x3b, 0x50, 0xa7, 0xe9, 0x1e, 0xd4, 0x23, 0x6d, 0x9a, 0xf1, 0x06, 0x48, 0xbf, 0x9e, 0x69, 0x27, 0xd0, 0xbb, 0x4c, 0x02, 0xf5, 0x40, 0xb7, 0xf9, 0x0e, 0x65, 0x92, 0xdc, 0x2b, 0x0a, 0xfd, 0xb3, 0x44, 0x2f, 0xd8, 0x96, 0x61, 0x55, 0xa2, 0xec, 0x1b, 0x70, 0x87, 0xc9, 0x3e, 0x1f, 0xe8, 0xa6, 0x51, 0x3a, 0xcd, 0x83, 0x74, 0xc1, 0x36, 0x78, 0x8f, 0xe4, 0x13, 0x5d, 0xaa, 0x8b, 0x7c, 0x32, 0xc5, 0xae, 0x59, 0x17, 0xe0, 0x2a, 0xdd, 0x93, 0x64, 0x0f, 0xf8, 0xb6, 0x41, 0x60, 0x97, 0xd9, 0x2e, 0x45, 0xb2, 0xfc, 0x0b, 0xbe, 0x49, 0x07, 0xf0, 0x9b, 0x6c, 0x22, 0xd5, 0xf4, 0x03, 0x4d, 0xba, 0xd1, 0x26, 0x68, 0x9f }; /** * Return a human readable name for @p type. * * @param type The type to query. */ const char * bhnd_nvram_type_name(bhnd_nvram_type type) { switch (type) { case BHND_NVRAM_TYPE_UINT8: return ("uint8"); case BHND_NVRAM_TYPE_UINT16: return ("uint16"); case BHND_NVRAM_TYPE_UINT32: return ("uint32"); case BHND_NVRAM_TYPE_UINT64: return ("uint64"); case BHND_NVRAM_TYPE_CHAR: return ("char"); case BHND_NVRAM_TYPE_INT8: return ("int8"); case BHND_NVRAM_TYPE_INT16: return ("int16"); case BHND_NVRAM_TYPE_INT32: return ("int32"); case BHND_NVRAM_TYPE_INT64: return ("int64"); case BHND_NVRAM_TYPE_STRING: return ("string"); + case BHND_NVRAM_TYPE_BOOL: + return ("bool"); + case BHND_NVRAM_TYPE_NULL: + return ("null"); + case BHND_NVRAM_TYPE_DATA: + return ("data"); case BHND_NVRAM_TYPE_UINT8_ARRAY: return ("uint8[]"); case BHND_NVRAM_TYPE_UINT16_ARRAY: return ("uint16[]"); case BHND_NVRAM_TYPE_UINT32_ARRAY: return ("uint32[]"); case BHND_NVRAM_TYPE_UINT64_ARRAY: return ("uint64[]"); case BHND_NVRAM_TYPE_INT8_ARRAY: return ("int8[]"); case BHND_NVRAM_TYPE_INT16_ARRAY: return ("int16[]"); case BHND_NVRAM_TYPE_INT32_ARRAY: return ("int32[]"); case BHND_NVRAM_TYPE_INT64_ARRAY: return ("int64[]"); case BHND_NVRAM_TYPE_CHAR_ARRAY: return ("char[]"); case BHND_NVRAM_TYPE_STRING_ARRAY: return ("string[]"); + case BHND_NVRAM_TYPE_BOOL_ARRAY: + return ("bool[]"); } /* Quiesce gcc4.2 */ BHND_NV_PANIC("bhnd nvram type %u unknown", type); } /** * Return true if @p type is a signed integer type, false otherwise. * * Will return false for all array types. * * @param type The type to query. */ bool bhnd_nvram_is_signed_type(bhnd_nvram_type type) { switch (type) { case BHND_NVRAM_TYPE_INT8: case BHND_NVRAM_TYPE_INT16: case BHND_NVRAM_TYPE_INT32: case BHND_NVRAM_TYPE_INT64: BHND_NV_ASSERT(bhnd_nvram_is_int_type(type), ("non-int type?")); return (true); case BHND_NVRAM_TYPE_CHAR: case BHND_NVRAM_TYPE_UINT8: case BHND_NVRAM_TYPE_UINT16: case BHND_NVRAM_TYPE_UINT32: case BHND_NVRAM_TYPE_UINT64: case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_BOOL: + case BHND_NVRAM_TYPE_NULL: + case BHND_NVRAM_TYPE_DATA: 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: return (false); } /* Quiesce gcc4.2 */ BHND_NV_PANIC("bhnd nvram type %u unknown", type); } /** * Return true if @p type is an unsigned integer type, false otherwise. * * @param type The type to query. * * @return Will return false for all array types. * @return Will return true for BHND_NVRAM_TYPE_CHAR. */ bool bhnd_nvram_is_unsigned_type(bhnd_nvram_type type) { /* If an integer type, must be either signed or unsigned */ if (!bhnd_nvram_is_int_type(type)) return (false); return (!bhnd_nvram_is_signed_type(type)); } /** * Return true if bhnd_nvram_is_signed_type() or bhnd_nvram_is_unsigned_type() * returns true for @p type. * * @param type The type to query. */ bool bhnd_nvram_is_int_type(bhnd_nvram_type type) { 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: return (true); case BHND_NVRAM_TYPE_CHAR: case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_BOOL: + case BHND_NVRAM_TYPE_NULL: + case BHND_NVRAM_TYPE_DATA: 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: return (false); } /* Quiesce gcc4.2 */ BHND_NV_PANIC("bhnd nvram type %u unknown", type); } /** * Return true if @p type is an array type, false otherwise. * * @param type The type to query. */ bool bhnd_nvram_is_array_type(bhnd_nvram_type type) { 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_CHAR: case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_BOOL: + case BHND_NVRAM_TYPE_NULL: + case BHND_NVRAM_TYPE_DATA: return (false); 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: return (true); } /* Quiesce gcc4.2 */ BHND_NV_PANIC("bhnd nvram type %u unknown", type); } /** * If @p type is an array type, return the base element type. Otherwise, * returns @p type. * * @param type The type to query. */ bhnd_nvram_type bhnd_nvram_base_type(bhnd_nvram_type type) { 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_CHAR: case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_BOOL: + case BHND_NVRAM_TYPE_NULL: + case BHND_NVRAM_TYPE_DATA: return (type); case BHND_NVRAM_TYPE_UINT8_ARRAY: return (BHND_NVRAM_TYPE_UINT8); case BHND_NVRAM_TYPE_UINT16_ARRAY: return (BHND_NVRAM_TYPE_UINT16); case BHND_NVRAM_TYPE_UINT32_ARRAY: return (BHND_NVRAM_TYPE_UINT32); case BHND_NVRAM_TYPE_UINT64_ARRAY: return (BHND_NVRAM_TYPE_UINT64); case BHND_NVRAM_TYPE_INT8_ARRAY: return (BHND_NVRAM_TYPE_INT8); case BHND_NVRAM_TYPE_INT16_ARRAY: return (BHND_NVRAM_TYPE_INT16); case BHND_NVRAM_TYPE_INT32_ARRAY: return (BHND_NVRAM_TYPE_INT32); case BHND_NVRAM_TYPE_INT64_ARRAY: return (BHND_NVRAM_TYPE_INT64); case BHND_NVRAM_TYPE_CHAR_ARRAY: return (BHND_NVRAM_TYPE_CHAR); case BHND_NVRAM_TYPE_STRING_ARRAY: return (BHND_NVRAM_TYPE_STRING); + case BHND_NVRAM_TYPE_BOOL_ARRAY: return (BHND_NVRAM_TYPE_BOOL); } /* Quiesce gcc4.2 */ BHND_NV_PANIC("bhnd nvram type %u unknown", type); } /** + * Return the raw data type used to represent values of @p type, or return + * @p type is @p type is not a complex type. + * + * @param type The type to query. + */ +bhnd_nvram_type +bhnd_nvram_raw_type(bhnd_nvram_type type) +{ + switch (type) { + case BHND_NVRAM_TYPE_CHAR: + return (BHND_NVRAM_TYPE_UINT8); + + case BHND_NVRAM_TYPE_CHAR_ARRAY: + return (BHND_NVRAM_TYPE_UINT8_ARRAY); + + case BHND_NVRAM_TYPE_BOOL: { + _Static_assert(sizeof(bhnd_nvram_bool_t) == sizeof(uint8_t), + "bhnd_nvram_bool_t must be uint8-representable"); + return (BHND_NVRAM_TYPE_UINT8); + } + + case BHND_NVRAM_TYPE_BOOL_ARRAY: + return (BHND_NVRAM_TYPE_UINT8_ARRAY); + + case BHND_NVRAM_TYPE_DATA: + return (BHND_NVRAM_TYPE_UINT8_ARRAY); + + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_STRING_ARRAY: + return (BHND_NVRAM_TYPE_UINT8_ARRAY); + + 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_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: + return (type); + } + + /* Quiesce gcc4.2 */ + BHND_NV_PANIC("bhnd nvram type %u unknown", type); +} + +/** * Return the size, in bytes, of a single element of @p type, or 0 * if @p type is a variable-width type. * * @param type The type to query. */ size_t bhnd_nvram_type_width(bhnd_nvram_type type) { switch (type) { case BHND_NVRAM_TYPE_STRING: case BHND_NVRAM_TYPE_STRING_ARRAY: + case BHND_NVRAM_TYPE_DATA: return (0); + case BHND_NVRAM_TYPE_NULL: + return (0); + + case BHND_NVRAM_TYPE_BOOL: + case BHND_NVRAM_TYPE_BOOL_ARRAY: + return (sizeof(bhnd_nvram_bool_t)); + case BHND_NVRAM_TYPE_CHAR: case BHND_NVRAM_TYPE_CHAR_ARRAY: case BHND_NVRAM_TYPE_UINT8: case BHND_NVRAM_TYPE_UINT8_ARRAY: case BHND_NVRAM_TYPE_INT8: case BHND_NVRAM_TYPE_INT8_ARRAY: return (sizeof(uint8_t)); case BHND_NVRAM_TYPE_UINT16: case BHND_NVRAM_TYPE_UINT16_ARRAY: case BHND_NVRAM_TYPE_INT16: case BHND_NVRAM_TYPE_INT16_ARRAY: return (sizeof(uint16_t)); case BHND_NVRAM_TYPE_UINT32: case BHND_NVRAM_TYPE_UINT32_ARRAY: case BHND_NVRAM_TYPE_INT32: case BHND_NVRAM_TYPE_INT32_ARRAY: return (sizeof(uint32_t)); case BHND_NVRAM_TYPE_UINT64: case BHND_NVRAM_TYPE_UINT64_ARRAY: case BHND_NVRAM_TYPE_INT64: case BHND_NVRAM_TYPE_INT64_ARRAY: return (sizeof(uint64_t)); } /* Quiesce gcc4.2 */ BHND_NV_PANIC("bhnd nvram type %u unknown", type); } /** * Return the native host alignment for values of @p type. * * @param type The type to query. */ size_t bhnd_nvram_type_host_align(bhnd_nvram_type type) { switch (type) { case BHND_NVRAM_TYPE_CHAR: case BHND_NVRAM_TYPE_CHAR_ARRAY: + case BHND_NVRAM_TYPE_DATA: case BHND_NVRAM_TYPE_STRING: case BHND_NVRAM_TYPE_STRING_ARRAY: return (_Alignof(uint8_t)); + case BHND_NVRAM_TYPE_BOOL: + case BHND_NVRAM_TYPE_BOOL_ARRAY: { + _Static_assert(sizeof(bhnd_nvram_bool_t) == sizeof(uint8_t), + "bhnd_nvram_bool_t must be uint8-representable"); + return (_Alignof(uint8_t)); + } + case BHND_NVRAM_TYPE_NULL: + return (1); case BHND_NVRAM_TYPE_UINT8: case BHND_NVRAM_TYPE_UINT8_ARRAY: return (_Alignof(uint8_t)); case BHND_NVRAM_TYPE_UINT16: case BHND_NVRAM_TYPE_UINT16_ARRAY: return (_Alignof(uint16_t)); case BHND_NVRAM_TYPE_UINT32: case BHND_NVRAM_TYPE_UINT32_ARRAY: return (_Alignof(uint32_t)); case BHND_NVRAM_TYPE_UINT64: case BHND_NVRAM_TYPE_UINT64_ARRAY: return (_Alignof(uint64_t)); case BHND_NVRAM_TYPE_INT8: case BHND_NVRAM_TYPE_INT8_ARRAY: return (_Alignof(int8_t)); case BHND_NVRAM_TYPE_INT16: case BHND_NVRAM_TYPE_INT16_ARRAY: return (_Alignof(int16_t)); case BHND_NVRAM_TYPE_INT32: case BHND_NVRAM_TYPE_INT32_ARRAY: return (_Alignof(int32_t)); case BHND_NVRAM_TYPE_INT64: case BHND_NVRAM_TYPE_INT64_ARRAY: return (_Alignof(int64_t)); } /* Quiesce gcc4.2 */ BHND_NV_PANIC("bhnd nvram type %u unknown", type); } /** * Iterate over all strings in the @p inp string array (@see * BHNF_NVRAM_TYPE_STRING_ARRAY). * * @param inp The string array to be iterated. This must be a * buffer of one or more NUL-terminated strings. * @param ilen The size, in bytes, of @p inp, including any * terminating NUL character(s). * @param prev The pointer previously returned by * bhnd_nvram_string_array_next(), or NULL to begin * iteration. * @param[in,out] olen If @p prev is non-NULL, @p olen must be a * pointer to the length previously returned by * bhnd_nvram_string_array_next(). On success, will * be set to the next element's length, in bytes. * * @retval non-NULL A reference to the next NUL-terminated string * @retval NULL If the end of the string array is reached. */ const char * bhnd_nvram_string_array_next(const char *inp, size_t ilen, const char *prev, size_t *olen) { return (bhnd_nvram_value_array_next(inp, ilen, BHND_NVRAM_TYPE_STRING_ARRAY, prev, olen)); } /* used by bhnd_nvram_find_vardefn() */ static int bhnd_nvram_find_vardefn_compare(const void *key, const void *rhs) { const struct bhnd_nvram_vardefn *r = rhs; return (strcmp((const char *)key, r->name)); } /** * Find and return the variable definition for @p varname, if any. * * @param varname variable name * * @retval bhnd_nvram_vardefn If a valid definition for @p varname is found. * @retval NULL If no definition for @p varname is found. */ const struct bhnd_nvram_vardefn * bhnd_nvram_find_vardefn(const char *varname) { return (bsearch(varname, bhnd_nvram_vardefns, bhnd_nvram_num_vardefns, sizeof(bhnd_nvram_vardefns[0]), bhnd_nvram_find_vardefn_compare)); } /** * Return the variable ID for a variable definition. * * @param defn Variable definition previously returned by * bhnd_nvram_find_vardefn() or bhnd_nvram_get_vardefn(). */ size_t bhnd_nvram_get_vardefn_id(const struct bhnd_nvram_vardefn *defn) { BHND_NV_ASSERT( defn >= bhnd_nvram_vardefns && defn <= &bhnd_nvram_vardefns[bhnd_nvram_num_vardefns-1], ("invalid variable definition pointer %p", defn)); return (defn - bhnd_nvram_vardefns); } /** * Return the variable definition with the given @p id, or NULL * if no such variable ID is defined. * * @param id variable ID. * * @retval bhnd_nvram_vardefn If a valid definition for @p id is found. * @retval NULL If no definition for @p id is found. */ const struct bhnd_nvram_vardefn * bhnd_nvram_get_vardefn(size_t id) { if (id >= bhnd_nvram_num_vardefns) return (NULL); return (&bhnd_nvram_vardefns[id]); } /** * Validate an NVRAM variable name. * * 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) { size_t limit; limit = strnlen(name, name_len); if (limit == 0) 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; } } /* Scan for special characters */ for (const char *p = name; (size_t)(p - name) < limit; p++) { switch (*p) { case '/': /* path delimiter */ case '=': /* key=value delimiter */ return (false); default: if (!isascii(*p) || bhnd_nv_isspace(*p)) return (false); } } return (true); } /** * Parses the string in the optionally NUL-terminated @p str to as an integer * value of @p otype, accepting any integer format supported by the standard * strtoul(). * * - Any leading whitespace in @p str -- as defined by the equivalent of * calling isspace_l() with an ASCII locale -- will be ignored. * - A @p str may be prefixed with a single optional '+' or '-' sign denoting * signedness. * - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a * base 16 integer follows. * - An octal @p str may include a '0' prefix, denoting that an octal integer * follows. * * If a @p base of 0 is specified, the base will be determined according * to the string's initial prefix, as per strtoul()'s documented behavior. * * When parsing a base 16 integer to a signed representation, if no explicit * sign prefix is given, the string will be parsed as the raw two's complement * representation of the signed integer value. * * @param str The string to be parsed. * @param maxlen The maximum number of bytes to be read in * @p str. * @param base The input string's base (2-36), or 0. * @param[out] nbytes On success or failure, will be set to the total * number of parsed bytes. If the total number of * bytes is not desired, a NULL pointer may be * provided. * @param[out] outp On success, the parsed integer value will be * written to @p outp. 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 integer type to be parsed. * * @retval 0 success * @retval EINVAL if an invalid @p base is specified. * @retval EINVAL if an unsupported (or non-integer) @p otype is * specified. * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too * small to hold the requested value. * @retval EFTYPE if @p str cannot be parsed as an integer of @p base. * @retval ERANGE If the integer parsed from @p str is too large to be * represented as a value of @p otype. */ int bhnd_nvram_parse_int(const char *str, size_t maxlen, u_int base, size_t *nbytes, void *outp, size_t *olen, bhnd_nvram_type otype) { uint64_t value; uint64_t carry_max, value_max; uint64_t type_max; size_t limit, local_nbytes; size_t ndigits; bool negative, sign, twos_compl; /* Must be an integer type */ if (!bhnd_nvram_is_int_type(otype)) return (EINVAL); /* Determine output byte limit */ if (outp != NULL) limit = *olen; else limit = 0; /* We always need a byte count. If the caller provides a NULL nbytes, * track our position in a stack variable */ if (nbytes == NULL) nbytes = &local_nbytes; value = 0; ndigits = 0; *nbytes = 0; negative = false; sign = false; /* Validate the specified base */ if (base != 0 && !(base >= 2 && base <= 36)) return (EINVAL); /* Skip any leading whitespace */ for (; *nbytes < maxlen; (*nbytes)++) { if (!bhnd_nv_isspace(str[*nbytes])) break; } /* Empty string? */ if (*nbytes == maxlen) return (EFTYPE); /* Parse and skip sign */ if (str[*nbytes] == '-') { negative = true; sign = true; (*nbytes)++; } else if (str[*nbytes] == '+') { sign = true; (*nbytes)++; } /* Truncated after sign character? */ if (*nbytes == maxlen) return (EFTYPE); /* Identify (or validate) hex base, skipping 0x/0X prefix */ if (base == 16 || base == 0) { /* Check for (and skip) 0x/0X prefix */ if (maxlen - *nbytes >= 2 && str[*nbytes] == '0' && (str[*nbytes+1] == 'x' || str[*nbytes+1] == 'X')) { base = 16; (*nbytes) += 2; } } /* Truncated after hex prefix? */ if (*nbytes == maxlen) return (EFTYPE); /* Differentiate decimal/octal by looking for a leading 0 */ if (base == 0) { if (str[*nbytes] == '0') { base = 8; } else { base = 10; } } /* Only enable twos-compliment signed integer parsing enabled if the * input is base 16, and no explicit sign prefix was provided */ if (!sign && base == 16) twos_compl = true; else twos_compl = false; /* Determine the maximum value representable by the requested type */ switch (otype) { case BHND_NVRAM_TYPE_CHAR: case BHND_NVRAM_TYPE_UINT8: type_max = (uint64_t)UINT8_MAX; break; case BHND_NVRAM_TYPE_UINT16: type_max = (uint64_t)UINT16_MAX; break; case BHND_NVRAM_TYPE_UINT32: type_max = (uint64_t)UINT32_MAX; break; case BHND_NVRAM_TYPE_UINT64: type_max = (uint64_t)UINT64_MAX; break; case BHND_NVRAM_TYPE_INT8: if (twos_compl) type_max = (uint64_t)UINT8_MAX; else if (negative) type_max = -(uint64_t)INT8_MIN; else type_max = (uint64_t)INT8_MAX; break; case BHND_NVRAM_TYPE_INT16: if (twos_compl) type_max = (uint64_t)UINT16_MAX; else if (negative) type_max = -(uint64_t)INT16_MIN; else type_max = (uint64_t)INT16_MAX; break; case BHND_NVRAM_TYPE_INT32: if (twos_compl) type_max = (uint64_t)UINT32_MAX; else if (negative) type_max = -(uint64_t)INT32_MIN; else type_max = (uint64_t)INT32_MAX; break; case BHND_NVRAM_TYPE_INT64: if (twos_compl) type_max = (uint64_t)UINT64_MAX; else if (negative) type_max = -(uint64_t)INT64_MIN; else type_max = (uint64_t)INT64_MAX; break; default: BHND_NV_LOG("unsupported integer type: %d\n", otype); return (EINVAL); } /* The maximum value after which an additional carry would overflow */ value_max = type_max / (uint64_t)base; /* The maximum carry value given a value equal to value_max */ carry_max = type_max % (uint64_t)base; /* Consume input until we hit maxlen or a non-digit character */ for (; *nbytes < maxlen; (*nbytes)++) { u_long carry; char c; /* Parse carry value */ c = str[*nbytes]; if (bhnd_nv_isdigit(c)) { carry = c - '0'; } else if (bhnd_nv_isxdigit(c)) { if (bhnd_nv_isupper(c)) carry = (c - 'A') + 10; else carry = (c - 'a') + 10; } else { /* Hit first non-digit character */ break; } /* If carry is outside the base, it's not a valid digit * in the current parse context; consider it a non-digit * character */ if (carry >= (uint64_t)base) break; /* Increment count of parsed digits */ ndigits++; if (value > value_max) { /* -Any- carry value would overflow */ return (ERANGE); } else if (value == value_max && carry > carry_max) { /* -This- carry value would overflow */ return (ERANGE); } value *= (uint64_t)base; value += carry; } /* If we hit a non-digit character before parsing the first digit, * we hit an empty integer string. */ if (ndigits == 0) return (EFTYPE); if (negative) value = -value; /* Provide (and verify) required length */ *olen = bhnd_nvram_type_width(otype); if (outp == NULL) return (0); else if (limit < *olen) return (ENOMEM); /* Provide result */ switch (otype) { case BHND_NVRAM_TYPE_CHAR: case BHND_NVRAM_TYPE_UINT8: *(uint8_t *)outp = (uint8_t)value; break; case BHND_NVRAM_TYPE_UINT16: *(uint16_t *)outp = (uint16_t)value; break; case BHND_NVRAM_TYPE_UINT32: *(uint32_t *)outp = (uint32_t)value; break; case BHND_NVRAM_TYPE_UINT64: *(uint64_t *)outp = (uint64_t)value; break; case BHND_NVRAM_TYPE_INT8: *(int8_t *)outp = (int8_t)(int64_t)value; break; case BHND_NVRAM_TYPE_INT16: *(int16_t *)outp = (int16_t)(int64_t)value; break; case BHND_NVRAM_TYPE_INT32: *(int32_t *)outp = (int32_t)(int64_t)value; break; case BHND_NVRAM_TYPE_INT64: *(int64_t *)outp = (int64_t)value; break; default: /* unreachable */ BHND_NV_PANIC("unhandled type %d\n", otype); } return (0); } /** * Parse a 'name=value' string. * * @param env The string to be parsed. * @param env_len The length of @p envp. * @param delim The delimiter used in @p envp. This will generally be '='. * @param[out] name If not NULL, a pointer to the name string. This argument * may be NULL. * @param[out] name_len On success, the length of the name substring. This * argument may be NULL. * @param[out] value On success, a pointer to the value substring. This argument * may be NULL. * @param[out] value_len On success, the length of the value substring. This * argument may be NULL. * * @retval 0 success * @retval EINVAL if parsing @p envp fails. */ int bhnd_nvram_parse_env(const char *env, size_t env_len, char delim, const char **name, size_t *name_len, const char **value, size_t *value_len) { const char *p; /* Name */ if ((p = memchr(env, delim, env_len)) == NULL) { BHND_NV_LOG("delimiter '%c' not found in '%.*s'\n", delim, BHND_NV_PRINT_WIDTH(env_len), env); return (EINVAL); } /* Name */ if (name != NULL) *name = env; if (name_len != NULL) *name_len = p - env; /* Skip delim */ p++; /* Value */ if (value != NULL) *value = p; if (value_len != NULL) *value_len = env_len - (p - env); return (0); } /** * Parse a field value, returning the actual pointer to the first * non-whitespace character and the total size of the field. * * @param[in,out] inp The field string to parse. Will be updated to point * at the first non-whitespace character found. * @param ilen The length of @p inp, in bytes. * @param delim The field delimiter to search for. * * @return Returns the actual size of the field data. */ size_t bhnd_nvram_parse_field(const char **inp, size_t ilen, char delim) { const char *p, *sp; /* Skip any leading whitespace */ for (sp = *inp; (size_t)(sp-*inp) < ilen && bhnd_nv_isspace(*sp); sp++) continue; *inp = sp; /* Find the last field character */ for (p = *inp; (size_t)(p - *inp) < ilen; p++) { if (*p == delim || *p == '\0') break; } return (p - *inp); } /** * Parse a field value, returning the actual pointer to the first * non-whitespace character and the total size of the field, minus * any trailing whitespace. * * @param[in,out] inp The field string to parse. Will be updated to point * at the first non-whitespace character found. * @param ilen The length of the parsed field, in bytes, excluding the * field elimiter and any trailing whitespace. * @param delim The field delimiter to search for. * * @return Returns the actual size of the field data. */ size_t bhnd_nvram_trim_field(const char **inp, size_t ilen, char delim) { const char *sp; size_t plen; plen = bhnd_nvram_parse_field(inp, ilen, delim); /* Trim trailing whitespace */ sp = *inp; while (plen > 0) { if (!bhnd_nv_isspace(*(sp + plen - 1))) break; plen--; } return (plen); } Index: head/sys/dev/bhnd/nvram/bhnd_nvram_value.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_value.c (revision 310292) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_value.c (revision 310293) @@ -1,1607 +1,1936 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #ifdef _KERNEL #include #include #include #include #include #else /* !_KERNEL */ #include #include #include #include #include #endif /* _KERNEL */ #include "bhnd_nvram_private.h" #include "bhnd_nvram_valuevar.h" static int bhnd_nvram_val_fmt_filter(const bhnd_nvram_val_fmt **fmt, const void *inp, size_t ilen, bhnd_nvram_type itype); static void *bhnd_nvram_val_alloc_bytes(bhnd_nvram_val *value, size_t ilen, bhnd_nvram_type itype, uint32_t flags); static int bhnd_nvram_val_set(bhnd_nvram_val *value, const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags); static int bhnd_nvram_val_set_inline(bhnd_nvram_val *value, const void *inp, size_t ilen, bhnd_nvram_type itype); + +static int bhnd_nvram_val_encode_data(const void *inp, size_t ilen, + bhnd_nvram_type itype, void *outp, size_t *olen, + bhnd_nvram_type otype); static int bhnd_nvram_val_encode_int(const void *inp, size_t ilen, bhnd_nvram_type itype, void *outp, size_t *olen, bhnd_nvram_type otype); +static int bhnd_nvram_val_encode_null(const void *inp, size_t ilen, + bhnd_nvram_type itype, void *outp, size_t *olen, + bhnd_nvram_type otype); +static int bhnd_nvram_val_encode_bool(const void *inp, size_t ilen, + bhnd_nvram_type itype, void *outp, size_t *olen, + bhnd_nvram_type otype); static int bhnd_nvram_val_encode_string(const void *inp, size_t ilen, bhnd_nvram_type itype, void *outp, size_t *olen, bhnd_nvram_type otype); /** Initialize an empty value instance with @p _fmt, @p _storage, and * an implicit callee-owned reference */ #define BHND_NVRAM_VAL_INITIALIZER(_fmt, _storage) \ (bhnd_nvram_val) { \ .refs = 1, \ .val_storage = _storage, \ .fmt = _fmt, \ .data_storage = BHND_NVRAM_VAL_DATA_NONE, \ }; - /** Assert that @p value's backing representation state has initialized * as empty. */ #define BHND_NVRAM_VAL_ASSERT_EMPTY(_value) \ BHND_NV_ASSERT( \ value->data_storage == BHND_NVRAM_VAL_DATA_NONE && \ value->data_len == 0 && \ value->data.ptr == NULL, \ ("previously initialized value")) /** Return true if BHND_NVRAM_VAL_BORROW_DATA or BHND_NVRAM_VAL_STATIC_DATA is * set in @p _flags (e.g. we should attempt to directly reference external * data */ #define BHND_NVRAM_VAL_EXTREF_BORROWED_DATA(_flags) \ (((_flags) & BHND_NVRAM_VAL_BORROW_DATA) || \ ((_flags) & BHND_NVRAM_VAL_STATIC_DATA)) /** Flags permitted when performing val-based initialization via * bhnd_nvram_val_convert_init() or bhnd_nvram_val_convert_new() */ #define BHND_NVRAM_VALID_CONV_FLAGS \ (BHND_NVRAM_VAL_FIXED | \ BHND_NVRAM_VAL_DYNAMIC | \ BHND_NVRAM_VAL_COPY_DATA) /** Returns true if @p _val must be copied in bhnd_nvram_val_copy(), false * if its reference count may be safely incremented */ #define BHND_NVRAM_VAL_NEED_COPY(_val) \ ((_val)->val_storage == BHND_NVRAM_VAL_STORAGE_AUTO || \ (_val)->data_storage == BHND_NVRAM_VAL_DATA_EXT_WEAK) volatile u_int refs; /**< reference count */ bhnd_nvram_val_storage val_storage; /**< value structure storage */ const bhnd_nvram_val_fmt *fmt; /**< value format */ bhnd_nvram_val_data_storage data_storage; /**< data storage */ bhnd_nvram_type data_type; /**< data type */ size_t data_len; /**< data size */ +/* Shared NULL value instance */ +bhnd_nvram_val bhnd_nvram_val_null = { + .refs = 1, + .val_storage = BHND_NVRAM_VAL_STORAGE_STATIC, + .fmt = &bhnd_nvram_val_null_fmt, + .data_storage = BHND_NVRAM_VAL_DATA_INLINE, + .data_type = BHND_NVRAM_TYPE_NULL, + .data_len = 0, +}; + /** * Return the human-readable name of @p fmt. */ const char * bhnd_nvram_val_fmt_name(const bhnd_nvram_val_fmt *fmt) { return (fmt->name); } /** * Return the default format for values of @p type. */ const bhnd_nvram_val_fmt * bhnd_nvram_val_default_fmt(bhnd_nvram_type type) { switch (type) { case BHND_NVRAM_TYPE_UINT8: return (&bhnd_nvram_val_uint8_fmt); case BHND_NVRAM_TYPE_UINT16: return (&bhnd_nvram_val_uint16_fmt); case BHND_NVRAM_TYPE_UINT32: return (&bhnd_nvram_val_uint32_fmt); case BHND_NVRAM_TYPE_UINT64: return (&bhnd_nvram_val_uint64_fmt); case BHND_NVRAM_TYPE_INT8: return (&bhnd_nvram_val_int8_fmt); case BHND_NVRAM_TYPE_INT16: return (&bhnd_nvram_val_int16_fmt); case BHND_NVRAM_TYPE_INT32: return (&bhnd_nvram_val_int32_fmt); case BHND_NVRAM_TYPE_INT64: return (&bhnd_nvram_val_int64_fmt); case BHND_NVRAM_TYPE_CHAR: return (&bhnd_nvram_val_char_fmt); case BHND_NVRAM_TYPE_STRING: return (&bhnd_nvram_val_string_fmt); + case BHND_NVRAM_TYPE_BOOL: + return (&bhnd_nvram_val_bool_fmt); + case BHND_NVRAM_TYPE_NULL: + return (&bhnd_nvram_val_null_fmt); + case BHND_NVRAM_TYPE_DATA: + return (&bhnd_nvram_val_data_fmt); case BHND_NVRAM_TYPE_UINT8_ARRAY: return (&bhnd_nvram_val_uint8_array_fmt); case BHND_NVRAM_TYPE_UINT16_ARRAY: return (&bhnd_nvram_val_uint16_array_fmt); case BHND_NVRAM_TYPE_UINT32_ARRAY: return (&bhnd_nvram_val_uint32_array_fmt); case BHND_NVRAM_TYPE_UINT64_ARRAY: return (&bhnd_nvram_val_uint64_array_fmt); case BHND_NVRAM_TYPE_INT8_ARRAY: return (&bhnd_nvram_val_int8_array_fmt); case BHND_NVRAM_TYPE_INT16_ARRAY: return (&bhnd_nvram_val_int16_array_fmt); case BHND_NVRAM_TYPE_INT32_ARRAY: return (&bhnd_nvram_val_int32_array_fmt); case BHND_NVRAM_TYPE_INT64_ARRAY: return (&bhnd_nvram_val_int64_array_fmt); case BHND_NVRAM_TYPE_CHAR_ARRAY: return (&bhnd_nvram_val_char_array_fmt); case BHND_NVRAM_TYPE_STRING_ARRAY: return (&bhnd_nvram_val_string_array_fmt); + case BHND_NVRAM_TYPE_BOOL_ARRAY: + return (&bhnd_nvram_val_bool_array_fmt); } /* Quiesce gcc4.2 */ BHND_NV_PANIC("bhnd nvram type %u unknown", type); } /** * Determine whether @p fmt (or new format delegated to by @p fmt) is * capable of direct initialization from buffer @p inp. * * @param[in,out] fmt Indirect pointer to the NVRAM value format. If * the format instance cannot handle the data type * directly, it may delegate to a new format * instance. On success, this parameter will be * set to the format that should be used when * performing initialization from @p inp. * @param inp Input data. * @param ilen Input data length. * @param itype Input data type. * * @retval 0 If initialization from @p inp is supported. * @retval EFTYPE If initialization from @p inp is unsupported. * @retval EFAULT if @p ilen is not correctly aligned for elements of * @p itype. */ static int bhnd_nvram_val_fmt_filter(const bhnd_nvram_val_fmt **fmt, const void *inp, size_t ilen, bhnd_nvram_type itype) { const bhnd_nvram_val_fmt *ofmt, *nfmt; int error; nfmt = ofmt = *fmt; /* Validate alignment */ if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype))) return (error); /* If the format does not provide a filter function, it only supports * direct initialization from its native type */ if (ofmt->op_filter == NULL) { if (itype == ofmt->native_type) return (0); return (EFTYPE); } /* Use the filter function to determine whether direct initialization * from itype is permitted */ error = ofmt->op_filter(&nfmt, inp, ilen, itype); if (error) return (error); /* Retry filter with new format? */ if (ofmt != nfmt) { error = bhnd_nvram_val_fmt_filter(&nfmt, inp, ilen, itype); if (error) return (error); /* Success -- provide delegated format to caller */ *fmt = nfmt; } /* Value can be initialized with provided format and input type */ return (0); } /* Common initialization support for bhnd_nvram_val_init() and * bhnd_nvram_val_new() */ static int bhnd_nvram_val_init_common(bhnd_nvram_val *value, bhnd_nvram_val_storage val_storage, const bhnd_nvram_val_fmt *fmt, const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags) { void *outp; bhnd_nvram_type otype; size_t olen; int error; /* If the value format is unspecified, we use the default format * for the input data type */ if (fmt == NULL) fmt = bhnd_nvram_val_default_fmt(itype); /* Determine expected data type, and allow the format to delegate to * a new format instance */ if ((error = bhnd_nvram_val_fmt_filter(&fmt, inp, ilen, itype))) { /* Direct initialization from the provided input type is * not supported; alue must be initialized with the format's * native type */ otype = fmt->native_type; } else { /* Value can be initialized with provided input type */ otype = itype; } /* Initialize value instance */ *value = BHND_NVRAM_VAL_INITIALIZER(fmt, val_storage); /* If input data already in native format, init directly. */ if (otype == itype) { error = bhnd_nvram_val_set(value, inp, ilen, itype, flags); if (error) return (error); return (0); } /* Determine size when encoded in native format */ error = bhnd_nvram_value_coerce(inp, ilen, itype, NULL, &olen, otype); if (error) return (error); /* Fetch reference to (or allocate) an appropriately sized buffer */ outp = bhnd_nvram_val_alloc_bytes(value, olen, otype, flags); if (outp == NULL) return (ENOMEM); /* Perform encode */ error = bhnd_nvram_value_coerce(inp, ilen, itype, outp, &olen, otype); if (error) return (error); return (0); } /** * Initialize an externally allocated instance of @p value with @p fmt from the * given @p inp buffer of @p itype and @p ilen. * * On success, the caller owns a reference to @p value, and is responsible for * freeing any resources allocated for @p value via bhnd_nvram_val_release(). * * @param value The externally allocated value instance to be * initialized. * @param fmt The value's format, or NULL to use the default format * for @p itype. * @param inp Input buffer. * @param ilen Input buffer length. * @param itype Input buffer type. * @param flags Value flags (see BHND_NVRAM_VAL_*). * * @retval 0 success * @retval ENOMEM If allocation fails. * @retval EFTYPE If @p fmt initialization from @p itype is unsupported. * @retval EFAULT if @p ilen is not correctly aligned for elements of * @p itype. * @retval ERANGE If value coercion would overflow (or underflow) the * @p fmt representation. */ int bhnd_nvram_val_init(bhnd_nvram_val *value, const bhnd_nvram_val_fmt *fmt, const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags) { int error; error = bhnd_nvram_val_init_common(value, BHND_NVRAM_VAL_STORAGE_AUTO, fmt, inp, ilen, itype, flags); if (error) bhnd_nvram_val_release(value); return (error); } /** * Allocate a value instance with @p fmt, and attempt to initialize its internal * representation from the given @p inp buffer of @p itype and @p ilen. * * On success, the caller owns a reference to @p value, and is responsible for * freeing any resources allocated for @p value via bhnd_nvram_val_release(). * * @param[out] value On success, the allocated value instance. * @param fmt The value's format, or NULL to use the default format * for @p itype. * @param inp Input buffer. * @param ilen Input buffer length. * @param itype Input buffer type. * @param flags Value flags (see BHND_NVRAM_VAL_*). * * @retval 0 success * @retval ENOMEM If allocation fails. * @retval EFTYPE If @p fmt initialization from @p itype is unsupported. * @retval EFAULT if @p ilen is not correctly aligned for elements of * @p itype. * @retval ERANGE If value coercion would overflow (or underflow) the * @p fmt representation. */ int bhnd_nvram_val_new(bhnd_nvram_val **value, const bhnd_nvram_val_fmt *fmt, const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags) { int error; /* Allocate new instance */ if ((*value = bhnd_nv_malloc(sizeof(**value))) == NULL) return (ENOMEM); /* Perform common initialization. */ error = bhnd_nvram_val_init_common(*value, BHND_NVRAM_VAL_STORAGE_DYNAMIC, fmt, inp, ilen, itype, flags); if (error) { /* Will also free() the value allocation */ bhnd_nvram_val_release(*value); } return (error); } /* Common initialization support for bhnd_nvram_val_convert_init() and * bhnd_nvram_val_convert_new() */ static int bhnd_nvram_val_convert_common(bhnd_nvram_val *value, bhnd_nvram_val_storage val_storage, const bhnd_nvram_val_fmt *fmt, bhnd_nvram_val *src, uint32_t flags) { const void *inp; void *outp; bhnd_nvram_type itype, otype; size_t ilen, olen; int error; /* Determine whether direct initialization from the source value's * existing data type is supported by the new format */ inp = bhnd_nvram_val_bytes(src, &ilen, &itype); if (bhnd_nvram_val_fmt_filter(&fmt, inp, ilen, itype) == 0) { /* Adjust value flags based on the source data storage */ switch (src->data_storage) { case BHND_NVRAM_VAL_DATA_NONE: case BHND_NVRAM_VAL_DATA_INLINE: case BHND_NVRAM_VAL_DATA_EXT_WEAK: case BHND_NVRAM_VAL_DATA_EXT_ALLOC: break; case BHND_NVRAM_VAL_DATA_EXT_STATIC: /* If the source data has static storage duration, * we should apply that transitively */ if (flags & BHND_NVRAM_VAL_BORROW_DATA) flags |= BHND_NVRAM_VAL_STATIC_DATA; break; } /* Delegate to standard initialization */ return (bhnd_nvram_val_init_common(value, val_storage, fmt, inp, ilen, itype, flags)); } /* Value must be initialized with the format's native type */ otype = fmt->native_type; /* Initialize value instance */ *value = BHND_NVRAM_VAL_INITIALIZER(fmt, val_storage); /* Determine size when encoded in native format */ if ((error = bhnd_nvram_val_encode(src, NULL, &olen, otype))) return (error); /* Fetch reference to (or allocate) an appropriately sized buffer */ outp = bhnd_nvram_val_alloc_bytes(value, olen, otype, flags); if (outp == NULL) return (ENOMEM); /* Perform encode */ if ((error = bhnd_nvram_val_encode(src, outp, &olen, otype))) return (error); return (0); } /** * Initialize an externally allocated instance of @p value with @p fmt, and * attempt to initialize its internal representation from the given @p src * value. * * On success, the caller owns a reference to @p value, and is responsible for * freeing any resources allocated for @p value via bhnd_nvram_val_release(). * * @param value The externally allocated value instance to be * initialized. * @param fmt The value's format. * @param src Input value to be converted. * @param flags Value flags (see BHND_NVRAM_VAL_*). * * @retval 0 success * @retval ENOMEM If allocation fails. * @retval EFTYPE If @p fmt initialization from @p src is unsupported. * @retval EFAULT if @p ilen is not correctly aligned for elements of * @p itype. * @retval ERANGE If value coercion of @p src would overflow * (or underflow) the @p fmt representation. */ int bhnd_nvram_val_convert_init(bhnd_nvram_val *value, const bhnd_nvram_val_fmt *fmt, bhnd_nvram_val *src, uint32_t flags) { int error; error = bhnd_nvram_val_convert_common(value, BHND_NVRAM_VAL_STORAGE_AUTO, fmt, src, flags); if (error) bhnd_nvram_val_release(value); return (error); } /** * Allocate a value instance with @p fmt, and attempt to initialize its internal * representation from the given @p src value. * * On success, the caller owns a reference to @p value, and is responsible for * freeing any resources allocated for @p value via bhnd_nvram_val_release(). * * @param[out] value On success, the allocated value instance. * @param fmt The value's format. * @param src Input value to be converted. * @param flags Value flags (see BHND_NVRAM_VAL_*). * * @retval 0 success * @retval ENOMEM If allocation fails. * @retval EFTYPE If @p fmt initialization from @p src is unsupported. * @retval EFAULT if @p ilen is not correctly aligned for elements of * @p itype. * @retval ERANGE If value coercion of @p src would overflow * (or underflow) the @p fmt representation. */ int bhnd_nvram_val_convert_new(bhnd_nvram_val **value, const bhnd_nvram_val_fmt *fmt, bhnd_nvram_val *src, uint32_t flags) { int error; /* Allocate new instance */ if ((*value = bhnd_nv_malloc(sizeof(**value))) == NULL) return (ENOMEM); /* Perform common initialization. */ error = bhnd_nvram_val_convert_common(*value, BHND_NVRAM_VAL_STORAGE_DYNAMIC, fmt, src, flags); if (error) { /* Will also free() the value allocation */ bhnd_nvram_val_release(*value); } return (error); } /** * Copy or retain a reference to @p value. * * On success, the caller is responsible for freeing the result via * bhnd_nvram_val_release(). * * @param value The value to be copied (or retained). * * @retval bhnd_nvram_val if @p value was successfully copied or retained. * @retval NULL if allocation failed. */ bhnd_nvram_val * bhnd_nvram_val_copy(bhnd_nvram_val *value) { bhnd_nvram_val *result; const void *bytes; bhnd_nvram_type type; size_t len; uint32_t flags; int error; switch (value->val_storage) { case BHND_NVRAM_VAL_STORAGE_STATIC: /* If static, can return as-is */ return (value); case BHND_NVRAM_VAL_STORAGE_DYNAMIC: if (!BHND_NVRAM_VAL_NEED_COPY(value)) { refcount_acquire(&value->refs); return (value); } /* Perform copy below */ break; case BHND_NVRAM_VAL_STORAGE_AUTO: BHND_NV_ASSERT(value->refs == 1, ("non-allocated value has " "active refcount (%u)", value->refs)); /* Perform copy below */ break; } /* Compute the new value's flags based on the source value */ switch (value->data_storage) { case BHND_NVRAM_VAL_DATA_NONE: case BHND_NVRAM_VAL_DATA_INLINE: case BHND_NVRAM_VAL_DATA_EXT_WEAK: case BHND_NVRAM_VAL_DATA_EXT_ALLOC: /* Copy the source data and permit additional allocation if the * value cannot be represented inline */ flags = BHND_NVRAM_VAL_COPY_DATA|BHND_NVRAM_VAL_DYNAMIC; break; case BHND_NVRAM_VAL_DATA_EXT_STATIC: flags = BHND_NVRAM_VAL_STATIC_DATA; break; default: BHND_NV_PANIC("invalid storage type: %d", value->data_storage); } /* Allocate new value copy */ bytes = bhnd_nvram_val_bytes(value, &len, &type); error = bhnd_nvram_val_new(&result, value->fmt, bytes, len, type, flags); if (error) { BHND_NV_LOG("copy failed: %d", error); return (NULL); } return (result); } /** * Release a reference to @p value. * * If this is the last reference, all associated resources will be freed. * * @param value The value to be released. */ void bhnd_nvram_val_release(bhnd_nvram_val *value) { BHND_NV_ASSERT(value->refs >= 1, ("value over-released")); /* Skip if value is static */ if (value->val_storage == BHND_NVRAM_VAL_STORAGE_STATIC) return; /* Drop reference */ if (!refcount_release(&value->refs)) return; /* Free allocated external representation data */ switch (value->data_storage) { case BHND_NVRAM_VAL_DATA_EXT_ALLOC: bhnd_nv_free(__DECONST(void *, value->data.ptr)); break; case BHND_NVRAM_VAL_DATA_NONE: case BHND_NVRAM_VAL_DATA_INLINE: case BHND_NVRAM_VAL_DATA_EXT_WEAK: case BHND_NVRAM_VAL_DATA_EXT_STATIC: /* Nothing to free */ break; } /* Free instance if dynamically allocated */ if (value->val_storage == BHND_NVRAM_VAL_STORAGE_DYNAMIC) bhnd_nv_free(value); } /** + * Standard BHND_NVRAM_TYPE_NULL encoding implementation. + */ +static int +bhnd_nvram_val_encode_null(const void *inp, size_t ilen, bhnd_nvram_type itype, + void *outp, size_t *olen, bhnd_nvram_type otype) +{ + size_t limit, nbytes; + + BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_NULL, + ("unsupported type: %d", itype)); + + /* Determine output byte limit */ + if (outp != NULL) + limit = *olen; + else + limit = 0; + + nbytes = 0; + + /* Write to output */ + switch (otype) { + case BHND_NVRAM_TYPE_NULL: + /* Can be directly encoded as a zero-length NULL value */ + nbytes = 0; + break; + default: + /* Not representable */ + return (EFTYPE); + } + + /* Provide required length */ + *olen = nbytes; + if (limit < *olen) { + if (outp == NULL) + return (0); + + return (ENOMEM); + } + + return (0); +} + +/** + * Standard BHND_NVRAM_TYPE_BOOL encoding implementation. + */ +static int +bhnd_nvram_val_encode_bool(const void *inp, size_t ilen, bhnd_nvram_type itype, + void *outp, size_t *olen, bhnd_nvram_type otype) +{ + bhnd_nvram_bool_t bval; + size_t limit, nbytes, nelem; + int error; + + BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_BOOL, + ("unsupported type: %d", itype)); + + /* Determine output byte limit */ + if (outp != NULL) + limit = *olen; + else + limit = 0; + + /* Must be exactly one element in input */ + if ((error = bhnd_nvram_value_nelem(inp, ilen, itype, &nelem))) + return (error); + + if (nelem != 1) + return (EFTYPE); + + /* Fetch (and normalize) boolean value */ + bval = (*(const bhnd_nvram_bool_t *)inp != 0) ? true : false; + + /* Write to output */ + switch (otype) { + case BHND_NVRAM_TYPE_NULL: + /* False can be directly encoded as a zero-length NULL value */ + if (bval != false) + return (EFTYPE); + + nbytes = 0; + break; + + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_STRING_ARRAY: { + /* Can encode as "true" or "false" */ + const char *str = bval ? "true" : "false"; + + nbytes = strlen(str) + 1; + if (limit > nbytes) + strcpy(outp, str); + + break; + } + + default: + /* If output type is an integer, we can delegate to standard + * integer encoding to encode as zero or one. */ + if (bhnd_nvram_is_int_type(otype)) { + uint8_t ival = bval ? 1 : 0; + + return (bhnd_nvram_val_encode_int(&ival, sizeof(ival), + BHND_NVRAM_TYPE_UINT8, outp, olen, otype)); + } + + /* Otherwise not representable */ + return (EFTYPE); + } + + /* Provide required length */ + *olen = nbytes; + if (limit < *olen) { + if (outp == NULL) + return (0); + + return (ENOMEM); + } + + return (0); +} + +/** + * Standard BHND_NVRAM_TYPE_DATA encoding implementation. + */ +static int +bhnd_nvram_val_encode_data(const void *inp, size_t ilen, bhnd_nvram_type itype, + void *outp, size_t *olen, bhnd_nvram_type otype) +{ + BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_DATA, + ("unsupported type: %d", itype)); + + /* Write to output */ + switch (otype) { + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_STRING_ARRAY: + /* If encoding as a string, produce an EFI-style hexadecimal + * byte array (HF1F...) by interpreting the octet string + * as an array of uint8 values */ + return (bhnd_nvram_value_printf("H%[]02hhX", inp, ilen, + BHND_NVRAM_TYPE_UINT8_ARRAY, outp, olen, "")); + + default: + /* Fall back on direct interpretation as an array of 8-bit + * integers array */ + return (bhnd_nvram_value_coerce(inp, ilen, + BHND_NVRAM_TYPE_UINT8_ARRAY, outp, olen, otype)); + } +} + + +/** * Standard string/char array/char encoding implementation. * * Input type must be one of: * - BHND_NVRAM_TYPE_STRING * - BHND_NVRAM_TYPE_CHAR * - BHND_NVRAM_TYPE_CHAR_ARRAY */ static int bhnd_nvram_val_encode_string(const void *inp, size_t ilen, bhnd_nvram_type itype, void *outp, size_t *olen, bhnd_nvram_type otype) { const char *cstr; bhnd_nvram_type otype_base; size_t cstr_size, cstr_len; size_t limit, nbytes; BHND_NV_ASSERT( itype == BHND_NVRAM_TYPE_STRING || itype == BHND_NVRAM_TYPE_CHAR || itype == BHND_NVRAM_TYPE_CHAR_ARRAY, ("unsupported type: %d", itype)); cstr = inp; cstr_size = ilen; nbytes = 0; otype_base = bhnd_nvram_base_type(otype); /* Determine output byte limit */ if (outp != NULL) limit = *olen; else limit = 0; /* Determine string length, minus trailing NUL (if any) */ cstr_len = strnlen(cstr, cstr_size); /* Parse the string data and write to output */ switch (otype) { + case BHND_NVRAM_TYPE_NULL: + /* Only an empty string may be represented as a NULL value */ + if (cstr_len != 0) + return (EFTYPE); + + *olen = 0; + return (0); + case BHND_NVRAM_TYPE_CHAR: case BHND_NVRAM_TYPE_CHAR_ARRAY: /* String must contain exactly 1 non-terminating-NUL character * to be represented as a single char */ if (!bhnd_nvram_is_array_type(otype)) { if (cstr_len != 1) return (EFTYPE); } /* Copy out the characters directly (excluding trailing NUL) */ for (size_t i = 0; i < cstr_len; i++) { if (limit > nbytes) *((uint8_t *)outp + nbytes) = cstr[i]; nbytes++; } /* Provide required length */ *olen = nbytes; if (limit < *olen && outp != NULL) return (ENOMEM); return (0); + case BHND_NVRAM_TYPE_BOOL: + case BHND_NVRAM_TYPE_BOOL_ARRAY: { + const char *p; + size_t plen; + bhnd_nvram_bool_t bval; + + /* Trim leading/trailing whitespace */ + p = cstr; + plen = bhnd_nvram_trim_field(&p, cstr_len, '\0'); + + /* Parse string representation */ + if (strncasecmp(p, "true", plen) == 0 || + strncasecmp(p, "yes", plen) == 0 || + strncmp(p, "1", plen) == 0) + { + bval = true; + } else if (strncasecmp(p, "false", plen) == 0 || + strncasecmp(p, "no", plen) == 0 || + strncmp(p, "0", plen) == 0) + { + bval = false; + } else { + /* Not a recognized boolean string */ + return (EFTYPE); + } + + /* Write to output */ + nbytes = sizeof(bhnd_nvram_bool_t); + if (limit >= nbytes) + *((bhnd_nvram_bool_t *)outp) = bval; + + /* Provide required length */ + *olen = nbytes; + if (limit < *olen && outp != NULL) + return (ENOMEM); + + return (0); + } + + case BHND_NVRAM_TYPE_DATA: { + const char *p; + size_t plen, parsed_len; + int error; + + /* Trim leading/trailing whitespace */ + p = cstr; + plen = bhnd_nvram_trim_field(&p, cstr_len, '\0'); + + /* Check for EFI-style hexadecimal byte array string format. + * Must have a 'H' prefix */ + if (plen < 1 || bhnd_nv_toupper(*p) != 'H') + return (EFTYPE); + + /* Skip leading 'H' */ + p++; + plen--; + + /* Parse the input string's two-char octets until the end + * of input is reached. The last octet may contain only + * one char */ + while (plen > 0) { + uint8_t byte; + size_t byte_len = sizeof(byte); + + /* Parse next two-character hex octet */ + error = bhnd_nvram_parse_int(p, bhnd_nv_ummin(plen, 2), + 16, &parsed_len, &byte, &byte_len, otype_base); + if (error) { + BHND_NV_DEBUG("error parsing '%.*s' as " + "integer: %d\n", BHND_NV_PRINT_WIDTH(plen), + p, error); + + return (error); + } + + /* Write to output */ + if (limit > nbytes) + *((uint8_t *)outp + nbytes) = byte; + nbytes++; + + /* Advance input */ + p += parsed_len; + plen -= parsed_len; + } + + /* Provide required length */ + *olen = nbytes; + if (limit < *olen && outp != NULL) + return (ENOMEM); + + return (0); + } + case BHND_NVRAM_TYPE_UINT8: case BHND_NVRAM_TYPE_UINT8_ARRAY: case BHND_NVRAM_TYPE_UINT16: case BHND_NVRAM_TYPE_UINT16_ARRAY: case BHND_NVRAM_TYPE_UINT32: case BHND_NVRAM_TYPE_UINT32_ARRAY: case BHND_NVRAM_TYPE_UINT64: case BHND_NVRAM_TYPE_UINT64_ARRAY: case BHND_NVRAM_TYPE_INT8: case BHND_NVRAM_TYPE_INT8_ARRAY: case BHND_NVRAM_TYPE_INT16: case BHND_NVRAM_TYPE_INT16_ARRAY: case BHND_NVRAM_TYPE_INT32: case BHND_NVRAM_TYPE_INT32_ARRAY: case BHND_NVRAM_TYPE_INT64: case BHND_NVRAM_TYPE_INT64_ARRAY: { const char *p; size_t plen, parsed_len; int error; /* Trim leading/trailing whitespace */ p = cstr; plen = bhnd_nvram_trim_field(&p, cstr_len, '\0'); /* Try to parse the integer value */ error = bhnd_nvram_parse_int(p, plen, 0, &parsed_len, outp, olen, otype_base); if (error) { BHND_NV_DEBUG("error parsing '%.*s' as integer: %d\n", BHND_NV_PRINT_WIDTH(plen), p, error); return (error); } /* Do additional bytes remain unparsed? */ if (plen != parsed_len) { BHND_NV_DEBUG("error parsing '%.*s' as a single " "integer value; trailing garbage '%.*s'\n", BHND_NV_PRINT_WIDTH(plen), p, BHND_NV_PRINT_WIDTH(plen-parsed_len), p+parsed_len); return (EFTYPE); } return (0); } case BHND_NVRAM_TYPE_STRING: case BHND_NVRAM_TYPE_STRING_ARRAY: /* Copy out the string representation as-is */ *olen = cstr_size; /* Need additional space for trailing NUL? */ if (cstr_len == cstr_size) (*olen)++; /* Skip output? */ if (outp == NULL) return (0); /* Verify required length */ if (limit < *olen) return (ENOMEM); /* Copy and NUL terminate */ strncpy(outp, cstr, cstr_len); *((char *)outp + cstr_len) = '\0'; return (0); } BHND_NV_PANIC("unknown type %s", bhnd_nvram_type_name(otype)); } /** * Standard integer encoding implementation. */ static int bhnd_nvram_val_encode_int(const void *inp, size_t ilen, bhnd_nvram_type itype, void *outp, size_t *olen, bhnd_nvram_type otype) { bhnd_nvram_type otype_base; size_t limit, nbytes; bool itype_signed, otype_signed, otype_int; union { uint64_t u64; int64_t i64; } intv; BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("non-integer type")); /* Determine output byte limit */ if (outp != NULL) limit = *olen; else limit = 0; /* Fetch output type info */ otype_base = bhnd_nvram_base_type(otype); otype_int = bhnd_nvram_is_int_type(otype); otype_signed = bhnd_nvram_is_signed_type(otype_base); /* * Promote integer value to a common 64-bit representation. */ switch (itype) { case BHND_NVRAM_TYPE_UINT8: if (ilen != sizeof(uint8_t)) return (EFAULT); itype_signed = false; intv.u64 = *(const uint8_t *)inp; break; case BHND_NVRAM_TYPE_UINT16: if (ilen != sizeof(uint16_t)) return (EFAULT); itype_signed = false; intv.u64 = *(const uint16_t *)inp; break; case BHND_NVRAM_TYPE_UINT32: if (ilen != sizeof(uint32_t)) return (EFAULT); itype_signed = false; intv.u64 = *(const uint32_t *)inp; break; case BHND_NVRAM_TYPE_UINT64: if (ilen != sizeof(uint64_t)) return (EFAULT); itype_signed = false; intv.u64 = *(const uint64_t *)inp; break; case BHND_NVRAM_TYPE_INT8: if (ilen != sizeof(int8_t)) return (EFAULT); itype_signed = true; intv.i64 = *(const int8_t *)inp; break; case BHND_NVRAM_TYPE_INT16: if (ilen != sizeof(int16_t)) return (EFAULT); itype_signed = true; intv.i64 = *(const int16_t *)inp; break; case BHND_NVRAM_TYPE_INT32: if (ilen != sizeof(int32_t)) return (EFAULT); itype_signed = true; intv.i64 = *(const int32_t *)inp; break; case BHND_NVRAM_TYPE_INT64: if (ilen != sizeof(int32_t)) return (EFAULT); itype_signed = true; intv.i64 = *(const int32_t *)inp; break; default: BHND_NV_PANIC("invalid type %d\n", itype); } /* Perform signed/unsigned conversion */ if (itype_signed && otype_int && !otype_signed) { if (intv.i64 < 0) { /* Can't represent negative value */ BHND_NV_LOG("cannot represent %" PRId64 " as %s\n", intv.i64, bhnd_nvram_type_name(otype)); return (ERANGE); } /* Convert to unsigned representation */ intv.u64 = intv.i64; } else if (!itype_signed && otype_int && otype_signed) { /* Handle unsigned -> signed coercions */ if (intv.u64 > INT64_MAX) { /* Can't represent positive value */ BHND_NV_LOG("cannot represent %" PRIu64 " as %s\n", intv.u64, bhnd_nvram_type_name(otype)); return (ERANGE); } /* Convert to signed representation */ intv.i64 = intv.u64; } /* Write output */ switch (otype) { + case BHND_NVRAM_TYPE_NULL: + /* Cannot encode an integer value as NULL */ + return (EFTYPE); + + case BHND_NVRAM_TYPE_BOOL: { + bhnd_nvram_bool_t bval; + + if (intv.u64 == 0 || intv.u64 == 1) { + bval = intv.u64; + } else { + /* Encoding as a bool would lose information */ + return (ERANGE); + } + + nbytes = sizeof(bhnd_nvram_bool_t); + if (limit >= nbytes) + *((bhnd_nvram_bool_t *)outp) = bval; + + break; + } + case BHND_NVRAM_TYPE_CHAR: case BHND_NVRAM_TYPE_CHAR_ARRAY: + case BHND_NVRAM_TYPE_DATA: case BHND_NVRAM_TYPE_UINT8: case BHND_NVRAM_TYPE_UINT8_ARRAY: if (intv.u64 > UINT8_MAX) return (ERANGE); nbytes = sizeof(uint8_t); if (limit >= nbytes) *((uint8_t *)outp) = (uint8_t)intv.u64; break; case BHND_NVRAM_TYPE_UINT16: case BHND_NVRAM_TYPE_UINT16_ARRAY: if (intv.u64 > UINT16_MAX) return (ERANGE); nbytes = sizeof(uint16_t); if (limit >= nbytes) *((uint16_t *)outp) = (uint16_t)intv.u64; break; case BHND_NVRAM_TYPE_UINT32: case BHND_NVRAM_TYPE_UINT32_ARRAY: if (intv.u64 > UINT32_MAX) return (ERANGE); nbytes = sizeof(uint32_t); if (limit >= nbytes) *((uint32_t *)outp) = (uint32_t)intv.u64; break; case BHND_NVRAM_TYPE_UINT64: case BHND_NVRAM_TYPE_UINT64_ARRAY: nbytes = sizeof(uint64_t); if (limit >= nbytes) *((uint64_t *)outp) = intv.u64; break; case BHND_NVRAM_TYPE_INT8: case BHND_NVRAM_TYPE_INT8_ARRAY: if (intv.i64 < INT8_MIN || intv.i64 > INT8_MAX) return (ERANGE); nbytes = sizeof(int8_t); if (limit >= nbytes) *((int8_t *)outp) = (int8_t)intv.i64; break; case BHND_NVRAM_TYPE_INT16: case BHND_NVRAM_TYPE_INT16_ARRAY: if (intv.i64 < INT16_MIN || intv.i64 > INT16_MAX) return (ERANGE); nbytes = sizeof(int16_t); if (limit >= nbytes) *((int16_t *)outp) = (int16_t)intv.i64; break; case BHND_NVRAM_TYPE_INT32: case BHND_NVRAM_TYPE_INT32_ARRAY: if (intv.i64 < INT32_MIN || intv.i64 > INT32_MAX) return (ERANGE); nbytes = sizeof(int32_t); if (limit >= nbytes) *((int32_t *)outp) = (int32_t)intv.i64; break; case BHND_NVRAM_TYPE_INT64: case BHND_NVRAM_TYPE_INT64_ARRAY: nbytes = sizeof(int64_t); if (limit >= nbytes) *((int64_t *)outp) = intv.i64; break; case BHND_NVRAM_TYPE_STRING: case BHND_NVRAM_TYPE_STRING_ARRAY: { ssize_t len; /* Attempt to write the entry + NUL */ if (otype_signed) { len = snprintf(outp, limit, "%" PRId64, intv.i64); } else { len = snprintf(outp, limit, "%" PRIu64, intv.u64); } if (len < 0) { BHND_NV_LOG("snprintf() failed: %zd\n", len); return (EFTYPE); } /* Set total length to the formatted string length, plus * trailing NUL */ nbytes = len + 1; break; } default: BHND_NV_LOG("unknown type %s\n", bhnd_nvram_type_name(otype)); return (EFTYPE); } /* Provide required length */ *olen = nbytes; if (limit < *olen) { if (outp == NULL) return (0); return (ENOMEM); } return (0); } /** * Encode the given @p value as @p otype, writing the result to @p outp. * * @param value The value to be encoded. * @param[out] outp On success, the 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 data type to be written to @p outp. * * @retval 0 success * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen * is too small to hold the encoded value. * @retval EFTYPE If value coercion from @p value to @p otype is * impossible. * @retval ERANGE If value coercion would overflow (or underflow) the * a @p otype representation. */ int bhnd_nvram_val_encode(bhnd_nvram_val *value, void *outp, size_t *olen, bhnd_nvram_type otype) { /* Prefer format implementation */ if (value->fmt->op_encode != NULL) return (value->fmt->op_encode(value, outp, olen, otype)); return (bhnd_nvram_val_generic_encode(value, outp, olen, otype)); } /** * Encode the given @p value's element as @p otype, writing the result to * @p outp. * * @param inp The element to be be encoded. Must be a value * previously returned by bhnd_nvram_val_next() * or bhnd_nvram_val_elem(). * @param ilen The size of @p inp, as returned by * bhnd_nvram_val_next() or bhnd_nvram_val_elem(). * @param[out] outp On success, the 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 data type to be written to @p outp. * * @retval 0 success * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen * is too small to hold the encoded value. * @retval EFTYPE If value coercion from @p value to @p otype is * impossible. * @retval ERANGE If value coercion would overflow (or underflow) the * a @p otype representation. */ int bhnd_nvram_val_encode_elem(bhnd_nvram_val *value, const void *inp, size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) { /* Prefer format implementation */ if (value->fmt->op_encode_elem != NULL) { return (value->fmt->op_encode_elem(value, inp, ilen, outp, olen, otype)); } return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen, outp, olen, otype)); } /** * Return the type, size, and a pointer to the internal representation * of @p value. * * @param value The value to be queried. * @param[out] olen Size of the returned data, in bytes. * @param[out] otype Data type. */ const void * bhnd_nvram_val_bytes(bhnd_nvram_val *value, size_t *olen, bhnd_nvram_type *otype) { /* Provide type and length */ *otype = value->data_type; *olen = value->data_len; switch (value->data_storage) { case BHND_NVRAM_VAL_DATA_EXT_ALLOC: case BHND_NVRAM_VAL_DATA_EXT_STATIC: case BHND_NVRAM_VAL_DATA_EXT_WEAK: /* Return a pointer to external storage */ return (value->data.ptr); case BHND_NVRAM_VAL_DATA_INLINE: /* Return a pointer to inline storage */ return (&value->data); case BHND_NVRAM_VAL_DATA_NONE: BHND_NV_PANIC("uninitialized value"); } BHND_NV_PANIC("unknown storage type: %d", value->data_storage); } /** * Iterate over all array elements in @p value. * * @param value The value to be iterated * @param prev A value pointer previously returned by * bhnd_nvram_val_next() or bhnd_nvram_val_elem(), * or NULL to begin iteration at the first element. * @param[in,out] olen If @p prev is non-NULL, @p olen must be a * pointer to the length previously returned by * bhnd_nvram_val_next() or bhnd_nvram_val_elem(). * On success, will be set to the next element's * length, in bytes. * * @retval non-NULL A borrowed reference to the element data. * @retval NULL If the end of the element array is reached. */ const void * bhnd_nvram_val_next(bhnd_nvram_val *value, const void *prev, size_t *olen) { /* Prefer the format implementation */ if (value->fmt->op_next != NULL) return (value->fmt->op_next(value, prev, olen)); return (bhnd_nvram_val_generic_next(value, prev, olen)); } /** * Return the value's data type. * * @param value The value to be queried. */ bhnd_nvram_type bhnd_nvram_val_type(bhnd_nvram_val *value) { return (value->data_type); } /** * Return value's element data type. * * @param value The value to be queried. */ bhnd_nvram_type bhnd_nvram_val_elem_type(bhnd_nvram_val *value) { return (bhnd_nvram_base_type(value->data_type)); } /** * Return the total number of elements represented by @p value. */ size_t bhnd_nvram_val_nelem(bhnd_nvram_val *value) { const void *bytes; bhnd_nvram_type type; size_t nelem, len; int error; /* Prefer format implementation */ if (value->fmt->op_nelem != NULL) return (value->fmt->op_nelem(value)); /* * If a custom op_next() is defined, bhnd_nvram_value_nelem() almost * certainly cannot produce a valid element count; it assumes a standard * data format that may not apply when custom iteration is required. * * Instead, use bhnd_nvram_val_next() to parse the backing data and * produce a total count. */ if (value->fmt->op_next != NULL) { const void *next; next = NULL; nelem = 0; while ((next = bhnd_nvram_val_next(value, next, &len)) != NULL) nelem++; return (nelem); } /* Otherwise, compute the standard element count */ bytes = bhnd_nvram_val_bytes(value, &len, &type); if ((error = bhnd_nvram_value_nelem(bytes, len, type, &nelem))) { /* Should always succeed */ BHND_NV_PANIC("error calculating element count for type '%s' " "with length %zu: %d\n", bhnd_nvram_type_name(type), len, error); } return (nelem); } /** * Generic implementation of bhnd_nvram_val_op_encode(), compatible with * all supported NVRAM data types. */ int bhnd_nvram_val_generic_encode(bhnd_nvram_val *value, void *outp, size_t *olen, bhnd_nvram_type otype) { const void *inp; bhnd_nvram_type itype; size_t ilen; const void *next; bhnd_nvram_type otype_base; size_t limit, nelem, nbytes; size_t next_len; int error; nbytes = 0; nelem = 0; otype_base = bhnd_nvram_base_type(otype); inp = bhnd_nvram_val_bytes(value, &ilen, &itype); /* * Normally, an array type is not universally representable as * non-array type. * * As exceptions, we support conversion directly to/from: * - CHAR_ARRAY/STRING: * ->STRING Interpret the character array as a * non-NUL-terminated string. * ->CHAR_ARRAY Trim the trailing NUL from the string. */ #define BHND_NV_IS_ISO_CONV(_lhs, _rhs) \ ((itype == BHND_NVRAM_TYPE_ ## _lhs && \ otype == BHND_NVRAM_TYPE_ ## _rhs) || \ (itype == BHND_NVRAM_TYPE_ ## _rhs && \ otype == BHND_NVRAM_TYPE_ ## _lhs)) if (BHND_NV_IS_ISO_CONV(CHAR_ARRAY, STRING)) { return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen, otype)); } #undef BHND_NV_IS_ISO_CONV /* * If both input and output are non-array types, try to encode them * without performing element iteration. */ if (!bhnd_nvram_is_array_type(itype) && !bhnd_nvram_is_array_type(otype)) { return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen, otype)); } /* Determine output byte limit */ if (outp != NULL) limit = *olen; else limit = 0; /* Iterate over our array elements and encode as the requested * type */ next = NULL; while ((next = bhnd_nvram_val_next(value, next, &next_len))) { void *elem_outp; size_t elem_nbytes; /* If the output type is not an array type, we can only encode * one element */ nelem++; if (nelem > 1 && !bhnd_nvram_is_array_type(otype)) { return (EFTYPE); } /* Determine output offset / limit */ if (nbytes >= limit) { elem_nbytes = 0; elem_outp = NULL; } else { elem_nbytes = limit - nbytes; elem_outp = (uint8_t *)outp + nbytes; } /* Attempt encode */ error = bhnd_nvram_val_encode_elem(value, next, next_len, elem_outp, &elem_nbytes, otype_base); /* If encoding failed for any reason other than ENOMEM (which * we'll detect and report below), return immediately */ if (error && error != ENOMEM) return (error); /* Add to total length */ if (SIZE_MAX - nbytes < elem_nbytes) return (EFTYPE); /* would overflow size_t */ nbytes += elem_nbytes; } /* Provide the actual length */ *olen = nbytes; /* If no output was requested, nothing left to do */ if (outp == NULL) return (0); /* Otherwise, report a memory error if the output buffer was too * small */ if (limit < nbytes) return (ENOMEM); return (0); } /** * Generic implementation of bhnd_nvram_val_op_encode_elem(), compatible with * all supported NVRAM data types. */ int bhnd_nvram_val_generic_encode_elem(bhnd_nvram_val *value, const void *inp, size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) { bhnd_nvram_type itype; itype = bhnd_nvram_val_elem_type(value); switch (itype) { + case BHND_NVRAM_TYPE_NULL: + return (bhnd_nvram_val_encode_null(inp, ilen, itype, outp, olen, + otype)); + + case BHND_NVRAM_TYPE_DATA: + return (bhnd_nvram_val_encode_data(inp, ilen, itype, outp, + olen, otype)); + case BHND_NVRAM_TYPE_STRING: case BHND_NVRAM_TYPE_CHAR: return (bhnd_nvram_val_encode_string(inp, ilen, itype, outp, olen, otype)); + case BHND_NVRAM_TYPE_BOOL: + return (bhnd_nvram_val_encode_bool(inp, ilen, itype, outp, olen, + otype)); + 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: return (bhnd_nvram_val_encode_int(inp, ilen, itype, outp, olen, otype)); default: BHND_NV_PANIC("missing encode_elem() implementation"); } } /** * Generic implementation of bhnd_nvram_val_op_next(), compatible with * all supported NVRAM data types. */ const void * bhnd_nvram_val_generic_next(bhnd_nvram_val *value, const void *prev, size_t *olen) { const uint8_t *inp; bhnd_nvram_type itype; size_t ilen; /* Iterate over the backing representation */ inp = bhnd_nvram_val_bytes(value, &ilen, &itype); return (bhnd_nvram_value_array_next(inp, ilen, itype, prev, olen)); } /** * Initialize the representation of @p value with @p ptr. * * @param value The value to be initialized. * @param inp The external representation. * @param ilen The external representation length, in bytes. * @param itype The external representation's data type. * @param flags Value flags. * * @retval 0 success. * @retval ENOMEM if allocation fails * @retval EFTYPE if @p itype is not an array type, and @p ilen is not * equal to the size of a single element of @p itype. * @retval EFAULT if @p ilen is not correctly aligned for elements of * @p itype. */ static int bhnd_nvram_val_set(bhnd_nvram_val *value, const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags) { void *bytes; int error; BHND_NVRAM_VAL_ASSERT_EMPTY(value); /* Validate alignment */ if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype))) return (error); /* Reference the external data */ if ((flags & BHND_NVRAM_VAL_BORROW_DATA) || (flags & BHND_NVRAM_VAL_STATIC_DATA)) { if (flags & BHND_NVRAM_VAL_STATIC_DATA) value->data_storage = BHND_NVRAM_VAL_DATA_EXT_STATIC; else value->data_storage = BHND_NVRAM_VAL_DATA_EXT_WEAK; value->data.ptr = inp; value->data_type = itype; value->data_len = ilen; return (0); } /* Fetch reference to (or allocate) an appropriately sized buffer */ bytes = bhnd_nvram_val_alloc_bytes(value, ilen, itype, flags); if (bytes == NULL) return (ENOMEM); /* Copy data */ memcpy(bytes, inp, ilen); return (0); } /** * Initialize the internal inline representation of @p value with a copy of * the data referenced by @p inp of @p itype. * * If @p inp is NULL, @p itype and @p ilen will be validated, but no data will * be copied. * * @param value The value to be initialized. * @param inp The input data to be copied, or NULL to verify * that data of @p ilen and @p itype can be represented * inline. * @param ilen The size of the external buffer to be allocated. * @param itype The type of the external buffer to be allocated. * * @retval 0 success * @retval ENOMEM if @p ilen is too large to be represented inline. * @retval EFAULT if @p ilen is not correctly aligned for elements of * @p itype. */ static int bhnd_nvram_val_set_inline(bhnd_nvram_val *value, const void *inp, size_t ilen, bhnd_nvram_type itype) { BHND_NVRAM_VAL_ASSERT_EMPTY(value); #define NV_STORE_INIT_INLINE() do { \ value->data_len = ilen; \ value->data_type = itype; \ } while(0) #define NV_STORE_INLINE(_type, _dest) do { \ if (ilen != sizeof(_type)) \ return (EFAULT); \ \ if (inp != NULL) { \ value->data._dest[0] = *(const _type *)inp; \ NV_STORE_INIT_INLINE(); \ } \ } while (0) #define NV_COPY_ARRRAY_INLINE(_type, _dest) do { \ if (ilen % sizeof(_type) != 0) \ return (EFAULT); \ \ if (ilen > nitems(value->data. _dest)) \ return (ENOMEM); \ \ if (inp == NULL) \ return (0); \ \ memcpy(&value->data._dest, inp, ilen); \ if (inp != NULL) { \ memcpy(&value->data._dest, inp, ilen); \ NV_STORE_INIT_INLINE(); \ } \ } while (0) /* Attempt to copy to inline storage */ switch (itype) { + case BHND_NVRAM_TYPE_NULL: + if (ilen != 0) + return (EFAULT); + + /* Nothing to copy */ + NV_STORE_INIT_INLINE(); + return (0); + case BHND_NVRAM_TYPE_CHAR: NV_STORE_INLINE(uint8_t, ch); return (0); + case BHND_NVRAM_TYPE_BOOL: + NV_STORE_INLINE(bhnd_nvram_bool_t, b); + return(0); + case BHND_NVRAM_TYPE_UINT8: case BHND_NVRAM_TYPE_INT8: NV_STORE_INLINE(uint8_t, u8); return (0); case BHND_NVRAM_TYPE_UINT16: case BHND_NVRAM_TYPE_INT16: NV_STORE_INLINE(uint16_t, u16); return (0); case BHND_NVRAM_TYPE_UINT32: case BHND_NVRAM_TYPE_INT32: NV_STORE_INLINE(uint32_t, u32); return (0); case BHND_NVRAM_TYPE_UINT64: case BHND_NVRAM_TYPE_INT64: NV_STORE_INLINE(uint32_t, u32); return (0); case BHND_NVRAM_TYPE_CHAR_ARRAY: NV_COPY_ARRRAY_INLINE(uint8_t, ch); return (0); + case BHND_NVRAM_TYPE_DATA: case BHND_NVRAM_TYPE_UINT8_ARRAY: case BHND_NVRAM_TYPE_INT8_ARRAY: NV_COPY_ARRRAY_INLINE(uint8_t, u8); return (0); case BHND_NVRAM_TYPE_UINT16_ARRAY: case BHND_NVRAM_TYPE_INT16_ARRAY: NV_COPY_ARRRAY_INLINE(uint16_t, u16); return (0); case BHND_NVRAM_TYPE_UINT32_ARRAY: case BHND_NVRAM_TYPE_INT32_ARRAY: NV_COPY_ARRRAY_INLINE(uint32_t, u32); return (0); case BHND_NVRAM_TYPE_UINT64_ARRAY: case BHND_NVRAM_TYPE_INT64_ARRAY: NV_COPY_ARRRAY_INLINE(uint64_t, u64); return (0); + + case BHND_NVRAM_TYPE_BOOL_ARRAY: + NV_COPY_ARRRAY_INLINE(bhnd_nvram_bool_t, b); + return(0); case BHND_NVRAM_TYPE_STRING: case BHND_NVRAM_TYPE_STRING_ARRAY: if (ilen > sizeof(value->data.ch)) return (ENOMEM); if (inp != NULL) { memcpy(&value->data.ch, inp, ilen); NV_STORE_INIT_INLINE(); } return (0); } #undef NV_STORE_INIT_INLINE #undef NV_STORE_INLINE #undef NV_COPY_ARRRAY_INLINE BHND_NV_PANIC("unknown data type %d", itype); } /** * Initialize the internal representation of @p value with a buffer allocation * of @p len and @p itype, returning a pointer to the allocated buffer. * * If a buffer of @p len and @p itype can be represented inline, no * external buffer will be allocated, and instead a pointer to the inline * data representation will be returned. * * @param value The value to be initialized. * @param ilen The size of the external buffer to be allocated. * @param itype The type of the external buffer to be allocated. * @param flags Value flags. * * @retval non-null The newly allocated buffer. * @retval NULL If allocation failed. * @retval NULL If @p value is an externally allocated instance. */ static void * bhnd_nvram_val_alloc_bytes(bhnd_nvram_val *value, size_t ilen, bhnd_nvram_type itype, uint32_t flags) { void *ptr; BHND_NVRAM_VAL_ASSERT_EMPTY(value); /* Can we use inline storage? */ if (bhnd_nvram_val_set_inline(value, NULL, ilen, itype) == 0) { BHND_NV_ASSERT(sizeof(value->data) >= ilen, ("ilen exceeds inline storage")); value->data_type = itype; value->data_len = ilen; value->data_storage = BHND_NVRAM_VAL_DATA_INLINE; return (&value->data); } /* Is allocation permitted? */ if (!(flags & BHND_NVRAM_VAL_DYNAMIC)) return (NULL); /* Allocate external storage */ if ((ptr = bhnd_nv_malloc(ilen)) == NULL) return (NULL); value->data.ptr = ptr; value->data_len = ilen; value->data_type = itype; value->data_storage = BHND_NVRAM_VAL_DATA_EXT_ALLOC; return (ptr); } Index: head/sys/dev/bhnd/nvram/bhnd_nvram_value.h =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_value.h (revision 310292) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_value.h (revision 310293) @@ -1,274 +1,283 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _BHND_NVRAM_BHND_NVRAM_VALUE_H_ #define _BHND_NVRAM_BHND_NVRAM_VALUE_H_ #include #ifdef _KERNEL #include #else /* !_KERNEL */ #include #endif /* _KERNEL */ #include "bhnd_nvram.h" typedef struct bhnd_nvram_val_fmt bhnd_nvram_val_fmt; typedef struct bhnd_nvram_val bhnd_nvram_val; const char *bhnd_nvram_val_fmt_name( const bhnd_nvram_val_fmt *fmt); const bhnd_nvram_val_fmt *bhnd_nvram_val_default_fmt( bhnd_nvram_type type); int bhnd_nvram_val_init(bhnd_nvram_val *value, const bhnd_nvram_val_fmt *fmt, const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags); int bhnd_nvram_val_convert_init( bhnd_nvram_val *value, const bhnd_nvram_val_fmt *fmt, bhnd_nvram_val *src, uint32_t flags); int bhnd_nvram_val_new(bhnd_nvram_val **value, const bhnd_nvram_val_fmt *fmt, const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags); int bhnd_nvram_val_convert_new( bhnd_nvram_val **value, const bhnd_nvram_val_fmt *fmt, bhnd_nvram_val *src, uint32_t flags); bhnd_nvram_val *bhnd_nvram_val_copy(bhnd_nvram_val *value); void bhnd_nvram_val_release( bhnd_nvram_val *value); int bhnd_nvram_val_encode(bhnd_nvram_val *value, void *outp, size_t *olen, bhnd_nvram_type otype); int bhnd_nvram_val_encode_elem( bhnd_nvram_val *value, const void *inp, size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype); int bhnd_nvram_val_printf(bhnd_nvram_val *value, const char *fmt, char *outp, size_t *olen, ...); int bhnd_nvram_val_vprintf(bhnd_nvram_val *value, const char *fmt, char *outp, size_t *olen, va_list ap); const void *bhnd_nvram_val_bytes(bhnd_nvram_val *value, size_t *olen, bhnd_nvram_type *otype); bhnd_nvram_type bhnd_nvram_val_type(bhnd_nvram_val *value); bhnd_nvram_type bhnd_nvram_val_elem_type( bhnd_nvram_val *value); const void *bhnd_nvram_val_next(bhnd_nvram_val *value, const void *prev, size_t *olen); size_t bhnd_nvram_val_nelem(bhnd_nvram_val *value); /** * NVRAM value flags */ enum { /** * Do not allocate additional space for value data; all data must be * represented inline within the value structure (default). */ BHND_NVRAM_VAL_FIXED = (0<<0), /** * Automatically allocate additional space for value data if it cannot * be represented within the value structure. */ BHND_NVRAM_VAL_DYNAMIC = (1<<0), /** * Copy the value data upon initialization. (default). */ BHND_NVRAM_VAL_COPY_DATA = (0<<1), /** * Do not perform an initial copy of the value data; the data must * remain valid for the lifetime of the NVRAM value. * * Value data will still be copied if the value itself is copied to the * heap. */ BHND_NVRAM_VAL_BORROW_DATA = (1<<1), /** * Do not copy the value data when copying the value to the heap; the * vlaue data is assumed to be statically allocated and must remain * valid for the lifetime of the process. * * Implies BHND_NVRAM_VAL_BORROW_DATA. */ BHND_NVRAM_VAL_STATIC_DATA = (1<<2), }; /** * @internal * * NVRAM value storage types. */ typedef enum { /** * The value structure has an automatic storage duration * (e.g. it is stack allocated, or is otherwise externally managed), * and no destructors will be run prior to deallocation of the value. * * When performing copy/retain, the existing structure must be copied * to a new heap allocation. */ BHND_NVRAM_VAL_STORAGE_AUTO = 0, /** * The value structure was heap allocated and is fully managed by the * the NVRAM value API. * * When performing copy/retain, the existing structure may be retained * as-is. */ BHND_NVRAM_VAL_STORAGE_DYNAMIC = 2, /** * The value structure has a static storage duration, and will never * be deallocated. * * When performing copy/retain, the existing structure may be referenced * without modification. */ BHND_NVRAM_VAL_STORAGE_STATIC = 3, } bhnd_nvram_val_storage; /** * @internal * * NVRAM data storage types. */ typedef enum { /** Value has no active representation. This is the default for * zero-initialized value structures. */ BHND_NVRAM_VAL_DATA_NONE = 0, /** Value data is represented inline */ BHND_NVRAM_VAL_DATA_INLINE = 1, /** * Value represented by an external reference to data with a static * storage location. The data need not be copied if copying the value. */ BHND_NVRAM_VAL_DATA_EXT_STATIC = 2, /** * Value represented by weak external reference, which must be copied * if copying the value. */ BHND_NVRAM_VAL_DATA_EXT_WEAK = 3, /** * Value represented by an external reference that must be deallocated * when deallocating the value. */ BHND_NVRAM_VAL_DATA_EXT_ALLOC = 4, } bhnd_nvram_val_data_storage; /** * NVRAM value */ struct bhnd_nvram_val { volatile u_int refs; /**< reference count */ bhnd_nvram_val_storage val_storage; /**< value structure storage */ const bhnd_nvram_val_fmt *fmt; /**< value format */ bhnd_nvram_val_data_storage data_storage; /**< data storage */ bhnd_nvram_type data_type; /**< data type */ size_t data_len; /**< data size */ /** data representation */ union { uint8_t u8[8]; /**< 8-bit unsigned data */ uint16_t u16[4]; /**< 16-bit unsigned data */ uint32_t u32[2]; /**< 32-bit unsigned data */ uint32_t u64[1]; /**< 64-bit unsigned data */ int8_t i8[8]; /**< 8-bit signed data */ int16_t i16[4]; /**< 16-bit signed data */ int32_t i32[2]; /**< 32-bit signed data */ int64_t i64[1]; /**< 64-bit signed data */ unsigned char ch[8]; /**< 8-bit character data */ + bhnd_nvram_bool_t b[8]; /**< 8-bit boolean data */ const void *ptr; /**< external data */ } data; }; /** Declare a bhnd_nvram_val_fmt with name @p _n */ #define BHND_NVRAM_VAL_FMT_DECL(_n) \ extern const bhnd_nvram_val_fmt bhnd_nvram_val_ ## _n ## _fmt; BHND_NVRAM_VAL_FMT_DECL(bcm_decimal); BHND_NVRAM_VAL_FMT_DECL(bcm_hex); BHND_NVRAM_VAL_FMT_DECL(bcm_leddc); BHND_NVRAM_VAL_FMT_DECL(bcm_macaddr); BHND_NVRAM_VAL_FMT_DECL(bcm_string); BHND_NVRAM_VAL_FMT_DECL(uint8); BHND_NVRAM_VAL_FMT_DECL(uint16); BHND_NVRAM_VAL_FMT_DECL(uint32); BHND_NVRAM_VAL_FMT_DECL(uint64); BHND_NVRAM_VAL_FMT_DECL(int8); BHND_NVRAM_VAL_FMT_DECL(int16); BHND_NVRAM_VAL_FMT_DECL(int32); BHND_NVRAM_VAL_FMT_DECL(int64); BHND_NVRAM_VAL_FMT_DECL(char); +BHND_NVRAM_VAL_FMT_DECL(bool); BHND_NVRAM_VAL_FMT_DECL(string); +BHND_NVRAM_VAL_FMT_DECL(data); +BHND_NVRAM_VAL_FMT_DECL(null); BHND_NVRAM_VAL_FMT_DECL(uint8_array); BHND_NVRAM_VAL_FMT_DECL(uint16_array); BHND_NVRAM_VAL_FMT_DECL(uint32_array); BHND_NVRAM_VAL_FMT_DECL(uint64_array); BHND_NVRAM_VAL_FMT_DECL(int8_array); BHND_NVRAM_VAL_FMT_DECL(int16_array); BHND_NVRAM_VAL_FMT_DECL(int32_array); BHND_NVRAM_VAL_FMT_DECL(int64_array); BHND_NVRAM_VAL_FMT_DECL(char_array); +BHND_NVRAM_VAL_FMT_DECL(bool_array); BHND_NVRAM_VAL_FMT_DECL(string_array); + +/** Shared NULL value instance */ +#define BHND_NVRAM_VAL_NULL (&bhnd_nvram_val_null) +extern bhnd_nvram_val bhnd_nvram_val_null; #endif /* _BHND_NVRAM_BHND_NVRAM_VALUE_H_ */ Index: head/sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c (revision 310292) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c (revision 310293) @@ -1,1052 +1,1056 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #ifdef _KERNEL #include #include #include #include #include #else /* !_KERNEL */ #include #include #include #include #include #endif /* _KERNEL */ #include "bhnd_nvram_private.h" #include "bhnd_nvram_valuevar.h" static bool bhnd_nvram_ident_octet_string(const char *inp, size_t ilen, char *delim, size_t *nelem); static bool bhnd_nvram_ident_num_string(const char *inp, size_t ilen, u_int base, u_int *obase); static int bhnd_nvram_val_bcm_macaddr_filter( const bhnd_nvram_val_fmt **fmt, const void *inp, size_t ilen, bhnd_nvram_type itype); static int bhnd_nvram_val_bcm_macaddr_encode( bhnd_nvram_val *value, void *outp, size_t *olen, bhnd_nvram_type otype); static int bhnd_nvram_val_bcm_macaddr_string_filter( const bhnd_nvram_val_fmt **fmt, const void *inp, size_t ilen, bhnd_nvram_type itype); static int bhnd_nvram_val_bcm_macaddr_string_encode_elem( bhnd_nvram_val *value, const void *inp, size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype); static const void *bhnd_nvram_val_bcm_macaddr_string_next( bhnd_nvram_val *value, const void *prev, size_t *len); static int bhnd_nvram_val_bcm_int_filter( const bhnd_nvram_val_fmt **fmt, const void *inp, size_t ilen, bhnd_nvram_type itype); static int bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val *value, void *outp, size_t *olen, bhnd_nvram_type otype); static int bhnd_nvram_val_bcm_decimal_encode_elem( bhnd_nvram_val *value, const void *inp, size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype); static int bhnd_nvram_val_bcm_hex_encode_elem( bhnd_nvram_val *value, const void *inp, size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype); static int bhnd_nvram_val_bcm_leddc_filter( const bhnd_nvram_val_fmt **fmt, const void *inp, size_t ilen, bhnd_nvram_type itype); static int bhnd_nvram_val_bcm_leddc_encode_elem( bhnd_nvram_val *value, const void *inp, size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype); static int bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val *value, void *outp, size_t *olen, bhnd_nvram_type otype); static int bhnd_nvram_val_bcmstr_csv_filter( const bhnd_nvram_val_fmt **fmt, const void *inp, size_t ilen, bhnd_nvram_type itype); static const void *bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val *value, const void *prev, size_t *len); /** * Broadcom NVRAM MAC address format. */ const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_macaddr_fmt = { .name = "bcm-macaddr", .native_type = BHND_NVRAM_TYPE_UINT8_ARRAY, .op_filter = bhnd_nvram_val_bcm_macaddr_filter, .op_encode = bhnd_nvram_val_bcm_macaddr_encode, }; /** Broadcom NVRAM MAC address string format. */ static const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_macaddr_string_fmt = { .name = "bcm-macaddr-string", .native_type = BHND_NVRAM_TYPE_STRING, .op_filter = bhnd_nvram_val_bcm_macaddr_string_filter, .op_encode_elem = bhnd_nvram_val_bcm_macaddr_string_encode_elem, .op_next = bhnd_nvram_val_bcm_macaddr_string_next, }; /** * Broadcom NVRAM LED duty-cycle format. */ const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_leddc_fmt = { .name = "bcm-leddc", .native_type = BHND_NVRAM_TYPE_UINT32, .op_filter = bhnd_nvram_val_bcm_leddc_filter, .op_encode_elem = bhnd_nvram_val_bcm_leddc_encode_elem, }; /** * Broadcom NVRAM decimal integer format. * * Extends standard integer handling, encoding the string representation of * the integer value as a decimal string: * - Positive values will be string-encoded without a prefix. * - Negative values will be string-encoded with a leading '-' sign. */ const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_decimal_fmt = { .name = "bcm-decimal", .native_type = BHND_NVRAM_TYPE_UINT64, .op_filter = bhnd_nvram_val_bcm_int_filter, .op_encode = bhnd_nvram_val_bcm_int_encode, .op_encode_elem = bhnd_nvram_val_bcm_decimal_encode_elem, }; /** * Broadcom NVRAM decimal integer format. * * Extends standard integer handling, encoding the string representation of * unsigned and positive signed integer values as an 0x-prefixed hexadecimal * string. * * For compatibility with standard Broadcom NVRAM parsing, if the integer is * both signed and negative, it will be string encoded as a negative decimal * value, not as a twos-complement hexadecimal value. */ const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_hex_fmt = { .name = "bcm-hex", .native_type = BHND_NVRAM_TYPE_UINT64, .op_filter = bhnd_nvram_val_bcm_int_filter, .op_encode = bhnd_nvram_val_bcm_int_encode, .op_encode_elem = bhnd_nvram_val_bcm_hex_encode_elem, }; /** * Broadcom NVRAM string format. * * Handles standard, comma-delimited, and octet-string values as used in * Broadcom NVRAM data. */ const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_string_fmt = { .name = "bcm-string", .native_type = BHND_NVRAM_TYPE_STRING, .op_encode = bhnd_nvram_val_bcmstr_encode, }; /** Broadcom comma-delimited string. */ static const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_string_csv_fmt = { .name = "bcm-string[]", .native_type = BHND_NVRAM_TYPE_STRING, .op_filter = bhnd_nvram_val_bcmstr_csv_filter, .op_next = bhnd_nvram_val_bcmstr_csv_next, }; /* Built-in format definitions */ #define BHND_NVRAM_VAL_FMT_NATIVE(_n, _type) \ const bhnd_nvram_val_fmt bhnd_nvram_val_ ## _n ## _fmt = { \ .name = __STRING(_n), \ .native_type = BHND_NVRAM_TYPE_ ## _type, \ } BHND_NVRAM_VAL_FMT_NATIVE(uint8, UINT8); BHND_NVRAM_VAL_FMT_NATIVE(uint16, UINT16); BHND_NVRAM_VAL_FMT_NATIVE(uint32, UINT32); BHND_NVRAM_VAL_FMT_NATIVE(uint64, UINT64); BHND_NVRAM_VAL_FMT_NATIVE(int8, INT8); BHND_NVRAM_VAL_FMT_NATIVE(int16, INT16); BHND_NVRAM_VAL_FMT_NATIVE(int32, INT32); BHND_NVRAM_VAL_FMT_NATIVE(int64, INT64); BHND_NVRAM_VAL_FMT_NATIVE(char, CHAR); +BHND_NVRAM_VAL_FMT_NATIVE(bool, BOOL); BHND_NVRAM_VAL_FMT_NATIVE(string, STRING); +BHND_NVRAM_VAL_FMT_NATIVE(data, DATA); +BHND_NVRAM_VAL_FMT_NATIVE(null, NULL); BHND_NVRAM_VAL_FMT_NATIVE(uint8_array, UINT8_ARRAY); BHND_NVRAM_VAL_FMT_NATIVE(uint16_array, UINT16_ARRAY); BHND_NVRAM_VAL_FMT_NATIVE(uint32_array, UINT32_ARRAY); BHND_NVRAM_VAL_FMT_NATIVE(uint64_array, UINT64_ARRAY); BHND_NVRAM_VAL_FMT_NATIVE(int8_array, INT8_ARRAY); BHND_NVRAM_VAL_FMT_NATIVE(int16_array, INT16_ARRAY); BHND_NVRAM_VAL_FMT_NATIVE(int32_array, INT32_ARRAY); BHND_NVRAM_VAL_FMT_NATIVE(int64_array, INT64_ARRAY); BHND_NVRAM_VAL_FMT_NATIVE(char_array, CHAR_ARRAY); +BHND_NVRAM_VAL_FMT_NATIVE(bool_array, BOOL_ARRAY); BHND_NVRAM_VAL_FMT_NATIVE(string_array, STRING_ARRAY); /** * Common hex/decimal integer filter implementation. */ static int bhnd_nvram_val_bcm_int_filter(const bhnd_nvram_val_fmt **fmt, const void *inp, size_t ilen, bhnd_nvram_type itype) { bhnd_nvram_type itype_base; itype_base = bhnd_nvram_base_type(itype); switch (itype_base) { case BHND_NVRAM_TYPE_STRING: /* * If the input is a string, delegate to the Broadcom * string format -- preserving the original string value * takes priority over enforcing hexadecimal/integer string * formatting. */ *fmt = &bhnd_nvram_val_bcm_string_fmt; return (0); default: if (bhnd_nvram_is_int_type(itype_base)) return (0); return (EFTYPE); } } /** * Broadcom hex/decimal integer encode implementation. */ static int bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val *value, void *outp, size_t *olen, bhnd_nvram_type otype) { /* If encoding to a string, format multiple elements (if any) with a * comma delimiter. */ if (otype == BHND_NVRAM_TYPE_STRING) return (bhnd_nvram_val_printf(value, "%[]s", outp, olen, ",")); return (bhnd_nvram_val_generic_encode(value, outp, olen, otype)); } /** * Broadcom hex integer encode_elem implementation. */ static int bhnd_nvram_val_bcm_hex_encode_elem(bhnd_nvram_val *value, const void *inp, size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) { bhnd_nvram_type itype; ssize_t width; int error; itype = bhnd_nvram_val_elem_type(value); BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type")); /* If not encoding as a string, perform generic value encoding */ if (otype != BHND_NVRAM_TYPE_STRING) return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen, outp, olen, otype)); /* If the value is a signed, negative value, encode as a decimal * string */ if (bhnd_nvram_is_signed_type(itype)) { int64_t sval; size_t slen; bhnd_nvram_type stype; stype = BHND_NVRAM_TYPE_INT64; slen = sizeof(sval); /* Fetch 64-bit signed representation */ error = bhnd_nvram_value_coerce(inp, ilen, itype, &sval, &slen, stype); if (error) return (error); /* Decimal encoding required? */ if (sval < 0) return (bhnd_nvram_value_printf("%I64d", &sval, slen, stype, outp, olen, otype)); } /* * Encode the value as a hex string. * * Most producers of Broadcom NVRAM values zero-pad hex values out to * their native width (width * two hex characters), and we do the same * for compatibility */ width = bhnd_nvram_type_width(itype) * 2; return (bhnd_nvram_value_printf("0x%0*I64X", inp, ilen, itype, outp, olen, width)); } /** * Broadcom decimal integer encode_elem implementation. */ static int bhnd_nvram_val_bcm_decimal_encode_elem(bhnd_nvram_val *value, const void *inp, size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) { const char *sfmt; bhnd_nvram_type itype; itype = bhnd_nvram_val_elem_type(value); BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type")); /* If not encoding as a string, perform generic value encoding */ if (otype != BHND_NVRAM_TYPE_STRING) return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen, outp, olen, otype)); sfmt = bhnd_nvram_is_signed_type(itype) ? "%I64d" : "%I64u"; return (bhnd_nvram_value_printf(sfmt, inp, ilen, itype, outp, olen)); } /** * Broadcom LED duty-cycle filter. */ static int bhnd_nvram_val_bcm_leddc_filter(const bhnd_nvram_val_fmt **fmt, const void *inp, size_t ilen, bhnd_nvram_type itype) { const char *p; size_t plen; switch (itype) { case BHND_NVRAM_TYPE_UINT16: case BHND_NVRAM_TYPE_UINT32: return (0); case BHND_NVRAM_TYPE_STRING: /* Trim any whitespace */ p = inp; plen = bhnd_nvram_trim_field(&p, ilen, '\0'); /* If the value is not a valid integer string, delegate to the * Broadcom string format */ if (!bhnd_nvram_ident_num_string(p, plen, 0, NULL)) *fmt = &bhnd_nvram_val_bcm_string_fmt; return (0); default: return (EFTYPE); } } /** * Broadcom LED duty-cycle encode. */ static int bhnd_nvram_val_bcm_leddc_encode_elem(bhnd_nvram_val *value, const void *inp, size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) { bhnd_nvram_type itype; size_t limit, nbytes; int error; uint16_t led16; uint32_t led32; bool led16_lossy; union { uint16_t u16; uint32_t u32; } strval; /* * LED duty-cycle values represent the on/off periods as a 32-bit * integer, with the top 16 bits representing on cycles, and the * bottom 16 representing off cycles. * * LED duty cycle values have three different formats: * * - SPROM: A 16-bit unsigned integer, with on/off cycles encoded * as 8-bit values. * - NVRAM: A 16-bit decimal or hexadecimal string, with on/off * cycles encoded as 8-bit values as per the SPROM format. * - NVRAM: A 32-bit decimal or hexadecimal string, with on/off * cycles encoded as 16-bit values. * * To convert from a 16-bit representation to a 32-bit representation: * ((value & 0xFF00) << 16) | ((value & 0x00FF) << 8) * * To convert from a 32-bit representation to a 16-bit representation, * perform the same operation in reverse, discarding the lower 8-bits * of each half of the 32-bit representation: * ((value >> 16) & 0xFF00) | ((value >> 8) & 0x00FF) */ itype = bhnd_nvram_val_elem_type(value); nbytes = 0; led16_lossy = false; /* Determine output byte limit */ if (outp != NULL) limit = *olen; else limit = 0; /* If the input/output types match, just delegate to standard value * encoding support */ if (otype == itype) { return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen, otype)); } /* If our value is a string, it may either be a 16-bit or a 32-bit * representation of the duty cycle */ if (itype == BHND_NVRAM_TYPE_STRING) { const char *p; uint32_t ival; size_t nlen, parsed; /* Parse integer value */ p = inp; nlen = sizeof(ival); error = bhnd_nvram_parse_int(p, ilen, 0, &parsed, &ival, &nlen, BHND_NVRAM_TYPE_UINT32); if (error) return (error); /* Trailing garbage? */ if (parsed < ilen && *(p+parsed) != '\0') return (EFTYPE); /* Point inp and itype to either our parsed 32-bit or 16-bit * value */ inp = &strval; if (ival & 0xFFFF0000) { strval.u32 = ival; itype = BHND_NVRAM_TYPE_UINT32; } else { strval.u16 = ival; itype = BHND_NVRAM_TYPE_UINT16; } } /* Populate both u32 and (possibly lossy) u16 LEDDC representations */ switch (itype) { case BHND_NVRAM_TYPE_UINT16: { led16 = *(const uint16_t *)inp; led32 = ((led16 & 0xFF00) << 16) | ((led16 & 0x00FF) << 8); /* If all bits are set in the 16-bit value (indicating that * the value is 'unset' in SPROM), we must update the 32-bit * representation to match. */ if (led16 == UINT16_MAX) led32 = UINT32_MAX; break; } case BHND_NVRAM_TYPE_UINT32: led32 = *(const uint32_t *)inp; led16 = ((led32 >> 16) & 0xFF00) | ((led32 >> 8) & 0x00FF); /* * Determine whether the led16 conversion is lossy: * * - If the lower 8 bits of each half of the 32-bit value * aren't set, we can safely use the 16-bit representation * without losing data. * - If all bits in the 32-bit value are set, the variable is * treated as unset in SPROM. We can safely use the 16-bit * representation without losing data. */ if ((led32 & 0x00FF00FF) != 0 && led32 != UINT32_MAX) led16_lossy = true; break; default: BHND_NV_PANIC("unsupported backing data type: %s", bhnd_nvram_type_name(itype)); } /* * Encode as requested output type. */ switch (otype) { case BHND_NVRAM_TYPE_STRING: /* * Prefer 16-bit format. */ if (!led16_lossy) { return (bhnd_nvram_value_printf("0x%04hX", &led16, sizeof(led16), BHND_NVRAM_TYPE_UINT16, outp, olen)); } else { return (bhnd_nvram_value_printf("0x%04X", &led32, sizeof(led32), BHND_NVRAM_TYPE_UINT32, outp, olen)); } break; case BHND_NVRAM_TYPE_UINT16: { /* Can we encode as uint16 without losing data? */ if (led16_lossy) return (ERANGE); /* Write led16 format */ nbytes += sizeof(uint16_t); if (limit >= nbytes) *(uint16_t *)outp = led16; break; } case BHND_NVRAM_TYPE_UINT32: /* Write led32 format */ nbytes += sizeof(uint32_t); if (limit >= nbytes) *(uint32_t *)outp = led32; break; default: /* No other output formats are supported */ return (EFTYPE); } /* Provide the actual length */ *olen = nbytes; /* Report insufficient space (if output was requested) */ if (limit < nbytes && outp != NULL) return (ENOMEM); return (0); } /** * Broadcom NVRAM string encoding. */ static int bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val *value, void *outp, size_t *olen, bhnd_nvram_type otype) { bhnd_nvram_val array; const bhnd_nvram_val_fmt *array_fmt; const void *inp; bhnd_nvram_type itype; size_t ilen; int error; inp = bhnd_nvram_val_bytes(value, &ilen, &itype); /* If the output is not an array type (or if it's a character array), * we can fall back on standard string encoding */ if (!bhnd_nvram_is_array_type(otype) || otype == BHND_NVRAM_TYPE_CHAR_ARRAY) { return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen, otype)); } /* Otherwise, we need to interpret our value as either a macaddr * string, or a comma-delimited string. */ inp = bhnd_nvram_val_bytes(value, &ilen, &itype); if (bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL)) array_fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt; else array_fmt = &bhnd_nvram_val_bcm_string_csv_fmt; /* Wrap in array-typed representation */ error = bhnd_nvram_val_init(&array, array_fmt, inp, ilen, itype, BHND_NVRAM_VAL_BORROW_DATA); if (error) { BHND_NV_LOG("error initializing array representation: %d\n", error); return (error); } /* Ask the array-typed value to perform the encode */ error = bhnd_nvram_val_encode(&array, outp, olen, otype); if (error) BHND_NV_LOG("error encoding array representation: %d\n", error); bhnd_nvram_val_release(&array); return (error); } /** * Broadcom NVRAM comma-delimited string filter. */ static int bhnd_nvram_val_bcmstr_csv_filter(const bhnd_nvram_val_fmt **fmt, const void *inp, size_t ilen, bhnd_nvram_type itype) { switch (itype) { case BHND_NVRAM_TYPE_STRING: case BHND_NVRAM_TYPE_STRING_ARRAY: return (0); default: return (EFTYPE); } } /** * Broadcom NVRAM comma-delimited string iteration. */ static const void * bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val *value, const void *prev, size_t *len) { const char *next; const char *inp; bhnd_nvram_type itype; size_t ilen, remain; char delim; /* Fetch backing representation */ inp = bhnd_nvram_val_bytes(value, &ilen, &itype); /* Fetch next value */ switch (itype) { case BHND_NVRAM_TYPE_STRING: /* Zero-length array? */ if (ilen == 0) return (NULL); if (prev == NULL) { /* First element */ next = inp; remain = ilen; delim = ','; } else { /* Advance to the previous element's delimiter */ next = (const char *)prev + *len; /* Did we hit the end of the string? */ if ((size_t)(next - inp) >= ilen) return (NULL); /* Fetch (and skip past) the delimiter */ delim = *next; next++; remain = ilen - (size_t)(next - inp); /* Was the delimiter the final character? */ if (remain == 0) return (NULL); } /* Parse the field value, up to the next delimiter */ *len = bhnd_nvram_parse_field(&next, remain, delim); return (next); case BHND_NVRAM_TYPE_STRING_ARRAY: /* Delegate to default array iteration */ return (bhnd_nvram_value_array_next(inp, ilen, itype, prev, len)); default: BHND_NV_PANIC("unsupported type: %d", itype); } } /** * MAC address filter. */ static int bhnd_nvram_val_bcm_macaddr_filter(const bhnd_nvram_val_fmt **fmt, const void *inp, size_t ilen, bhnd_nvram_type itype) { switch (itype) { case BHND_NVRAM_TYPE_UINT8_ARRAY: return (0); case BHND_NVRAM_TYPE_STRING: /* Let bcm_macaddr_string format handle it */ *fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt; return (0); default: return (EFTYPE); } } /** * MAC address encoding. */ static int bhnd_nvram_val_bcm_macaddr_encode(bhnd_nvram_val *value, void *outp, size_t *olen, bhnd_nvram_type otype) { const void *inp; bhnd_nvram_type itype; size_t ilen; /* * If converting to a string (or a single-element string array), * produce an octet string (00:00:...). */ if (bhnd_nvram_base_type(otype) == BHND_NVRAM_TYPE_STRING) { return (bhnd_nvram_val_printf(value, "%[]02hhX", outp, olen, ":")); } /* Otherwise, use standard encoding support */ inp = bhnd_nvram_val_bytes(value, &ilen, &itype); return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen, otype));} /** * MAC address string filter. */ static int bhnd_nvram_val_bcm_macaddr_string_filter(const bhnd_nvram_val_fmt **fmt, const void *inp, size_t ilen, bhnd_nvram_type itype) { switch (itype) { case BHND_NVRAM_TYPE_STRING: /* Use the standard Broadcom string format implementation if * the input is not an octet string. */ if (!bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL)) *fmt = &bhnd_nvram_val_bcm_string_fmt; return (0); default: return (EFTYPE); } } /** * MAC address string octet encoding. */ static int bhnd_nvram_val_bcm_macaddr_string_encode_elem(bhnd_nvram_val *value, const void *inp, size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) { size_t nparsed; int error; /* If integer encoding is requested, explicitly parse our * non-0x-prefixed as a base 16 integer value */ if (bhnd_nvram_is_int_type(otype)) { error = bhnd_nvram_parse_int(inp, ilen, 16, &nparsed, outp, olen, otype); if (error) return (error); if (nparsed != ilen) return (EFTYPE); return (0); } /* Otherwise, use standard encoding support */ return (bhnd_nvram_value_coerce(inp, ilen, bhnd_nvram_val_elem_type(value), outp, olen, otype)); } /** * MAC address string octet iteration. */ static const void * bhnd_nvram_val_bcm_macaddr_string_next(bhnd_nvram_val *value, const void *prev, size_t *len) { const char *next; const char *str; bhnd_nvram_type stype; size_t slen, remain; char delim; /* Fetch backing string */ str = bhnd_nvram_val_bytes(value, &slen, &stype); BHND_NV_ASSERT(stype == BHND_NVRAM_TYPE_STRING, ("unsupported type: %d", stype)); /* Zero-length array? */ if (slen == 0) return (NULL); if (prev == NULL) { /* First element */ /* Determine delimiter */ if (!bhnd_nvram_ident_octet_string(str, slen, &delim, NULL)) { /* Default to comma-delimited parsing */ delim = ','; } /* Parsing will start at the base string pointer */ next = str; remain = slen; } else { /* Advance to the previous element's delimiter */ next = (const char *)prev + *len; /* Did we hit the end of the string? */ if ((size_t)(next - str) >= slen) return (NULL); /* Fetch (and skip past) the delimiter */ delim = *next; next++; remain = slen - (size_t)(next - str); /* Was the delimiter the final character? */ if (remain == 0) return (NULL); } /* Parse the field value, up to the next delimiter */ *len = bhnd_nvram_parse_field(&next, remain, delim); return (next); } /** * Determine whether @p inp is in octet string format, consisting of a * fields of two hex characters, separated with ':' or '-' delimiters. * * This may be used to identify MAC address octet strings * (BHND_NVRAM_SFMT_MACADDR). * * @param inp The string to be parsed. * @param ilen The length of @p inp, in bytes. * @param[out] delim On success, the delimiter used by this octet * string. May be set to NULL if the field * delimiter is not desired. * @param[out] nelem On success, the number of fields in this * octet string. May be set to NULL if the field * count is not desired. * * * @retval true if @p inp is a valid octet string * @retval false if @p inp is not a valid octet string. */ static bool bhnd_nvram_ident_octet_string(const char *inp, size_t ilen, char *delim, size_t *nelem) { size_t elem_count; size_t max_elem_count, min_elem_count; size_t field_count; char idelim; field_count = 0; /* Require exactly two digits. If we relax this, there is room * for ambiguity with signed integers and the '-' delimiter */ min_elem_count = 2; max_elem_count = 2; /* Identify the delimiter used. The standard delimiter for MAC * addresses is ':', but some earlier NVRAM formats may use '-' */ for (const char *d = ":-";; d++) { const char *loc; /* No delimiter found, not an octet string */ if (*d == '\0') return (false); /* Look for the delimiter */ if ((loc = memchr(inp, *d, ilen)) == NULL) continue; /* Delimiter found */ idelim = *loc; break; } /* To disambiguate from signed integers, if the delimiter is "-", * the octets must be exactly 2 chars each */ if (idelim == '-') min_elem_count = 2; /* String must be composed of individual octets (zero or more hex * digits) separated by our delimiter. */ elem_count = 0; for (const char *p = inp; (size_t)(p - inp) < ilen; p++) { switch (*p) { case ':': case '-': case '\0': /* Hit a delim character; all delims must match * the first delimiter used */ if (*p != '\0' && *p != idelim) return (false); /* Must have parsed at least min_elem_count digits */ if (elem_count < min_elem_count) return (false); /* Reset element count */ elem_count = 0; /* Bump field count */ field_count++; break; default: /* More than maximum number of hex digits? */ if (elem_count >= max_elem_count) return (false); /* Octet values must be hex digits */ if (!bhnd_nv_isxdigit(*p)) return (false); elem_count++; break; } } if (delim != NULL) *delim = idelim; if (nelem != NULL) *nelem = field_count; return (true); } /** * Determine whether @p inp is in hexadecimal, octal, or decimal string * format. * * - A @p str may be prefixed with a single optional '+' or '-' sign denoting * signedness. * - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a * base 16 integer follows. * - An octal @p str may include a '0' prefix, denoting that an octal integer * follows. * * @param inp The string to be parsed. * @param ilen The length of @p inp, in bytes. * @param base The input string's base (2-36), or 0. * @param[out] obase On success, will be set to the base of the parsed * integer. May be set to NULL if the base is not * desired. * * @retval true if @p inp is a valid number string * @retval false if @p inp is not a valid number string. * @retval false if @p base is invalid. */ static bool bhnd_nvram_ident_num_string(const char *inp, size_t ilen, u_int base, u_int *obase) { size_t nbytes, ndigits; nbytes = 0; ndigits = 0; /* Parse and skip sign */ if (nbytes >= ilen) return (false); if (inp[nbytes] == '-' || inp[nbytes] == '+') nbytes++; /* Truncated after sign character? */ if (nbytes == ilen) return (false); /* Identify (or validate) hex base, skipping 0x/0X prefix */ if (base == 16 || base == 0) { /* Check for (and skip) 0x/0X prefix */ if (ilen - nbytes >= 2 && inp[nbytes] == '0' && (inp[nbytes+1] == 'x' || inp[nbytes+1] == 'X')) { base = 16; nbytes += 2; } } /* Truncated after hex prefix? */ if (nbytes == ilen) return (false); /* Differentiate decimal/octal by looking for a leading 0 */ if (base == 0) { if (inp[nbytes] == '0') { base = 8; } else { base = 10; } } /* Consume and validate all remaining digit characters */ for (; nbytes < ilen; nbytes++) { u_int carry; char c; /* Parse carry value */ c = inp[nbytes]; if (bhnd_nv_isdigit(c)) { carry = c - '0'; } else if (bhnd_nv_isxdigit(c)) { if (bhnd_nv_isupper(c)) carry = (c - 'A') + 10; else carry = (c - 'a') + 10; } else { /* Hit a non-digit character */ return (false); } /* If carry is outside the base, it's not a valid digit * in the current parse context; consider it a non-digit * character */ if (carry >= base) return (false); /* Increment parsed digit count */ ndigits++; } /* Empty integer string? */ if (ndigits == 0) return (false); /* Valid integer -- provide the base and return */ if (obase != NULL) *obase = base; return (true); } Index: head/sys/dev/bhnd/nvram/bhnd_nvram_value_subr.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_value_subr.c (revision 310292) +++ head/sys/dev/bhnd/nvram/bhnd_nvram_value_subr.c (revision 310293) @@ -1,513 +1,551 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #ifdef _KERNEL #include #else /* !_KERNEL */ #include #include #endif /* _KERNEL */ #include "bhnd_nvram_private.h" #include "bhnd_nvram_valuevar.h" /** * Validate the alignment of a value of @p type. * * @param inp The value data. * @param ilen The value length, in bytes. * @param itype The value type. * * @retval 0 success * @retval EFTYPE if @p type is not an array type, and @p len is not * equal to the size of a single element of @p type. * @retval EFAULT if @p data is not correctly aligned to the required * host alignment. * @retval EFAULT if @p len is not aligned to the @p type width. */ int bhnd_nvram_value_check_aligned(const void *inp, size_t ilen, bhnd_nvram_type itype) { size_t align, width; + /* As a special case, NULL values have no alignment, but must + * always have a length of zero */ + if (itype == BHND_NVRAM_TYPE_NULL) { + if (ilen != 0) + return (EFAULT); + + return (0); + } + /* Check pointer alignment against the required host alignment */ align = bhnd_nvram_type_host_align(itype); BHND_NV_ASSERT(align != 0, ("invalid zero alignment")); if ((uintptr_t)inp % align != 0) return (EFAULT); /* If type is not fixed width, nothing else to check */ width = bhnd_nvram_type_width(itype); if (width == 0) return (0); /* Length must be aligned to the element width */ if (ilen % width != 0) return (EFAULT); /* If the type is not an array type, the length must be equal to the * size of a single element of @p type. */ if (!bhnd_nvram_is_array_type(itype) && ilen != width) return (EFTYPE); return (0); } /** * Calculate the number of elements represented by a value of @p ilen bytes * with @p itype. * * @param inp The value data. * @param ilen The value length. * @param itype The value type. * @param[out] nelem On success, the number of elements. * * @retval 0 success * @retval EINVAL if @p inp is NULL and the element count of @p itype * cannot be determined without parsing the value data. * @retval EFTYPE if @p itype is not an array type, and @p ilen is not * equal to the size of a single element of @p itype. * @retval EFAULT if @p ilen is not correctly aligned for elements of * @p itype. */ int bhnd_nvram_value_nelem(const void *inp, size_t ilen, bhnd_nvram_type itype, size_t *nelem) { int error; BHND_NV_ASSERT(inp != NULL, ("NULL inp")); /* Check alignment */ if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype))) return (error); switch (itype) { + case BHND_NVRAM_TYPE_DATA: + /* Always exactly one element */ + *nelem = 1; + return (0); + + case BHND_NVRAM_TYPE_NULL: + /* Must be zero length */ + if (ilen != 0) + return (EFAULT); + + /* Always exactly one element */ + *nelem = 1; + return (0); + case BHND_NVRAM_TYPE_STRING: /* Always exactly one element */ *nelem = 1; return (0); case BHND_NVRAM_TYPE_STRING_ARRAY: { const char *p; size_t nleft; /* Iterate over the NUL-terminated strings to calculate * total element count */ p = inp; nleft = ilen; *nelem = 0; while (nleft > 0) { size_t slen; /* Increment element count */ (*nelem)++; /* Determine string length */ slen = strnlen(p, nleft); nleft -= slen; /* Advance input */ p += slen; /* Account for trailing NUL, if we haven't hit the end * of the input */ if (nleft > 0) { nleft--; p++; } } return (0); } 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_CHAR_ARRAY: + case BHND_NVRAM_TYPE_BOOL_ARRAY: { size_t width = bhnd_nvram_type_width(itype); BHND_NV_ASSERT(width != 0, ("invalid width")); *nelem = ilen / width; return (0); } case BHND_NVRAM_TYPE_INT8: case BHND_NVRAM_TYPE_UINT8: case BHND_NVRAM_TYPE_CHAR: case BHND_NVRAM_TYPE_INT16: case BHND_NVRAM_TYPE_UINT16: case BHND_NVRAM_TYPE_INT32: case BHND_NVRAM_TYPE_UINT32: case BHND_NVRAM_TYPE_INT64: case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_BOOL: /* Length must be equal to the size of exactly one * element (arrays can represent zero elements -- non-array * types cannot) */ if (ilen != bhnd_nvram_type_width(itype)) return (EFTYPE); *nelem = 1; return (0); } /* Quiesce gcc4.2 */ BHND_NV_PANIC("bhnd nvram type %u unknown", itype); } /** * Return the size, in bytes, of a value of @p itype with @p nelem elements. * * @param inp The actual data to be queried, or NULL if unknown. If * NULL and the base type is not a fixed width type * (e.g. BHND_NVRAM_TYPE_STRING), 0 will be returned. * @param ilen The size of @p inp, in bytes, or 0 if @p inp is NULL. * @param itype The value type. * @param nelem The number of elements. If @p itype is not an array * type, this value must be 1. * * @retval 0 If @p itype has a variable width, and @p inp is NULL. * @retval 0 If a @p nelem value greater than 1 is provided for a * non-array @p itype. * @retval 0 If a @p nelem value of 0 is provided. * @retval 0 If the result would exceed the maximum value * representable by size_t. * @retval 0 If @p itype is BHND_NVRAM_TYPE_NULL. * @retval non-zero The size, in bytes, of @p itype with @p nelem elements. */ size_t bhnd_nvram_value_size(const void *inp, size_t ilen, bhnd_nvram_type itype, size_t nelem) { /* If nelem 0, nothing to do */ if (nelem == 0) return (0); /* Non-array types must have an nelem value of 1 */ if (!bhnd_nvram_is_array_type(itype) && nelem != 1) return (0); switch (itype) { 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_CHAR_ARRAY: + case BHND_NVRAM_TYPE_BOOL_ARRAY:{ size_t width; width = bhnd_nvram_type_width(itype); /* Would nelem * width overflow? */ if (SIZE_MAX / nelem < width) { BHND_NV_LOG("cannot represent size %s[%zu]\n", bhnd_nvram_type_name(bhnd_nvram_base_type(itype)), nelem); return (0); } return (nelem * width); } case BHND_NVRAM_TYPE_STRING_ARRAY: { const char *p; size_t total_size; if (inp == NULL) return (0); /* Iterate over the NUL-terminated strings to calculate * total byte length */ p = inp; total_size = 0; for (size_t i = 0; i < nelem; i++) { size_t elem_size; elem_size = strnlen(p, ilen - total_size); p += elem_size; /* Check for (and skip) terminating NUL */ if (total_size < ilen && *p == '\0') { elem_size++; p++; } /* Would total_size + elem_size overflow? * * A memory range larger than SIZE_MAX shouldn't be, * possible, but include the check for completeness */ if (SIZE_MAX - total_size < elem_size) return (0); total_size += elem_size; } return (total_size); } case BHND_NVRAM_TYPE_STRING: { size_t size; if (inp == NULL) return (0); /* Find length */ size = strnlen(inp, ilen); /* Is there a terminating NUL, or did we just hit the * end of the string input */ if (size < ilen) size++; return (size); } + + case BHND_NVRAM_TYPE_NULL: + return (0); + + case BHND_NVRAM_TYPE_DATA: + if (inp == NULL) + return (0); + + return (ilen); + + case BHND_NVRAM_TYPE_BOOL: + return (sizeof(bhnd_nvram_bool_t)); case BHND_NVRAM_TYPE_INT8: case BHND_NVRAM_TYPE_UINT8: case BHND_NVRAM_TYPE_CHAR: return (sizeof(uint8_t)); case BHND_NVRAM_TYPE_INT16: case BHND_NVRAM_TYPE_UINT16: return (sizeof(uint16_t)); case BHND_NVRAM_TYPE_INT32: case BHND_NVRAM_TYPE_UINT32: return (sizeof(uint32_t)); case BHND_NVRAM_TYPE_UINT64: case BHND_NVRAM_TYPE_INT64: return (sizeof(uint64_t)); } /* Quiesce gcc4.2 */ BHND_NV_PANIC("bhnd nvram type %u unknown", itype); } /** * Format a string representation of @p inp using @p fmt, with, writing the * result to @p outp. * * Refer to bhnd_nvram_val_vprintf() for full format string documentation. * * @param fmt The format string. * @param inp The value to be formatted. * @param ilen The size of @p inp, in bytes. * @param itype The type of @p inp. * @param[out] outp On success, the string 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 formatted string. * * @retval 0 success * @retval EINVAL If @p fmt contains unrecognized format string * specifiers. * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen * is too small to hold the encoded value. * @retval EFTYPE If value coercion from @p inp to a string value via * @p fmt is unsupported. * @retval ERANGE If value coercion of @p value would overflow (or * underflow) the representation defined by @p fmt. */ int bhnd_nvram_value_printf(const char *fmt, const void *inp, size_t ilen, bhnd_nvram_type itype, char *outp, size_t *olen, ...) { va_list ap; int error; va_start(ap, olen); error = bhnd_nvram_value_vprintf(fmt, inp, ilen, itype, outp, olen, ap); va_end(ap); return (error); } /** * Format a string representation of @p inp using @p fmt, with, writing the * result to @p outp. * * Refer to bhnd_nvram_val_vprintf() for full format string documentation. * * @param fmt The format string. * @param inp The value to be formatted. * @param ilen The size of @p inp, in bytes. * @param itype The type of @p inp. * @param[out] outp On success, the string 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 formatted string. * @param ap Argument list. * * @retval 0 success * @retval EINVAL If @p fmt contains unrecognized format string * specifiers. * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen * is too small to hold the encoded value. * @retval EFTYPE If value coercion from @p inp to a string value via * @p fmt is unsupported. * @retval ERANGE If value coercion of @p value would overflow (or * underflow) the representation defined by @p fmt. */ int bhnd_nvram_value_vprintf(const char *fmt, const void *inp, size_t ilen, bhnd_nvram_type itype, char *outp, size_t *olen, va_list ap) { bhnd_nvram_val val; int error; /* Map input buffer as a value instance */ error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype, BHND_NVRAM_VAL_BORROW_DATA); if (error) return (error); /* Attempt to format the value */ error = bhnd_nvram_val_vprintf(&val, fmt, outp, olen, ap); /* Clean up */ bhnd_nvram_val_release(&val); return (error); } /** * Iterate over all elements in @p inp. * * @param inp The value to be iterated. * @param ilen The size, in bytes, of @p inp. * @param itype The data type of @p inp. * @param prev The value previously returned by * bhnd_nvram_value_array_next(), or NULL to begin * iteration. * @param[in,out] olen If @p prev is non-NULL, @p olen must be a * pointer to the length previously returned by * bhnd_nvram_value_array_next(). On success, will * be set to the next element's length, in bytes. * * @retval non-NULL A borrowed reference to the next element of @p inp. * @retval NULL If the end of the array is reached. */ const void * bhnd_nvram_value_array_next(const void *inp, size_t ilen, bhnd_nvram_type itype, const void *prev, size_t *olen) { const u_char *next; size_t offset; /* Handle first element */ if (prev == NULL) { /* Zero-length array? */ if (ilen == 0) return (NULL); *olen = bhnd_nvram_value_size(inp, ilen, itype, 1); return (inp); } /* Advance to next element */ BHND_NV_ASSERT(prev >= (const void *)inp, ("invalid cookiep")); next = (const u_char *)prev + *olen; offset = (size_t)(next - (const u_char *)inp); if (offset >= ilen) { /* Hit end of the array */ return (NULL); } /* Determine element size */ *olen = bhnd_nvram_value_size(next, ilen - offset, itype, 1); if (ilen - offset < *olen) { BHND_NV_LOG("short element of type %s -- misaligned " "representation", bhnd_nvram_type_name(itype)); return (NULL); } return (next); } /** * Coerce value @p inp of type @p itype to @p otype, writing the * result to @p outp. * * @param inp The value to be coerced. * @param ilen The size of @p inp, in bytes. * @param itype The base data type of @p inp. * @param[out] outp On success, the 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 data type to be written to @p outp. * * @retval 0 success * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too * small to hold the requested value. * @retval EFTYPE If the variable data cannot be coerced to @p otype. * @retval ERANGE If value coercion would overflow @p otype. */ int bhnd_nvram_value_coerce(const void *inp, size_t ilen, bhnd_nvram_type itype, void *outp, size_t *olen, bhnd_nvram_type otype) { bhnd_nvram_val val; int error; /* Wrap input buffer in a value instance */ error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype, BHND_NVRAM_VAL_BORROW_DATA|BHND_NVRAM_VAL_FIXED); if (error) return (error); /* Try to encode as requested type */ error = bhnd_nvram_val_encode(&val, outp, olen, otype); /* Clean up and return error */ bhnd_nvram_val_release(&val); return (error); }