Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_data.c
39536 views
/*-1* Copyright (c) 2015-2016 Landon Fuller <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer,9* without modification.10* 2. Redistributions in binary form must reproduce at minimum a disclaimer11* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any12* redistribution must be conditioned upon including a substantially13* similar Disclaimer requirement for further binary redistribution.14*15* NO WARRANTY16* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY19* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL20* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,21* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF22* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS23* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER24* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)25* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF26* THE POSSIBILITY OF SUCH DAMAGES.27*/2829#include <sys/cdefs.h>30#ifdef _KERNEL3132#include <sys/param.h>33#include <sys/systm.h>3435#include <machine/_inttypes.h>3637#else /* !_KERNEL */3839#include <errno.h>40#include <stdint.h>41#include <stdlib.h>42#include <string.h>4344#endif /* _KERNEL */4546#include "bhnd_nvram_private.h"47#include "bhnd_nvram_io.h"4849#include "bhnd_nvram_datavar.h"50#include "bhnd_nvram_data.h"5152/**53* Return a human-readable description for the given NVRAM data class.54*55* @param cls The NVRAM class.56*/57const char *58bhnd_nvram_data_class_desc(bhnd_nvram_data_class *cls)59{60return (cls->desc);61}6263/**64* Return the class-level capability flags (@see BHND_NVRAM_DATA_CAP_*) for65* of @p cls.66*67* @param cls The NVRAM class.68*/69uint32_t70bhnd_nvram_data_class_caps(bhnd_nvram_data_class *cls)71{72return (cls->caps);73}7475/**76* Serialize all NVRAM properties in @p plist using @p cls's NVRAM data77* format, writing the result to @p outp.78*79* @param cls The NVRAM data class to be used to perform80* serialization.81* @param props The raw property values to be serialized to82* @p outp, in serialization order.83* @param options Serialization options for @p cls, or NULL.84* @param[out] outp On success, the serialed NVRAM data will be85* written to this buffer. This argment may be86* NULL if the value is not desired.87* @param[in,out] olen The capacity of @p buf. On success, will be set88* to the actual length of the serialized data.89*90* @retval 0 success91*92* @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too93* small to hold the serialized data.94* @retval EINVAL If a property value required by @p cls is not found in95* @p plist.96* @retval EFTYPE If a property value in @p plist cannot be represented97* as the data type required by @p cls.98* @retval ERANGE If a property value in @p plist would would overflow99* (or underflow) the data type required by @p cls.100* @retval non-zero If serialization otherwise fails, a regular unix error101* code will be returned.102*/103int104bhnd_nvram_data_serialize(bhnd_nvram_data_class *cls,105bhnd_nvram_plist *props, bhnd_nvram_plist *options, void *outp,106size_t *olen)107{108return (cls->op_serialize(cls, props, options, outp, olen));109}110111/**112* Probe to see if this NVRAM data class class supports the data mapped by the113* given I/O context, returning a BHND_NVRAM_DATA_PROBE probe result.114*115* @param cls The NVRAM class.116* @param io An I/O context mapping the NVRAM data.117*118* @retval 0 if this is the only possible NVRAM data class for @p io.119* @retval negative if the probe succeeds, a negative value should be returned;120* the class returning the highest negative value should be selected to handle121* NVRAM parsing.122* @retval ENXIO If the NVRAM format is not handled by @p cls.123* @retval positive if an error occurs during probing, a regular unix error124* code should be returned.125*/126int127bhnd_nvram_data_probe(bhnd_nvram_data_class *cls, struct bhnd_nvram_io *io)128{129return (cls->op_probe(io));130}131132/**133* Probe to see if an NVRAM data class in @p classes supports parsing134* of the data mapped by @p io, returning the parsed data in @p data.135*136* The caller is responsible for deallocating the returned instance via137* bhnd_nvram_data_release().138*139* @param[out] data On success, the parsed NVRAM data instance.140* @param io An I/O context mapping the NVRAM data to be copied and parsed.141* @param classes An array of NVRAM data classes to be probed, or NULL to142* probe the default supported set.143* @param num_classes The number of NVRAM data classes in @p classes.144*145* @retval 0 success146* @retval ENXIO if no class is found capable of parsing @p io.147* @retval non-zero if an error otherwise occurs during allocation,148* initialization, or parsing of the NVRAM data, a regular unix error code149* will be returned.150*/151int152bhnd_nvram_data_probe_classes(struct bhnd_nvram_data **data,153struct bhnd_nvram_io *io, bhnd_nvram_data_class *classes[],154size_t num_classes)155{156bhnd_nvram_data_class *cls;157int error, prio, result;158159cls = NULL;160prio = 0;161*data = NULL;162163/* If class array is NULL, default to our linker set */164if (classes == NULL) {165classes = SET_BEGIN(bhnd_nvram_data_class_set);166num_classes = SET_COUNT(bhnd_nvram_data_class_set);167}168169/* Try to find the best data class capable of parsing io */170for (size_t i = 0; i < num_classes; i++) {171bhnd_nvram_data_class *next_cls;172173next_cls = classes[i];174175/* Try to probe */176result = bhnd_nvram_data_probe(next_cls, io);177178/* The parser did not match if an error was returned */179if (result > 0)180continue;181182/* Lower priority than previous match; keep183* searching */184if (cls != NULL && result <= prio)185continue;186187/* Drop any previously parsed data */188if (*data != NULL) {189bhnd_nvram_data_release(*data);190*data = NULL;191}192193/* If this is a 'maybe' match, attempt actual parsing to194* verify that this does in fact match */195if (result <= BHND_NVRAM_DATA_PROBE_MAYBE) {196/* If parsing fails, keep searching */197error = bhnd_nvram_data_new(next_cls, data, io);198if (error)199continue;200}201202/* Record best new match */203prio = result;204cls = next_cls;205206/* Terminate search immediately on207* BHND_NVRAM_DATA_PROBE_SPECIFIC */208if (result == BHND_NVRAM_DATA_PROBE_SPECIFIC)209break;210}211212/* If no match, return error */213if (cls == NULL)214return (ENXIO);215216/* If the NVRAM data was not parsed above, do so now */217if (*data == NULL) {218if ((error = bhnd_nvram_data_new(cls, data, io)))219return (error);220}221222return (0);223}224225/**226* Read a variable directly from @p io and decode as @p type.227*228* This may be used to perform reading of NVRAM variables during the very229* early boot process, prior to the availability of the kernel allocator.230*231* @param cls An NVRAM class capable of parsing @p io.232* @param io NVRAM data to be parsed.233* @param name The raw name of the variable to be fetched,234* including any device path (/pci/1/1/varname) or235* alias prefix (0:varname).236* @param[out] buf On success, the requested value will be written237* to this buffer. This argment may be NULL if238* the value is not desired.239* @param[in,out] len The capacity of @p buf. On success, will be set240* to the actual size of the requested value.241* @param type The data type to be written to @p buf.242*243* @retval 0 success244* @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too245* small to hold the requested value.246* @retval ENOENT If @p name is not found in @p io.247* @retval EFTYPE If the variable data cannot be coerced to @p type.248* @retval ERANGE If value coercion would overflow @p type.249* @retval non-zero If parsing @p io otherwise fails, a regular unix error250* code will be returned.251*/252int253bhnd_nvram_data_getvar_direct(bhnd_nvram_data_class *cls,254struct bhnd_nvram_io *io, const char *name, void *buf, size_t *len,255bhnd_nvram_type type)256{257return (cls->op_getvar_direct(io, name, buf, len, type));258}259260/**261* Allocate and initialize a new instance of data class @p cls, copying and262* parsing NVRAM data from @p io.263*264* The caller is responsible for releasing the returned parser instance265* reference via bhnd_nvram_data_release().266*267* @param cls If non-NULL, the data class to be allocated. If NULL,268* bhnd_nvram_data_probe_classes() will be used to determine the data format.269* @param[out] nv On success, a pointer to the newly allocated NVRAM data instance.270* @param io An I/O context mapping the NVRAM data to be copied and parsed.271*272* @retval 0 success273* @retval non-zero if an error occurs during allocation or initialization, a274* regular unix error code will be returned.275*/276int277bhnd_nvram_data_new(bhnd_nvram_data_class *cls, struct bhnd_nvram_data **nv,278struct bhnd_nvram_io *io)279{280struct bhnd_nvram_data *data;281int error;282283/* If NULL, try to identify the appropriate class */284if (cls == NULL)285return (bhnd_nvram_data_probe_classes(nv, io, NULL, 0));286287/* Allocate new instance */288BHND_NV_ASSERT(sizeof(struct bhnd_nvram_data) <= cls->size,289("instance size %zu less than minimum %zu", cls->size,290sizeof(struct bhnd_nvram_data)));291292data = bhnd_nv_calloc(1, cls->size);293data->cls = cls;294refcount_init(&data->refs, 1);295296/* Let the class handle initialization */297if ((error = cls->op_new(data, io))) {298bhnd_nv_free(data);299return (error);300}301302*nv = data;303return (0);304}305306/**307* Retain and return a reference to the given data instance.308*309* @param nv The reference to be retained.310*/311struct bhnd_nvram_data *312bhnd_nvram_data_retain(struct bhnd_nvram_data *nv)313{314refcount_acquire(&nv->refs);315return (nv);316}317318/**319* Release a reference to the given data instance.320*321* If this is the last reference, the data instance and its associated322* resources will be freed.323*324* @param nv The reference to be released.325*/326void327bhnd_nvram_data_release(struct bhnd_nvram_data *nv)328{329if (!refcount_release(&nv->refs))330return;331332/* Free any internal resources */333nv->cls->op_free(nv);334335/* Free the instance allocation */336bhnd_nv_free(nv);337}338339/**340* Return a pointer to @p nv's data class.341*342* @param nv The NVRAM data instance to be queried.343*/344bhnd_nvram_data_class *345bhnd_nvram_data_get_class(struct bhnd_nvram_data *nv)346{347return (nv->cls);348}349350/**351* Return the number of variables in @p nv.352*353* @param nv The NVRAM data to be queried.354*/355size_t356bhnd_nvram_data_count(struct bhnd_nvram_data *nv)357{358return (nv->cls->op_count(nv));359}360361/**362* Return a borrowed reference to the serialization options for @p nv,363* suitable for use with bhnd_nvram_data_serialize(), or NULL if none.364*365* @param nv The NVRAM data to be queried.366*/367bhnd_nvram_plist *368bhnd_nvram_data_options(struct bhnd_nvram_data *nv)369{370return (nv->cls->op_options(nv));371}372373/**374* Return the capability flags (@see BHND_NVRAM_DATA_CAP_*) for @p nv.375*376* @param nv The NVRAM data to be queried.377*/378uint32_t379bhnd_nvram_data_caps(struct bhnd_nvram_data *nv)380{381return (nv->cls->op_caps(nv));382}383384/**385* Iterate over @p nv, returning the names of subsequent variables.386*387* @param nv The NVRAM data to be iterated.388* @param[in,out] cookiep A pointer to a cookiep value previously returned389* by bhnd_nvram_data_next(), or a NULL value to390* begin iteration.391*392* @return Returns the next variable name, or NULL if there are no more393* variables defined in @p nv.394*/395const char *396bhnd_nvram_data_next(struct bhnd_nvram_data *nv, void **cookiep)397{398const char *name;399#ifdef BHND_NV_INVARIANTS400void *prev = *cookiep;401#endif402403/* Fetch next */404if ((name = nv->cls->op_next(nv, cookiep)) == NULL)405return (NULL);406407/* Enforce precedence ordering invariant between bhnd_nvram_data_next()408* and bhnd_nvram_data_getvar_order() */409#ifdef BHND_NV_INVARIANTS410if (prev != NULL &&411bhnd_nvram_data_getvar_order(nv, prev, *cookiep) > 0)412{413BHND_NV_PANIC("%s: returned out-of-order entry", __FUNCTION__);414}415#endif416417return (name);418}419420/**421* Search @p nv for a named variable, returning the variable's opaque reference422* if found, or NULL if unavailable.423*424* The BHND_NVRAM_DATA_CAP_INDEXED capability flag will be returned by425* bhnd_nvram_data_caps() if @p nv supports effecient name-based426* lookups.427*428* @param nv The NVRAM data to search.429* @param name The name to search for.430*431* @retval non-NULL If @p name is found, the opaque cookie value will be432* returned.433* @retval NULL If @p name is not found.434*/435void *436bhnd_nvram_data_find(struct bhnd_nvram_data *nv, const char *name)437{438return (nv->cls->op_find(nv, name));439}440441/**442* A generic implementation of bhnd_nvram_data_find().443*444* This implementation will use bhnd_nvram_data_next() to perform a445* simple O(n) case-insensitve search for @p name.446*/447void *448bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv, const char *name)449{450const char *next;451void *cookiep;452453cookiep = NULL;454while ((next = bhnd_nvram_data_next(nv, &cookiep))) {455if (strcmp(name, next) == 0)456return (cookiep);457}458459/* Not found */460return (NULL);461}462463/**464* Compare the declaration order of two NVRAM variables.465*466* Variable declaration order is used to determine the current order of467* the variables in the source data, as well as to determine the precedence468* of variable declarations in data sources that define duplicate names.469*470* The comparison order will match the order of variables returned via471* bhnd_nvstore_path_data_next().472*473* @param nv The NVRAM data.474* @param cookiep1 An NVRAM variable cookie previously475* returned via bhnd_nvram_data_next() or476* bhnd_nvram_data_find().477* @param cookiep2 An NVRAM variable cookie previously478* returned via bhnd_nvram_data_next() or479* bhnd_nvram_data_find().480*481* @retval <= -1 If @p cookiep1 has an earlier declaration order than482* @p cookiep2.483* @retval 0 If @p cookiep1 and @p cookiep2 are identical.484* @retval >= 1 If @p cookiep has a later declaration order than485* @p cookiep2.486*/487int488bhnd_nvram_data_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,489void *cookiep2)490{491return (nv->cls->op_getvar_order(nv, cookiep1, cookiep2));492}493494/**495* Read a variable and decode as @p type.496*497* @param nv The NVRAM data.498* @param cookiep An NVRAM variable cookie previously returned499* via bhnd_nvram_data_next() or500* bhnd_nvram_data_find().501* @param[out] buf On success, the requested value will be written502* to this buffer. This argment may be NULL if503* the value is not desired.504* @param[in,out] len The capacity of @p buf. On success, will be set505* to the actual size of the requested value.506* @param type The data type to be written to @p buf.507*508* @retval 0 success509* @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too510* small to hold the requested value.511* @retval EFTYPE If the variable data cannot be coerced to @p type.512* @retval ERANGE If value coercion would overflow @p type.513*/514int515bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,516size_t *len, bhnd_nvram_type type)517{518return (nv->cls->op_getvar(nv, cookiep, buf, len, type));519}520521/*522* Common bhnd_nvram_data_getvar_ptr() wrapper used by523* bhnd_nvram_data_generic_rp_getvar() and524* bhnd_nvram_data_generic_rp_copy_val().525*526* If a variable definition for the requested variable is found via527* bhnd_nvram_find_vardefn(), the definition will be used to populate fmt.528*/529static const void *530bhnd_nvram_data_getvar_ptr_info(struct bhnd_nvram_data *nv, void *cookiep,531size_t *len, bhnd_nvram_type *type, const bhnd_nvram_val_fmt **fmt)532{533const struct bhnd_nvram_vardefn *vdefn;534const char *name;535const void *vptr;536537BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR,538("instance does not advertise READ_PTR support"));539540/* Fetch pointer to variable data */541vptr = bhnd_nvram_data_getvar_ptr(nv, cookiep, len, type);542if (vptr == NULL)543return (NULL);544545/* Select a default value format implementation */546547/* Fetch the reference variable name */548name = bhnd_nvram_data_getvar_name(nv, cookiep);549550/* Trim path prefix, if any; the Broadcom NVRAM format assumes a global551* namespace for all variable definitions */552if (bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_DEVPATHS)553name = bhnd_nvram_trim_path_name(name);554555/* Check the variable definition table for a matching entry; if556* it exists, use it to populate the value format. */557vdefn = bhnd_nvram_find_vardefn(name);558if (vdefn != NULL) {559BHND_NV_ASSERT(vdefn->fmt != NULL,560("NULL format for %s", name));561*fmt = vdefn->fmt;562} else if (*type == BHND_NVRAM_TYPE_STRING) {563/* Default to Broadcom-specific string interpretation */564*fmt = &bhnd_nvram_val_bcm_string_fmt;565} else {566/* Fall back on native formatting */567*fmt = bhnd_nvram_val_default_fmt(*type);568}569570return (vptr);571}572573/**574* A generic implementation of bhnd_nvram_data_getvar().575*576* This implementation will call bhnd_nvram_data_getvar_ptr() to fetch577* a pointer to the variable data and perform data coercion on behalf578* of the caller.579*580* If a variable definition for the requested variable is available via581* bhnd_nvram_find_vardefn(), the definition will be used to provide a582* formatting instance to bhnd_nvram_val_init().583*/584int585bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, void *cookiep,586void *outp, size_t *olen, bhnd_nvram_type otype)587{588bhnd_nvram_val val;589const bhnd_nvram_val_fmt *fmt;590const void *vptr;591bhnd_nvram_type vtype;592size_t vlen;593int error;594595BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR,596("instance does not advertise READ_PTR support"));597598/* Fetch variable data and value format*/599vptr = bhnd_nvram_data_getvar_ptr_info(nv, cookiep, &vlen, &vtype,600&fmt);601if (vptr == NULL)602return (EINVAL);603604/* Attempt value coercion */605error = bhnd_nvram_val_init(&val, fmt, vptr, vlen, vtype,606BHND_NVRAM_VAL_BORROW_DATA);607if (error)608return (error);609610error = bhnd_nvram_val_encode(&val, outp, olen, otype);611612/* Clean up */613bhnd_nvram_val_release(&val);614return (error);615}616617/**618* Return a caller-owned copy of an NVRAM entry's variable data.619*620* The caller is responsible for deallocating the returned value via621* bhnd_nvram_val_release().622*623* @param nv The NVRAM data.624* @param cookiep An NVRAM variable cookie previously returned625* via bhnd_nvram_data_next() or bhnd_nvram_data_find().626* @param[out] value On success, the caller-owned value instance.627*628* @retval 0 success629* @retval ENOMEM If allocation fails.630* @retval non-zero If initialization of the value otherwise fails, a631* regular unix error code will be returned.632*/633int634bhnd_nvram_data_copy_val(struct bhnd_nvram_data *nv, void *cookiep,635bhnd_nvram_val **value)636{637return (nv->cls->op_copy_val(nv, cookiep, value));638}639640/**641* A generic implementation of bhnd_nvram_data_copy_val().642*643* This implementation will call bhnd_nvram_data_getvar_ptr() to fetch644* a pointer to the variable data and perform data coercion on behalf645* of the caller.646*647* If a variable definition for the requested variable is available via648* bhnd_nvram_find_vardefn(), the definition will be used to provide a649* formatting instance to bhnd_nvram_val_init().650*/651int652bhnd_nvram_data_generic_rp_copy_val(struct bhnd_nvram_data *nv,653void *cookiep, bhnd_nvram_val **value)654{655const bhnd_nvram_val_fmt *fmt;656const void *vptr;657bhnd_nvram_type vtype;658size_t vlen;659660BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR,661("instance does not advertise READ_PTR support"));662663/* Fetch variable data and value format*/664vptr = bhnd_nvram_data_getvar_ptr_info(nv, cookiep, &vlen, &vtype,665&fmt);666if (vptr == NULL)667return (EINVAL);668669/* Allocate and return the new value instance */670return (bhnd_nvram_val_new(value, fmt, vptr, vlen, vtype,671BHND_NVRAM_VAL_DYNAMIC));672}673674/**675* If available and supported by the NVRAM data instance, return a reference676* to the internal buffer containing an entry's variable data,677*678* Note that string values may not be NUL terminated.679*680* @param nv The NVRAM data.681* @param cookiep An NVRAM variable cookie previously returned682* via bhnd_nvram_data_next() or683* bhnd_nvram_data_find().684* @param[out] len On success, will be set to the actual size of685* the requested value.686* @param[out] type The data type of the entry data.687*688* @retval non-NULL success689* @retval NULL if direct data access is unsupported by @p nv, or690* unavailable for @p cookiep.691*/692const void *693bhnd_nvram_data_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,694size_t *len, bhnd_nvram_type *type)695{696return (nv->cls->op_getvar_ptr(nv, cookiep, len, type));697}698699/**700* Return the variable name associated with a given @p cookiep.701* @param nv The NVRAM data to be iterated.702* @param[in,out] cookiep A pointer to a cookiep value previously returned703* via bhnd_nvram_data_next() or704* bhnd_nvram_data_find().705*706* @return Returns the variable's name.707*/708const char *709bhnd_nvram_data_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)710{711return (nv->cls->op_getvar_name(nv, cookiep));712}713714/**715* Filter a request to set variable @p name with @p value.716*717* On success, the caller owns a reference to @p result, and must release718* any held resources via bhnd_nvram_val_release().719*720* @param nv The NVRAM data instance.721* @param name The name of the variable to be set.722* @param value The proposed value to be set.723* @param[out] result On success, a caller-owned reference to the filtered724* value to be set.725*726* @retval 0 success727* @retval ENOENT if @p name is unrecognized by @p nv.728* @retval EINVAL if @p name is read-only.729* @retval EINVAL if @p value cannot be converted to the required value730* type.731*/732int733bhnd_nvram_data_filter_setvar(struct bhnd_nvram_data *nv, const char *name,734bhnd_nvram_val *value, bhnd_nvram_val **result)735{736return (nv->cls->op_filter_setvar(nv, name, value, result));737}738739/**740* Filter a request to delete variable @p name.741*742* @param nv The NVRAM data instance.743* @param name The name of the variable to be deleted.744*745* @retval 0 success746* @retval ENOENT if @p name is unrecognized by @p nv.747* @retval EINVAL if @p name is read-only.748*/749int750bhnd_nvram_data_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)751{752return (nv->cls->op_filter_unsetvar(nv, name));753}754755756