From 77cb4d3e5016a2fd090d07ed3e01a199723641d9 Mon Sep 17 00:00:00 2001 From: "Landon J. Fuller" Date: Sat, 26 Nov 2016 23:22:32 +0000 Subject: [PATCH] bhnd(4): Unify NVRAM/SPROM parsing, implement compact SPROM layout encoding. - Defined an abstract NVRAM I/O API (bhnd_nvram_io), decoupling NVRAM/SPROM parsing from the actual underlying NVRAM data provider (e.g. CFE firmware devices). - Defined an abstract NVRAM data API (bhnd_nvram_data), decoupling higher-level NVRAM operations (indexed lookup, data conversion, etc) from the underlying NVRAM file format parsing/serialization. - Implemented a new high-level bhnd_nvram_store API, providing indexed variable lookup, pending write tracking, etc on top of an arbitrary bhnd_nvram_data instance. - Migrated all bhnd(4) NVRAM device drivers to the common bhnd_nvram_store API. - Implemented a common bhnd_nvram_val API for parsing/encoding NVRAM variable values, including applying format-specific behavior when converting to/from the NVRAM string representations. - Dropped the now unnecessary bhnd_nvram driver, and moved the broadcom/mips-specific CFE NVRAM driver out into sys/mips/broadcom. - Implemented a new nvram_map file format: - Variable definitions are now defined separately from the SPROM layout. This will also allow us to define CIS tuple NVRAM mappings referencing the common NVRAM variable definitions. - Variables can now be defined within arbitrary named groups. - Textual descriptions and help information can be defined inline for both variables and variable groups. - Implemented a new, compact encoding of SPROM image layout offsets. - Source-level (but not build system) support for building the NVRAM file format APIs (bhnd_nvram_io, bhnd_nvram_data, bhnd_nvram_store) as a userspace library. The new compact SPROM image layout encoding is loosely modeled on Apple dyld compressed LINKEDIT symbol binding opcodes; it provides a compact state-machine encoding of the mapping between NVRAM variables and the SPROM image offset, mask, and shift instructions necessary to decode or encode the SPROM variable data. The compact encoding reduces the size of the generated SPROM layout data from roughly 60KB to 3KB. The sequential nature SPROM layout opcode tables also simplify iteration of the SPROM variables, as it's no longer neccessary to iterate the full NVRAM variable definition table, but instead simply scan the SPROM revision's layout opcode table. Approved by: adrian (mentor) Differential Revision: https://reviews.freebsd.org/D8645 --- sys/conf/files | 20 +- sys/dev/bhnd/bhnd_subr.c | 3 +- sys/dev/bhnd/nvram/bhnd_nvram.c | 189 - sys/dev/bhnd/nvram/bhnd_nvram.h | 88 +- sys/dev/bhnd/nvram/bhnd_nvram_cfe.c | 373 -- sys/dev/bhnd/nvram/bhnd_nvram_common.c | 661 --- sys/dev/bhnd/nvram/bhnd_nvram_common.h | 180 - sys/dev/bhnd/nvram/bhnd_nvram_data.c | 527 ++ sys/dev/bhnd/nvram/bhnd_nvram_data.h | 137 + sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c | 748 +++ sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c | 380 ++ sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h | 73 + ...m_parservar.h => bhnd_nvram_data_bcmvar.h} | 68 +- sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c | 586 ++ sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c | 1988 +++++++ sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h | 150 + sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c | 662 +++ ...m_parserreg.h => bhnd_nvram_data_tlvreg.h} | 41 +- sys/dev/bhnd/nvram/bhnd_nvram_datavar.h | 188 + sys/dev/bhnd/nvram/bhnd_nvram_io.c | 205 + sys/dev/bhnd/nvram/bhnd_nvram_io.h | 79 + sys/dev/bhnd/nvram/bhnd_nvram_iobuf.c | 341 ++ sys/dev/bhnd/nvram/bhnd_nvram_iores.c | 302 ++ sys/dev/bhnd/nvram/bhnd_nvram_iovar.h | 106 + sys/dev/bhnd/nvram/bhnd_nvram_parser.c | 1578 ------ sys/dev/bhnd/nvram/bhnd_nvram_parser.h | 101 - sys/dev/bhnd/nvram/bhnd_nvram_parservar.h | 86 - sys/dev/bhnd/nvram/bhnd_nvram_private.h | 402 ++ sys/dev/bhnd/nvram/bhnd_nvram_store.c | 572 ++ .../{bhnd_nvramvar.h => bhnd_nvram_store.h} | 54 +- sys/dev/bhnd/nvram/bhnd_nvram_storevar.h | 111 + sys/dev/bhnd/nvram/bhnd_nvram_subr.c | 1271 +++++ sys/dev/bhnd/nvram/bhnd_nvram_value.c | 1313 +++++ sys/dev/bhnd/nvram/bhnd_nvram_value.h | 224 + sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c | 1032 ++++ sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c | 883 ++++ sys/dev/bhnd/nvram/bhnd_nvram_valuevar.h | 100 + sys/dev/bhnd/nvram/bhnd_sprom.c | 98 +- sys/dev/bhnd/nvram/bhnd_sprom_parser.c | 756 --- sys/dev/bhnd/nvram/bhnd_spromvar.h | 9 +- sys/dev/bhnd/nvram/nvram_map | 4457 +++++++++++----- sys/dev/bhnd/tools/nvram_map_gen.awk | 4692 ++++++++++++++--- sys/dev/bwn/bwn_mac.c | 2 +- sys/mips/broadcom/bcm_nvram_cfe.c | 527 ++ .../broadcom/bcm_nvram_cfevar.h} | 41 +- sys/mips/broadcom/files.broadcom | 2 + sys/modules/bhnd/Makefile | 20 +- 47 files changed, 20020 insertions(+), 6406 deletions(-) delete mode 100644 sys/dev/bhnd/nvram/bhnd_nvram.c delete mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_cfe.c delete mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_common.c delete mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_common.h create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_data.c create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_data.h create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h rename sys/dev/bhnd/nvram/{bhnd_sprom_parservar.h => bhnd_nvram_data_bcmvar.h} (55%) create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c rename sys/dev/bhnd/nvram/{bhnd_nvram_parserreg.h => bhnd_nvram_data_tlvreg.h} (58%) create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_datavar.h create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_io.c create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_io.h create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_iobuf.c create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_iores.c create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_iovar.h delete mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_parser.c delete mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_parser.h delete mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_parservar.h create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_private.h create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_store.c rename sys/dev/bhnd/nvram/{bhnd_nvramvar.h => bhnd_nvram_store.h} (60%) create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_storevar.h create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_subr.c create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_value.c create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_value.h create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c create mode 100644 sys/dev/bhnd/nvram/bhnd_nvram_valuevar.h delete mode 100644 sys/dev/bhnd/nvram/bhnd_sprom_parser.c create mode 100644 sys/mips/broadcom/bcm_nvram_cfe.c rename sys/{dev/bhnd/nvram/bhnd_sprom_parser.h => mips/broadcom/bcm_nvram_cfevar.h} (57%) diff --git a/sys/conf/files b/sys/conf/files index 1ff24ab3dcf..cae888bd5bd 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1220,14 +1220,22 @@ dev/bhnd/cores/pmu/bhnd_pmu.c optional bhnd dev/bhnd/cores/pmu/bhnd_pmu_core.c optional bhnd dev/bhnd/cores/pmu/bhnd_pmu_if.m optional bhnd dev/bhnd/cores/pmu/bhnd_pmu_subr.c optional bhnd -dev/bhnd/nvram/bhnd_nvram.c optional bhnd -dev/bhnd/nvram/bhnd_nvram_common.c optional bhnd -dev/bhnd/nvram/bhnd_nvram_cfe.c optional bhnd siba_nexus cfe | \ - bhnd bcma_nexus cfe +dev/bhnd/nvram/bhnd_nvram_data.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_data_bcm.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_data_btxt.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_data_sprom.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_data_tlv.c optional bhnd dev/bhnd/nvram/bhnd_nvram_if.m optional bhnd -dev/bhnd/nvram/bhnd_nvram_parser.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_io.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_iobuf.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_iores.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_store.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_subr.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_value.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_value_fmts.c optional bhnd +dev/bhnd/nvram/bhnd_nvram_value_prf.c optional bhnd dev/bhnd/nvram/bhnd_sprom.c optional bhnd -dev/bhnd/nvram/bhnd_sprom_parser.c optional bhnd dev/bhnd/siba/siba.c optional siba bhnd dev/bhnd/siba/siba_bhndb.c optional siba bhnd bhndb dev/bhnd/siba/siba_erom.c optional siba bhnd diff --git a/sys/dev/bhnd/bhnd_subr.c b/sys/dev/bhnd/bhnd_subr.c index 8b70e24376e..716a8721ece 100644 --- a/sys/dev/bhnd/bhnd_subr.c +++ b/sys/dev/bhnd/bhnd_subr.c @@ -1057,7 +1057,8 @@ bhnd_nvram_getvar_str(device_t dev, const char *name, char *buf, size_t len, int error; larg = len; - error = bhnd_nvram_getvar(dev, name, buf, &larg, BHND_NVRAM_TYPE_CSTR); + error = bhnd_nvram_getvar(dev, name, buf, &larg, + BHND_NVRAM_TYPE_STRING); if (rlen != NULL) *rlen = larg; diff --git a/sys/dev/bhnd/nvram/bhnd_nvram.c b/sys/dev/bhnd/nvram/bhnd_nvram.c deleted file mode 100644 index 78cad99f24e..00000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram.c +++ /dev/null @@ -1,189 +0,0 @@ -/*- - * Copyright (c) 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$"); - -/* - * BHND CFE NVRAM driver. - * - * Provides access to device NVRAM via CFE. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -#include "bhnd_nvram_if.h" - -#include "bhnd_nvramvar.h" - -/** - * Default bhnd_nvram driver implementation of DEVICE_PROBE(). - */ -int -bhnd_nvram_probe(device_t dev) -{ - device_set_desc(dev, "Broadcom NVRAM"); - - /* Refuse wildcard attachments */ - return (BUS_PROBE_NOWILDCARD); -} - -/** - * Call from subclass DEVICE_ATTACH() implementations to handle - * device attachment. - * - * @param dev BHND NVRAM device. - * @param data NVRAM data to be copied and parsed. No reference to data - * will be held after return. - * @param size Size of @p data, in bytes. - * @param fmt NVRAM format. - */ -int -bhnd_nvram_attach(device_t dev, void *data, size_t size, bhnd_nvram_format fmt) -{ - struct bhnd_nvram_softc *sc; - int error; - - sc = device_get_softc(dev); - sc->dev = dev; - - /* Initialize NVRAM parser */ - error = bhnd_nvram_parser_init(&sc->nvram, dev, data, size, fmt); - if (error) - return (error); - - /* Initialize mutex */ - BHND_NVRAM_LOCK_INIT(sc); - - return (0); -} - -/** - * Default bhnd_nvram driver implementation of DEVICE_RESUME(). - */ -int -bhnd_nvram_resume(device_t dev) -{ - return (0); -} - -/** - * Default bhnd_nvram driver implementation of DEVICE_SUSPEND(). - */ -int -bhnd_nvram_suspend(device_t dev) -{ - return (0); -} - -/** - * Default bhnd_nvram driver implementation of DEVICE_DETACH(). - */ -int -bhnd_nvram_detach(device_t dev) -{ - struct bhnd_nvram_softc *sc; - - sc = device_get_softc(dev); - - bhnd_nvram_parser_fini(&sc->nvram); - BHND_NVRAM_LOCK_DESTROY(sc); - - return (0); -} - -/** - * Default bhnd_nvram driver implementation of BHND_NVRAM_GETVAR(). - */ -static int -bhnd_nvram_getvar_method(device_t dev, const char *name, void *buf, size_t *len, - bhnd_nvram_type type) -{ - struct bhnd_nvram_softc *sc; - int error; - - sc = device_get_softc(dev); - - BHND_NVRAM_LOCK(sc); - error = bhnd_nvram_parser_getvar(&sc->nvram, name, buf, len, type); - BHND_NVRAM_UNLOCK(sc); - - return (error); -} - -/** - * Default bhnd_nvram driver implementation of BHND_NVRAM_SETVAR(). - */ -static int -bhnd_nvram_setvar_method(device_t dev, const char *name, const void *buf, - size_t len, bhnd_nvram_type type) -{ - struct bhnd_nvram_softc *sc; - int error; - - sc = device_get_softc(dev); - - BHND_NVRAM_LOCK(sc); - error = bhnd_nvram_parser_setvar(&sc->nvram, name, buf, len, type); - BHND_NVRAM_UNLOCK(sc); - - return (error); -} - -static device_method_t bhnd_nvram_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, bhnd_nvram_probe), - DEVMETHOD(device_resume, bhnd_nvram_resume), - DEVMETHOD(device_suspend, bhnd_nvram_suspend), - DEVMETHOD(device_detach, bhnd_nvram_detach), - - /* NVRAM interface */ - DEVMETHOD(bhnd_nvram_getvar, bhnd_nvram_getvar_method), - DEVMETHOD(bhnd_nvram_setvar, bhnd_nvram_setvar_method), - - DEVMETHOD_END -}; - -DEFINE_CLASS_0(bhnd_nvram, bhnd_nvram_driver, bhnd_nvram_methods, sizeof(struct bhnd_nvram_softc)); diff --git a/sys/dev/bhnd/nvram/bhnd_nvram.h b/sys/dev/bhnd/nvram/bhnd_nvram.h index 5d7da07173d..f21ec696b2c 100644 --- a/sys/dev/bhnd/nvram/bhnd_nvram.h +++ b/sys/dev/bhnd/nvram/bhnd_nvram.h @@ -32,11 +32,17 @@ #ifndef _BHND_NVRAM_BHND_NVRAM_H_ #define _BHND_NVRAM_BHND_NVRAM_H_ +#ifdef _KERNEL +#include +#else /* !_KERNEL */ +#include +#include +#endif /* _KERNEL */ + /** * NVRAM data sources supported by bhnd(4) devices. */ typedef enum { - BHND_NVRAM_SRC_OTP, /**< On-chip one-time-programmable * memory. */ @@ -67,50 +73,52 @@ typedef enum { */ } bhnd_nvram_src; -/** Supported NVRAM formats. */ +/** + * 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_FMT_BCM = 0, /**< Broadcom NUL-delimited key=value pairs */ - BHND_NVRAM_FMT_TLV = 1, /**< CFE TLV encoding, as used on WGT634U */ - BHND_NVRAM_FMT_BTXT = 2, /**< Broadcom board text file. This is used - to provide external NVRAM data for some - fullmac WiFi devices. */ - BHND_NVRAM_FMT_SPROM = 3, /**< SPROM/OTP-specific encoding used by - Broadcom network adapters */ - BHND_NVRAM_FMT_CIS = 4, /**< A mostly CIS-compatible encoding used - on some Broadcom network adapters */ - BHND_NVRAM_FMT_UNKNOWN = 5 /**< Unknown or unrecognized format */ -} bhnd_nvram_format; + 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 */ + /* 10-15 reserved for primitive (non-array) types */ -/** bhnd_nvram_type bit flags */ -enum { - BHND_NVRAM_TF_SIGNED = (1<<7), -}; - -#define BHND_NVRAM_TYPE_ID_MASK 0xF -#define BHND_NVRAM_TYPE_FLAGS_MASK 0x70 - -#define BHND_NVRAM_TYPE_ID(_id, _flags) \ - (((_id) & BHND_NVRAM_TYPE_ID_MASK) | \ - ((_flags) & BHND_NVRAM_TYPE_FLAGS_MASK)) - -/** Supported NVRAM data types */ -typedef enum { - BHND_NVRAM_TYPE_UINT8 = BHND_NVRAM_TYPE_ID(0, 0), /**< unsigned 8-bit integer */ - BHND_NVRAM_TYPE_UINT16 = BHND_NVRAM_TYPE_ID(1, 0), /**< unsigned 16-bit integer */ - BHND_NVRAM_TYPE_UINT32 = BHND_NVRAM_TYPE_ID(2, 0), /**< unsigned 32-bit integer */ - BHND_NVRAM_TYPE_INT8 = BHND_NVRAM_TYPE_ID(4, BHND_NVRAM_TF_SIGNED), /**< signed 8-bit integer */ - BHND_NVRAM_TYPE_INT16 = BHND_NVRAM_TYPE_ID(5, BHND_NVRAM_TF_SIGNED), /**< signed 16-bit integer */ - BHND_NVRAM_TYPE_INT32 = BHND_NVRAM_TYPE_ID(6, BHND_NVRAM_TF_SIGNED), /**< signed 32-bit integer */ - BHND_NVRAM_TYPE_CHAR = BHND_NVRAM_TYPE_ID(7, BHND_NVRAM_TF_SIGNED), /**< ASCII character */ - BHND_NVRAM_TYPE_CSTR = BHND_NVRAM_TYPE_ID(8, 0), /**< NUL-terminated C string */ + 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; -#undef BHND_NVRAM_TYPE_ID_MASK -#undef BHND_NVRAM_TYPE_FLAGS_MASK -#undef BHND_NVRAM_TYPE_ID +const char *bhnd_nvram_string_array_next(const char *inp, size_t ilen, + const char *prev); -#define BHND_NVRAM_SIGNED_TYPE(_type) \ - (((_type) & BHND_NVRAM_TF_SIGNED) == BHND_NVRAM_TF_SIGNED) +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); +const char *bhnd_nvram_type_name(bhnd_nvram_type type); #endif /* _BHND_NVRAM_BHND_NVRAM_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_cfe.c b/sys/dev/bhnd/nvram/bhnd_nvram_cfe.c deleted file mode 100644 index 766ccd6698c..00000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram_cfe.c +++ /dev/null @@ -1,373 +0,0 @@ -/*- - * Copyright (c) 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$"); - -/* - * BHND CFE NVRAM driver. - * - * Provides access to device NVRAM via CFE. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -#include "bhnd_nvram_if.h" - -#include "bhnd_nvramvar.h" - -static int nvram_open_cfedev(device_t dev, char *devname, int fd, - int64_t *offset, uint32_t *size, bhnd_nvram_format fmt); -static char *nvram_find_cfedev(device_t dev, int *fd, int64_t *offset, - uint32_t *size, bhnd_nvram_format *fmt); - -/** Known CFE NVRAM device names, in probe order. */ -static char *nvram_cfe_devs[] = { - "nflash0.nvram", /* NAND */ - "nflash1.nvram", - "flash0.nvram", - "flash1.nvram", -}; - -/** Supported CFE NVRAM formats, in probe order. */ -bhnd_nvram_format nvram_cfe_fmts[] = { - BHND_NVRAM_FMT_BCM, - BHND_NVRAM_FMT_TLV -}; - - -static int -bhnd_nvram_cfe_probe(device_t dev) -{ - char *devname; - bhnd_nvram_format fmt; - int64_t offset; - uint32_t size; - int error; - int fd; - - /* Defer to default driver implementation */ - if ((error = bhnd_nvram_probe(dev)) > 0) - return (error); - - /* Locate a usable CFE device */ - devname = nvram_find_cfedev(dev, &fd, &offset, &size, &fmt); - if (devname == NULL) - return (ENXIO); - cfe_close(fd); - - switch (fmt) { - case BHND_NVRAM_FMT_BCM: - device_set_desc(dev, "Broadcom NVRAM"); - break; - case BHND_NVRAM_FMT_TLV: - device_set_desc(dev, "Broadcom WGT634U NVRAM"); - break; - default: - device_printf(dev, "unknown NVRAM format: %d\n", fmt); - return (ENXIO); - } - - /* Refuse wildcard attachments */ - return (BUS_PROBE_NOWILDCARD); -} - - -static int -bhnd_nvram_cfe_attach(device_t dev) -{ - char *devname; - unsigned char *buffer; - bhnd_nvram_format fmt; - int64_t offset; - uint32_t size; - int error; - int fd; - - error = 0; - buffer = NULL; - fd = CFE_ERR; - - /* Locate NVRAM device via CFE */ - devname = nvram_find_cfedev(dev, &fd, &offset, &size, &fmt); - if (devname == NULL) { - device_printf(dev, "CFE NVRAM device not found\n"); - return (ENXIO); - } - - /* Copy out NVRAM buffer */ - buffer = malloc(size, M_TEMP, M_NOWAIT); - if (buffer == NULL) - return (ENOMEM); - - for (size_t remain = size; remain > 0;) { - int nr, req; - - req = ulmin(INT_MAX, remain); - nr = cfe_readblk(fd, size-remain, buffer+(size-remain), - req); - if (nr < 0) { - device_printf(dev, "%s: cfe_readblk() failed: %d\n", - devname, fd); - - error = ENXIO; - goto cleanup; - } - - remain -= nr; - - if (nr == 0 && remain > 0) { - device_printf(dev, "%s: cfe_readblk() unexpected EOF: " - "%zu of %zu pending\n", devname, remain, size); - - error = ENXIO; - goto cleanup; - } - } - - device_printf(dev, "CFE %s (%#jx+%#jx)\n", devname, (uintmax_t)offset, - (uintmax_t)size); - - /* Delegate to default driver implementation */ - error = bhnd_nvram_attach(dev, buffer, size, fmt); - -cleanup: - if (buffer != NULL) - free(buffer, M_TEMP); - - if (fd >= 0) - cfe_close(fd); - - return (error); -} - -/** - * Identify and open a CFE NVRAM device. - * - * @param dev bhnd_nvram_cfe device. - * @param devname The name of the CFE device to be probed. - * @param fd An open CFE file descriptor for @p devname. - * @param[out] offset On success, the NVRAM data offset within @p @fd. - * @param[out] size On success, maximum the NVRAM data size within @p fd. - * @param fmt The expected NVRAM data format for this device. - * - * @retval 0 success - * @retval non-zero If probing @p devname fails, a regular unix - * error code will be returned. - */ -static int -nvram_open_cfedev(device_t dev, char *devname, int fd, int64_t *offset, - uint32_t *size, bhnd_nvram_format fmt) -{ - union bhnd_nvram_ident ident; - nvram_info_t nvram_info; - int cerr, devinfo, dtype, rlen; - int error; - - /* Try to fetch device info */ - if ((devinfo = cfe_getdevinfo(devname)) == CFE_ERR_DEVNOTFOUND) - return (ENODEV); - - if (devinfo < 0) { - device_printf(dev, "cfe_getdevinfo() failed: %d", - devinfo); - return (ENXIO); - } - - /* Verify device type */ - dtype = devinfo & CFE_DEV_MASK; - switch (dtype) { - case CFE_DEV_FLASH: - case CFE_DEV_NVRAM: - /* Valid device type */ - break; - default: - device_printf(dev, "%s: unknown device type %d\n", - devname, dtype); - return (ENXIO); - } - - /* Try to fetch nvram info from CFE */ - cerr = cfe_ioctl(fd, IOCTL_NVRAM_GETINFO, (unsigned char *)&nvram_info, - sizeof(nvram_info), &rlen, 0); - if (cerr != CFE_OK && cerr != CFE_ERR_INV_COMMAND) { - device_printf(dev, "%s: IOCTL_NVRAM_GETINFO failed: %d\n", - devname, cerr); - return (ENXIO); - } - - /* Fall back on flash info. - * - * This is known to be required on the Asus RT-N53 (CFE 5.70.55.33, - * BBP 1.0.37, BCM5358UB0), where IOCTL_NVRAM_GETINFO returns - * CFE_ERR_INV_COMMAND. - */ - if (cerr == CFE_ERR_INV_COMMAND) { - flash_info_t fi; - - cerr = cfe_ioctl(fd, IOCTL_FLASH_GETINFO, (unsigned char *)&fi, - sizeof(fi), &rlen, 0); - - if (cerr != CFE_OK) { - device_printf(dev, "%s: IOCTL_FLASH_GETINFO failed: " - "%d\n", devname, cerr); - return (ENXIO); - } - - nvram_info.nvram_eraseflg = - !(fi.flash_flags & FLASH_FLAG_NOERASE); - nvram_info.nvram_offset = 0x0; - nvram_info.nvram_size = fi.flash_size; - } - - /* Try to read NVRAM header/format identification */ - cerr = cfe_readblk(fd, 0, (unsigned char *)&ident, sizeof(ident)); - if (cerr < 0) { - device_printf(dev, "%s: cfe_readblk() failed: %d\n", - devname, cerr); - return (ENXIO); - } else if (cerr == 0) { - /* EOF */ - return (ENODEV); - } else if (cerr != sizeof(ident)) { - device_printf(dev, "%s: cfe_readblk() short read: %d\n", - devname, cerr); - return (ENXIO); - } - - /* Verify expected format */ - if ((error = bhnd_nvram_parser_identify(&ident, fmt))) - return (error); - - /* Provide offset and size */ - switch (fmt) { - case BHND_NVRAM_FMT_TLV: - /* No size field is available; must assume the NVRAM data - * consumes up to the full CFE NVRAM range */ - *offset = nvram_info.nvram_offset; - *size = nvram_info.nvram_size; - break; - case BHND_NVRAM_FMT_BCM: - if (ident.bcm.size > nvram_info.nvram_size) { - device_printf(dev, "%s: NVRAM size %#x overruns %#x " - "device limit\n", devname, ident.bcm.size, - nvram_info.nvram_size); - return (ENODEV); - } - - *offset = nvram_info.nvram_offset; - *size = ident.bcm.size; - break; - default: - return (EINVAL); - } - - return (0); -} - -/** - * Find (and open) a CFE NVRAM device. - * - * @param dev bhnd_nvram_cfe device. - * @param[out] fd On success, a valid CFE file descriptor. The callee - * is responsible for closing this file descriptor via - * cfe_close(). - * @param[out] offset On success, the NVRAM data offset within @p @fd. - * @param[out] size On success, maximum the NVRAM data size within @p fd. - * @param fmt The expected NVRAM data format for this device. - * - * @return On success, the opened CFE device's name will be returned. On - * error, returns NULL. - */ -static char * -nvram_find_cfedev(device_t dev, int *fd, int64_t *offset, - uint32_t *size, bhnd_nvram_format *fmt) -{ - char *devname; - int error; - - for (u_int i = 0; i < nitems(nvram_cfe_fmts); i++) { - *fmt = nvram_cfe_fmts[i]; - - for (u_int j = 0; j < nitems(nvram_cfe_devs); j++) { - devname = nvram_cfe_devs[j]; - - /* Open for reading */ - *fd = cfe_open(devname); - if (*fd == CFE_ERR_DEVNOTFOUND) { - continue; - } else if (*fd < 0) { - device_printf(dev, "%s: cfe_open() failed: " - "%d\n", devname, *fd); - continue; - } - - /* Probe */ - error = nvram_open_cfedev(dev, devname, *fd, offset, - size, *fmt); - if (error == 0) - return (devname); - - /* Keep searching */ - devname = NULL; - cfe_close(*fd); - } - } - - return (NULL); -} - -static device_method_t bhnd_nvram_cfe_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, bhnd_nvram_cfe_probe), - DEVMETHOD(device_attach, bhnd_nvram_cfe_attach), - - DEVMETHOD_END -}; - -DEFINE_CLASS_1(bhnd_nvram, bhnd_nvram_cfe, bhnd_nvram_cfe_methods, - sizeof(struct bhnd_nvram_softc), bhnd_nvram_driver); -EARLY_DRIVER_MODULE(bhnd_nvram_cfe, nexus, bhnd_nvram_cfe, - bhnd_nvram_devclass, NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_EARLY); diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_common.c b/sys/dev/bhnd/nvram/bhnd_nvram_common.c deleted file mode 100644 index 16249b4f04d..00000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram_common.c +++ /dev/null @@ -1,661 +0,0 @@ -/*- - * Copyright (c) 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 - -#include -#include -#include -#include -#include - -#include - -#include "bhnd_nvram_common.h" - -#include "bhnd_nvram_map_data.h" - -/* - * Common NVRAM/SPROM support, including NVRAM variable map - * lookup. - */ - -MALLOC_DEFINE(M_BHND_NVRAM, "bhnd_nvram", "bhnd nvram data"); - -/* - * 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 the size of type @p type, or 0 if @p type has a variable width - * (e.g. a C string). - * - * @param type NVRAM data type. - * @result the byte width of @p type. - */ -size_t -bhnd_nvram_type_width(bhnd_nvram_type type) -{ - switch (type) { - 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_CSTR: - return (0); - } - - /* Quiesce gcc4.2 */ - panic("bhnd nvram type %u unknown", type); -} - -/** - * Return the format string to use when printing @p type with @p sfmt - * - * @param type The value type being printed. - * @param sfmt The string format required for @p type. - * @param elem_num The element index being printed. If this is the first - * value in an array of elements, the index would be 0, the next would be 1, - * and so on. - * - * @retval non-NULL A valid printf format string. - * @retval NULL If no format string is available for @p type and @p sfmt. - */ -const char * -bhnd_nvram_type_fmt(bhnd_nvram_type type, bhnd_nvram_sfmt sfmt, - size_t elem_num) -{ - size_t width; - - width = bhnd_nvram_type_width(type); - - /* Sanity-check the type width */ - switch (width) { - case 1: - case 2: - case 4: - break; - default: - return (NULL); - } - - /* Special-cased string formats */ - switch (sfmt) { - case BHND_NVRAM_SFMT_LEDDC: - /* If this is the first element, use the 0x-prefixed - * SFMT_HEX */ - if (elem_num == 0) - sfmt = BHND_NVRAM_SFMT_HEX; - break; - default: - break; - } - - /* Return the format string */ - switch (sfmt) { - case BHND_NVRAM_SFMT_MACADDR: - switch (width) { - case 1: return ("%02" PRIx8); - } - break; - - case BHND_NVRAM_SFMT_HEX: - switch (width) { - case 1: return ("0x%02" PRIx8); - case 2: return ("0x%04" PRIx16); - case 4: return ("0x%08" PRIx32); - } - break; - case BHND_NVRAM_SFMT_DEC: - if (BHND_NVRAM_SIGNED_TYPE(type)) { - switch (width) { - case 1: return ("%" PRId8); - case 2: return ("%" PRId16); - case 4: return ("%" PRId32); - } - } else { - switch (width) { - case 1: return ("%" PRIu8); - case 2: return ("%" PRIu16); - case 4: return ("%" PRIu32); - } - } - break; - case BHND_NVRAM_SFMT_LEDDC: - switch (width) { - case 1: return ("%02" PRIx8); - case 2: return ("%04" PRIx16); - case 4: return ("%08" PRIx32); - } - break; - - case BHND_NVRAM_SFMT_CCODE: - switch (width) { - case 1: return ("%c"); - } - break; - } - - return (NULL); -} - -/** - * 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) -{ - size_t min, mid, max; - int order; - - /* - * Locate the requested variable using a binary search. - * - * The variable table is guaranteed to be sorted in lexicographical - * order (using the 'C' locale for collation rules) - */ - min = 0; - mid = 0; - max = nitems(bhnd_nvram_vardefs) - 1; - - while (max >= min) { - /* Select midpoint */ - mid = (min + max) / 2; - - /* Determine which side of the partition to search */ - order = strcmp(bhnd_nvram_vardefs[mid].name, varname); - if (order < 0) { - /* Search upper partition */ - min = mid + 1; - } else if (order > 0) { - /* Search lower partition */ - max = mid - 1; - } else if (order == 0) { - /* Match found */ - return (&bhnd_nvram_vardefs[mid]); - } - } - - /* Not found */ - return (NULL); -} - -/** - * 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 && isdigit(*name)) { - for (const char *p = name; p - name < limit; p++) { - if (isdigit(*p)) - continue; - else if (*p == ':') - return (false); - else - break; - } - } - - /* Scan for special characters */ - for (const char *p = name; p - name < limit; p++) { - switch (*p) { - case '/': /* path delimiter */ - case '=': /* key=value delimiter */ - return (false); - - default: - if (isspace(*p) || !isascii(*p)) - return (false); - } - } - - return (true); -} - -/** - * Parse an octet string, such as a MAC address, consisting of hex octets - * separated with ':' or '-'. - * - * @param value The octet string to parse. - * @param value_len The length of @p value, in bytes. - * @param buf The output buffer to which parsed octets will be written. May be - * NULL. - * @param[in,out] len The capacity of @p buf. On success, will be set - * to the actual size of the requested value. - * @param type - */ -int -bhnd_nvram_parse_octet_string(const char *value, size_t value_len, void *buf, - size_t *len, bhnd_nvram_type type) -{ - size_t limit, nbytes, width; - size_t slen; - uint8_t octet; - char delim; - - slen = strnlen(value, value_len); - - nbytes = 0; - if (buf != NULL) - limit = *len; - else - limit = 0; - - /* Type must have a fixed width */ - if ((width = bhnd_nvram_type_width(type)) == 0) - return (EINVAL); - - /* String length (not including NUL) must be aligned on an octet - * boundary ('AA:BB', not 'AA:B', etc), and must be large enough - * to contain at least two octet entries. */ - if (slen % 3 != 2 || slen < sizeof("AA:BB") - 1) - return (EINVAL); - - /* Identify the delimiter used. The standard delimiter for - * MAC addresses is ':', but some earlier NVRAM formats may use - * '-' */ - switch ((delim = value[2])) { - case ':': - case '-': - break; - default: - return (EINVAL); - } - - /* Parse octets */ - for (const char *p = value; p - value < value_len; p++) { - void *outp; - size_t pos; - unsigned char c; - - pos = (p - value); - - /* Skip delimiter after each octet */ - if (pos % 3 == 2) { - if (*p == delim) - continue; - - if (*p == '\0') - return (0); - - /* No delimiter? */ - return (EINVAL); - } - - c = *(const unsigned char *)p; - - if (isdigit(c)) - c -= '0'; - else if (isxdigit(c)) - c -= islower(c) ? 'a' - 10 : 'A' - 10; - else - return (EINVAL); - - if (pos % 3 == 0) { - /* MSB */ - octet = (c << 4); - continue; - } else if (pos % 3 == 1) { - /* LSB */ - octet |= (c & 0xF); - } - - /* Skip writing? */ - if (limit < width || limit - width < nbytes) { - nbytes += width; - continue; - } - - /* Write output */ - outp = ((uint8_t *)buf) + nbytes; - switch (type) { - case BHND_NVRAM_TYPE_UINT8: - *(uint8_t *)outp = octet; - break; - - case BHND_NVRAM_TYPE_UINT16: - *(uint16_t *)outp = octet; - break; - - case BHND_NVRAM_TYPE_UINT32: - *(uint32_t *)outp = octet; - break; - - case BHND_NVRAM_TYPE_INT8: - if (octet > INT8_MAX) - return (ERANGE); - *(int8_t *)outp = (int8_t)octet; - break; - - case BHND_NVRAM_TYPE_INT16: - *(int16_t *)outp = (int8_t)octet; - break; - - case BHND_NVRAM_TYPE_INT32: - *(int32_t *)outp = (int8_t)octet; - break; - - case BHND_NVRAM_TYPE_CHAR: -#if (CHAR_MAX < UINT8_MAX) - if (octet > CHAR_MAX) - return (ERANGE); -#endif - *(char *)outp = (char)octet; - break; - default: - printf("unknown type %d\n", type); - return (EINVAL); - } - - nbytes += width; - } - - return (0); -} - - -/** - * Initialize a new variable hash table with @p nelements. - * - * @param map Hash table instance to be initialized. - * @param nelements The number of hash table buckets to allocate. - * @param flags Hash table flags (HASH_*). - */ -int -bhnd_nvram_varmap_init(struct bhnd_nvram_varmap *map, size_t nelements, - int flags) -{ - map->table = hashinit_flags(nelements, M_BHND_NVRAM, &map->mask, - flags); - if (map->table == NULL) - return (ENOMEM); - - return (0); -} - -/** - * Deallocate all resources associated with @p map. - * - * @param map Hash table to be deallocated. - */ -void -bhnd_nvram_varmap_free(struct bhnd_nvram_varmap *map) -{ - struct bhnd_nvram_tuple *t, *tnext; - - /* Free all elements */ - for (size_t i = 0; i <= map->mask; i++) { - LIST_FOREACH_SAFE(t, &map->table[i], t_link, tnext) { - LIST_REMOVE(t, t_link); - bhnd_nvram_tuple_free(t); - } - } - - /* Free hash table */ - hashdestroy(map->table, M_BHND_NVRAM, map->mask); -} - -/** - * Add a variable entry to @p map. - * - * @param map Hash table to modify. - * @param name Variable name. - * @param value Variable value. - * @param value_len The length of @p value, in bytes. - * - * @retval 0 success - * @retval ENOMEM unable to allocate new entry - */ -int -bhnd_nvram_varmap_add(struct bhnd_nvram_varmap *map, const char *name, - const char *value, size_t value_len) -{ - struct bhnd_nvram_tuples *head; - struct bhnd_nvram_tuple *t; - - /* Locate target bucket */ - head = &map->table[hash32_str(name, HASHINIT) & map->mask]; - - /* Allocate new entry */ - if ((t = bhnd_nvram_tuple_alloc(name, value)) == NULL) - return (ENOMEM); - - /* Remove any existing entry */ - bhnd_nvram_varmap_remove(map, name); - - /* Insert new entry */ - LIST_INSERT_HEAD(head, t, t_link); - return (0); -} - -/** - * Remove @p map in @p tuples, if it exists. - * - * @param map Hash table to modify. - * @param key Key to remove. - * - * @retval 0 success - * @retval ENOENT If @p name is not found in @p map. - */ -int -bhnd_nvram_varmap_remove(struct bhnd_nvram_varmap *map, const char *name) -{ - struct bhnd_nvram_tuples *head; - struct bhnd_nvram_tuple *t; - size_t name_len; - - /* Locate target bucket */ - head = &map->table[hash32_str(name, HASHINIT) & map->mask]; - name_len = strlen(name); - - LIST_FOREACH(t, head, t_link) { - if (t->name_len != name_len) - continue; - - if (strncmp(t->name, name, name_len) != 0) - continue; - - LIST_REMOVE(t, t_link); - bhnd_nvram_tuple_free(t); - return (0); - } - - /* Not found */ - return (ENOENT); -} - -/** - * Search for @p name in @p map. - * - * @param map Hash table to modify. - * @param name Variable name. - * @param name_len Length of @p name, not including trailing NUL. - * - * @retval bhnd_nvram_tuple If @p name is found in @p map. - * @retval NULL If @p name is not found. - */ -struct bhnd_nvram_tuple * -bhnd_nvram_varmap_find(struct bhnd_nvram_varmap *map, const char *name, - size_t name_len) -{ - struct bhnd_nvram_tuples *head; - struct bhnd_nvram_tuple *t; - - head = &map->table[hash32_str(name, HASHINIT) & map->mask]; - - LIST_FOREACH(t, head, t_link) { - if (t->name_len != name_len) - continue; - - if (strncmp(t->name, name, name_len) != 0) - continue; - - /* Match */ - return (t); - } - - /* not found */ - return (NULL); -} - -/** - * Check for @p name in @p map. - * - * @param map Hash table to modify. - * @param name Variable name. - * @param name_len Length of @p name, not including trailing NUL. - * - * @retval true If @p name is found in @p tuples. - * @retval false If @p name is not found. - */ -bool bhnd_nvram_varmap_contains(struct bhnd_nvram_varmap *map, - const char *name, size_t name_len) -{ - return (bhnd_nvram_varmap_find(map, name, name_len) != NULL); -} - -/** - * Allocate a new tuple with @p name and @p value. - * - * @param name Variable name. - * @param value Variable value. - * - * @retval bhnd_nvram_tuple success. - * @retval NULL if allocation fails. - */ -struct bhnd_nvram_tuple * -bhnd_nvram_tuple_alloc(const char *name, const char *value) -{ - struct bhnd_nvram_tuple *t; - - t = malloc(sizeof(*t), M_BHND_NVRAM, M_NOWAIT); - if (t == NULL) - return (NULL); - - t->name_len = strlen(name); - t->name = malloc(t->name_len+1, M_BHND_NVRAM, M_NOWAIT); - - t->value_len = strlen(value); - t->value = malloc(t->value_len+1, M_BHND_NVRAM, M_NOWAIT); - - if (t->name == NULL || t->value == NULL) - goto failed; - - strcpy(t->name, name); - strcpy(t->value, value); - - return (t); - -failed: - if (t->name != NULL) - free(t->name, M_BHND_NVRAM); - - if (t->value != NULL) - free(t->value, M_BHND_NVRAM); - - free(t, M_BHND_NVRAM); - - return (NULL); -} - -void -bhnd_nvram_tuple_free(struct bhnd_nvram_tuple *tuple) -{ - free(tuple->name, M_BHND_NVRAM); - free(tuple->value, M_BHND_NVRAM); - free(tuple, M_BHND_NVRAM); -} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_common.h b/sys/dev/bhnd/nvram/bhnd_nvram_common.h deleted file mode 100644 index 3d25b2b3419..00000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram_common.h +++ /dev/null @@ -1,180 +0,0 @@ -/*- - * 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_COMMON_H_ -#define _BHND_NVRAM_BHND_NVRAM_COMMON_H_ - -#include -#include - -#include "bhnd_nvram.h" - -struct bhnd_nvram_tuple; -struct bhnd_nvram_varmap; - -struct bhnd_nvram_vardefn; - -MALLOC_DECLARE(M_BHND_NVRAM); - -extern const uint8_t bhnd_nvram_crc8_tab[]; - -#define BHND_NVRAM_CRC8_INITIAL 0xFF /**< Initial bhnd_nvram_crc8 value */ -#define BHND_NVRAM_CRC8_VALID 0x9F /**< Valid CRC-8 checksum */ -#define BHND_SPROMREV_MAX UINT8_MAX /**< maximum supported SPROM revision */ - - -/** NVRAM data type string representations */ -typedef enum { - BHND_NVRAM_SFMT_HEX = 1, /**< hex format */ - BHND_NVRAM_SFMT_DEC = 2, /**< decimal format */ - BHND_NVRAM_SFMT_MACADDR = 3, /**< mac address (canonical form, hex octets, - separated with ':') */ - BHND_NVRAM_SFMT_LEDDC = 4, /**< LED PWM duty-cycle (2 bytes -- on/off) */ - BHND_NVRAM_SFMT_CCODE = 5 /**< count code format (2-3 ASCII chars, or hex string) */ -} bhnd_nvram_sfmt; - -size_t bhnd_nvram_type_width(bhnd_nvram_type type); -const char *bhnd_nvram_type_fmt(bhnd_nvram_type type, - bhnd_nvram_sfmt sfmt, size_t elem_num); - -const struct bhnd_nvram_vardefn *bhnd_nvram_find_vardefn(const char *varname); - -bool bhnd_nvram_validate_name(const char *name, - size_t name_len); -int bhnd_nvram_parse_octet_string( - const char *value, size_t value_len, - void *buf, size_t *len, - bhnd_nvram_type type); - -int bhnd_nvram_varmap_init( - struct bhnd_nvram_varmap *map, - size_t nelements, int flags); -void bhnd_nvram_varmap_free( - struct bhnd_nvram_varmap *map); -int bhnd_nvram_varmap_add( - struct bhnd_nvram_varmap *map, - const char *name, const char *value, - size_t value_len); -int bhnd_nvram_varmap_remove( - struct bhnd_nvram_varmap *map, - const char *name); -struct bhnd_nvram_tuple *bhnd_nvram_varmap_find( - struct bhnd_nvram_varmap *map, - const char *name, size_t name_len); -bool bhnd_nvram_varmap_contains( - struct bhnd_nvram_varmap *map, - const char *name, size_t name_len); - -struct bhnd_nvram_tuple *bhnd_nvram_tuple_alloc(const char *name, - const char *value); -void bhnd_nvram_tuple_free( - struct bhnd_nvram_tuple *tuple); - -/** NVRAM variable flags */ -enum { - BHND_NVRAM_VF_ARRAY = (1<<0), /**< variable is an array */ - BHND_NVRAM_VF_MFGINT = (1<<1), /**< mfg-internal variable; should not be externally visible */ - BHND_NVRAM_VF_IGNALL1 = (1<<2) /**< hide variable if its value has all bits set. */ -}; - -/** SPROM revision compatibility declaration */ -struct bhnd_sprom_compat { - uint8_t first; /**< first compatible SPROM revision */ - uint8_t last; /**< last compatible SPROM revision, or BHND_SPROMREV_MAX */ -}; - -/** SPROM value descriptor */ -struct bhnd_sprom_offset { - uint16_t offset; /**< byte offset within SPROM */ - bool cont:1; /**< value should be bitwise OR'd with the - * previous offset descriptor */ - bhnd_nvram_type type:7; /**< data type */ - int8_t shift; /**< shift to be applied to the value */ - uint32_t mask; /**< mask to be applied to the value(s) */ -}; - -/** SPROM-specific variable definition */ -struct bhnd_sprom_vardefn { - struct bhnd_sprom_compat compat; /**< sprom compatibility declaration */ - const struct bhnd_sprom_offset *offsets; /**< offset descriptors */ - size_t num_offsets; /**< number of offset descriptors */ -}; - -/** NVRAM variable definition */ -struct bhnd_nvram_vardefn { - const char *name; /**< variable name */ - bhnd_nvram_type type; /**< base data type */ - bhnd_nvram_sfmt sfmt; /**< string format */ - uint32_t flags; /**< BHND_NVRAM_VF_* flags */ - - const struct bhnd_sprom_vardefn *sp_defs; /**< SPROM-specific variable definitions */ - size_t num_sp_defs; /**< number of sprom definitions */ -}; - -/** - * NVRAM value tuple. - */ -struct bhnd_nvram_tuple { - char *name; /**< variable name. */ - size_t name_len; /**< variable length. */ - char *value; /**< value, or NULL if this tuple represents variable - deletion */ - size_t value_len; /**< value length. */ - - LIST_ENTRY(bhnd_nvram_tuple) t_link; -}; - -LIST_HEAD(bhnd_nvram_tuples, bhnd_nvram_tuple); - -/** NVRAM tuple hash table */ -struct bhnd_nvram_varmap { - struct bhnd_nvram_tuples *table; /**< hash buckets */ - u_long mask; /**< hash index mask */ -}; - -/** - * Calculate CRC-8 over @p buf. - * - * @param buf input buffer - * @param size buffer size - * @param crc last computed crc, or BHND_NVRAM_CRC8_INITIAL - */ -static inline uint8_t -bhnd_nvram_crc8(const void *buf, size_t size, uint8_t crc) -{ - const uint8_t *p = (const uint8_t *)buf; - while (size--) - crc = bhnd_nvram_crc8_tab[(crc ^ *p++)]; - - return (crc); -} - -#endif /* _BHND_NVRAM_BHND_NVRAM_COMMON_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data.c b/sys/dev/bhnd/nvram/bhnd_nvram_data.c new file mode 100644 index 00000000000..87300857bb8 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data.c @@ -0,0 +1,527 @@ +/*- + * 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$"); + + +#ifdef _KERNEL + +#include +#include + +#include + +#else /* !_KERNEL */ + +#include +#include +#include +#include + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" +#include "bhnd_nvram_io.h" + +#include "bhnd_nvram_datavar.h" +#include "bhnd_nvram_data.h" + +/** + * Return a human-readable description for the given NVRAM data class. + * + * @param cls The NVRAM class. + */ +const char * +bhnd_nvram_data_class_desc(bhnd_nvram_data_class_t *cls) +{ + return (cls->desc); +} + +/** + * Probe to see if this NVRAM data class class supports the data mapped by the + * given I/O context, returning a BHND_NVRAM_DATA_PROBE probe result. + * + * @param cls The NVRAM class. + * @param io An I/O context mapping the NVRAM data. + * + * @retval 0 if this is the only possible NVRAM data class for @p io. + * @retval negative if the probe succeeds, a negative value should be returned; + * the class returning the highest negative value should be selected to handle + * NVRAM parsing. + * @retval ENXIO If the NVRAM format is not handled by @p cls. + * @retval positive if an error occurs during probing, a regular unix error + * code should be returned. + */ +int +bhnd_nvram_data_probe(bhnd_nvram_data_class_t *cls, struct bhnd_nvram_io *io) +{ + return (cls->op_probe(io)); +} + +/** + * Probe to see if an NVRAM data class in @p classes supports parsing + * of the data mapped by @p io, returning the parsed data in @p data. + * + * The caller is responsible for deallocating the returned instance via + * bhnd_nvram_data_release(). + * + * @param[out] data On success, the parsed NVRAM data instance. + * @param io An I/O context mapping the NVRAM data to be copied and parsed. + * @param classes An array of NVRAM data classes to be probed, or NULL to + * probe the default supported set. + * @param num_classes The number of NVRAM data classes in @p classes. + * + * @retval 0 success + * @retval ENXIO if no class is found capable of parsing @p io. + * @retval non-zero if an error otherwise occurs during allocation, + * initialization, or parsing of the NVRAM data, a regular unix error code + * will be returned. + */ +int +bhnd_nvram_data_probe_classes(struct bhnd_nvram_data **data, + struct bhnd_nvram_io *io, bhnd_nvram_data_class_t *classes[], + size_t num_classes) +{ + bhnd_nvram_data_class_t *cls; + int error, prio, result; + + cls = NULL; + prio = 0; + *data = NULL; + + /* If class array is NULL, default to our linker set */ + if (classes == NULL) { + classes = SET_BEGIN(bhnd_nvram_data_class_set); + num_classes = SET_COUNT(bhnd_nvram_data_class_set); + } + + /* Try to find the best data class capable of parsing io */ + for (size_t i = 0; i < num_classes; i++) { + bhnd_nvram_data_class_t *next_cls; + + next_cls = classes[i]; + + /* Try to probe */ + result = bhnd_nvram_data_probe(next_cls, io); + + /* The parser did not match if an error was returned */ + if (result > 0) + continue; + + /* Lower priority than previous match; keep + * searching */ + if (cls != NULL && result <= prio) + continue; + + /* Drop any previously parsed data */ + if (*data != NULL) { + bhnd_nvram_data_release(*data); + *data = NULL; + } + + /* If this is a 'maybe' match, attempt actual parsing to + * verify that this does in fact match */ + if (result <= BHND_NVRAM_DATA_PROBE_MAYBE) { + /* If parsing fails, keep searching */ + error = bhnd_nvram_data_new(next_cls, data, io); + if (error) + continue; + } + + /* Record best new match */ + prio = result; + cls = next_cls; + + + /* Terminate search immediately on + * BHND_NVRAM_DATA_PROBE_SPECIFIC */ + if (result == BHND_NVRAM_DATA_PROBE_SPECIFIC) + break; + } + + /* If no match, return error */ + if (cls == NULL) + return (ENXIO); + + /* If the NVRAM data was not parsed above, do so now */ + if (*data == NULL) { + if ((error = bhnd_nvram_data_new(cls, data, io))) + return (error); + } + + return (0); +} + +/** + * Allocate and initialize a new instance of data class @p cls, copying and + * parsing NVRAM data from @p io. + * + * The caller is responsible for releasing the returned parser instance + * reference via bhnd_nvram_data_release(). + * + * @param cls If non-NULL, the data class to be allocated. If NULL, + * bhnd_nvram_data_probe_classes() will be used to determine the data format. + * @param[out] nv 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. + * + * @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_data_new(bhnd_nvram_data_class_t *cls, + struct bhnd_nvram_data **nv, struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_data *data; + int error; + + /* If NULL, try to identify the appropriate class */ + if (cls == NULL) + return (bhnd_nvram_data_probe_classes(nv, io, NULL, 0)); + + /* Allocate new instance */ + BHND_NV_ASSERT(sizeof(struct bhnd_nvram_data) <= cls->size, + ("instance size %zu less than minimum %zu", cls->size, + sizeof(struct bhnd_nvram_data))); + + data = bhnd_nv_calloc(1, cls->size); + data->cls = cls; + refcount_init(&data->refs, 1); + + /* Let the class handle initialization */ + if ((error = cls->op_new(data, io))) { + bhnd_nv_free(data); + return (error); + } + + *nv = data; + return (0); +} + +/** + * Retain and return a reference to the given data instance. + * + * @param nv The reference to be retained. + */ +struct bhnd_nvram_data * +bhnd_nvram_data_retain(struct bhnd_nvram_data *nv) +{ + refcount_acquire(&nv->refs); + return (nv); +} + +/** + * Release a reference to the given data instance. + * + * If this is the last reference, the data instance and its associated + * resources will be freed. + * + * @param nv The reference to be released. + */ +void +bhnd_nvram_data_release(struct bhnd_nvram_data *nv) +{ + if (!refcount_release(&nv->refs)) + return; + + /* Free any internal resources */ + nv->cls->op_free(nv); + + /* Free the instance allocation */ + bhnd_nv_free(nv); +} + +/** + * Return a pointer to @p nv's data class. + * + * @param nv The NVRAM data instance to be queried. + */ +bhnd_nvram_data_class_t * +bhnd_nvram_data_class(struct bhnd_nvram_data *nv) +{ + return (nv->cls); +} + +/** + * Return the number of variables in @p nv. + * + * @param nv The NVRAM data to be queried. + */ +size_t +bhnd_nvram_data_count(struct bhnd_nvram_data *nv) +{ + return (nv->cls->op_count(nv)); +} + +/** + * Compute the size of the serialized form of @p nv. + * + * Serialization may be performed via bhnd_nvram_data_serialize(). + * + * @param nv The NVRAM data to be queried. + * @param[out] len On success, will be set to the computed size. + * + * @retval 0 success + * @retval non-zero if computing the serialized size otherwise fails, a + * regular unix error code will be returned. + */ +int +bhnd_nvram_data_size(struct bhnd_nvram_data *nv, size_t *len) +{ + return (nv->cls->op_size(nv, len)); +} + +/** + * Serialize the NVRAM data to @p buf, using the NVRAM data class' native + * format. + * + * The resulting serialization may be reparsed with @p nv's BHND NVRAM data + * class. + * + * @param nv The NVRAM data to be serialized. + * @param[out] buf On success, the serialed NVRAM data 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 length of the serialized data. + * + * @retval 0 success + * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too + * small to hold the serialized data. + * @retval non-zero If serialization otherwise fails, a regular unix error + * code will be returned. + */ +int +bhnd_nvram_data_serialize(struct bhnd_nvram_data *nv, + void *buf, size_t *len) +{ + return (nv->cls->op_serialize(nv, buf, len)); +} + +/** + * Return the capability flags (@see BHND_NVRAM_DATA_CAP_*) for @p nv. + * + * @param nv The NVRAM data to be queried. + */ +uint32_t +bhnd_nvram_data_caps(struct bhnd_nvram_data *nv) +{ + return (nv->cls->op_caps(nv)); +} + +/** + * Iterate over @p nv, returning the names of subsequent variables. + * + * @param nv The NVRAM data to be iterated. + * @param[in,out] cookiep A pointer to a cookiep value previously returned + * by bhnd_nvram_data_next(), or a NULL value to + * begin iteration. + * + * @return Returns the next variable name, or NULL if there are no more + * variables defined in @p nv. + */ +const char * +bhnd_nvram_data_next(struct bhnd_nvram_data *nv, void **cookiep) +{ + return (nv->cls->op_next(nv, cookiep)); +} + +/** + * Search @p nv for a named variable, returning the variable's opaque reference + * if found, or NULL if unavailable. + * + * The BHND_NVRAM_DATA_CAP_INDEXED capability flag will be returned by + * bhnd_nvram_data_caps() if @p nv supports effecient name-based + * lookups. + * + * @param nv The NVRAM data to search. + * @param name The name to search for. + * + * @retval non-NULL If @p name is found, the opaque cookie value will be + * returned. + * @retval NULL If @p name is not found. + */ +void * +bhnd_nvram_data_find(struct bhnd_nvram_data *nv, const char *name) +{ + return (nv->cls->op_find(nv, name)); +} + +/** + * A generic implementation of bhnd_nvram_data_find(). + * + * This implementation will use bhnd_nvram_data_next() to perform a + * simple O(n) case-insensitve search for @p name. + */ +void * +bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv, const char *name) +{ + const char *next; + void *cookiep; + + cookiep = NULL; + while ((next = bhnd_nvram_data_next(nv, &cookiep))) { + if (strcasecmp(name, next) == 0) + return (cookiep); + } + + /* Not found */ + return (NULL); +} + +/** + * Read a variable and decode as @p type. + * + * @param nv The NVRAM data. + * @param cookiep An NVRAM variable cookie previously returned + * via bhnd_nvram_data_next() or + * bhnd_nvram_data_find(). + * @param[out] 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 data type to be written to @p buf. + * + * @retval 0 success + * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too + * small to hold the requested value. + * @retval EFTYPE If the variable data cannot be coerced to @p type. + * @retval ERANGE If value coercion would overflow @p type. + */ +int +bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, + size_t *len, bhnd_nvram_type type) +{ + return (nv->cls->op_getvar(nv, cookiep, buf, len, type)); +} + +/** + * A generic implementation of bhnd_nvram_data_getvar(). + * + * This implementation will call bhnd_nvram_data_getvar_ptr() to fetch + * a pointer to the variable data and perform data coercion on behalf + * of the caller. + * + * If a variable definition for the requested variable is available via + * bhnd_nvram_find_vardefn(), the definition will be used to provide + * formatting hints to bhnd_nvram_coerce_value(). + */ +int +bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, void *cookiep, + void *outp, size_t *olen, bhnd_nvram_type otype) +{ + bhnd_nvram_val_t val; + const struct bhnd_nvram_vardefn *vdefn; + const bhnd_nvram_val_fmt_t *fmt; + const char *name; + const void *vptr; + bhnd_nvram_type vtype; + size_t vlen; + int error; + + BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR, + ("instance does not advertise READ_PTR support")); + + /* Fetch pointer to our variable data */ + vptr = bhnd_nvram_data_getvar_ptr(nv, cookiep, &vlen, &vtype); + if (vptr == NULL) + return (EINVAL); + + /* Use the NVRAM string support */ + switch (vtype) { + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_STRING_ARRAY: + fmt = &bhnd_nvram_val_bcm_string_fmt; + break; + default: + fmt = NULL; + } + + /* Check the variable definition table for a matching entry; if + * it exists, use it to populate the value format. */ + name = bhnd_nvram_data_getvar_name(nv, cookiep); + vdefn = bhnd_nvram_find_vardefn(name); + if (vdefn != NULL) + fmt = vdefn->fmt; + + /* Attempt value coercion */ + error = bhnd_nvram_val_init(&val, fmt, vptr, vlen, vtype, + BHND_NVRAM_VAL_BORROW_DATA); + if (error) + return (error); + + error = bhnd_nvram_val_encode(&val, outp, olen, otype); + + /* Clean up */ + bhnd_nvram_val_release(&val); + return (error); +} + +/** + * If available and supported by the NVRAM data instance, return a reference + * to the internal buffer containing an entry's variable data, + * + * Note that string values may not be NUL terminated. + * + * @param nv The NVRAM data. + * @param cookiep An NVRAM variable cookie previously returned + * via bhnd_nvram_data_next() or + * bhnd_nvram_data_find(). + * @param[out] len On success, will be set to the actual size of + * the requested value. + * @param[out] type The data type of the entry data. + * + * @retval non-NULL success + * @retval NULL if direct data access is unsupported by @p nv, or + * unavailable for @p cookiep. + */ +const void * +bhnd_nvram_data_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type) +{ + return (nv->cls->op_getvar_ptr(nv, cookiep, len, type)); +} + + +/** + * Return the variable name associated with a given @p cookiep. + * @param nv The NVRAM data to be iterated. + * @param[in,out] cookiep A pointer to a cookiep value previously returned + * via bhnd_nvram_data_next() or + * bhnd_nvram_data_find(). + * + * @return Returns the variable's name. + */ +const char * +bhnd_nvram_data_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) +{ + return (nv->cls->op_getvar_name(nv, cookiep)); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data.h b/sys/dev/bhnd/nvram/bhnd_nvram_data.h new file mode 100644 index 00000000000..ed8098f81c0 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data.h @@ -0,0 +1,137 @@ +/*- + * 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_DATA_H_ +#define _BHND_NVRAM_BHND_NVRAM_DATA_H_ + +#ifdef _KERNEL +#include +#include +#else /* !_KERNEL */ +#include + +#include +#include +#endif /* _KERNEL */ + +#include "bhnd_nvram.h" +#include "bhnd_nvram_io.h" + +/* NVRAM data class */ +typedef struct bhnd_nvram_data_class bhnd_nvram_data_class_t; + +/* NVRAM data instance */ +struct bhnd_nvram_data; + +/** Declare a bhnd_nvram_data_class with name @p _n */ +#define BHND_NVRAM_DATA_CLASS_DECL(_n) \ + extern struct bhnd_nvram_data_class bhnd_nvram_ ## _n ## _class + +BHND_NVRAM_DATA_CLASS_DECL(bcm); +BHND_NVRAM_DATA_CLASS_DECL(bcmraw); +BHND_NVRAM_DATA_CLASS_DECL(tlv); +BHND_NVRAM_DATA_CLASS_DECL(btxt); +BHND_NVRAM_DATA_CLASS_DECL(sprom); + +/** bhnd_nvram_data capabilities */ +enum { + /** Supports efficient lookup of variables by name */ + BHND_NVRAM_DATA_CAP_INDEXED = (1<<0), + + /** Supports direct access to backing buffer */ + BHND_NVRAM_DATA_CAP_READ_PTR = (1<<1), + + /** Supports device path prefixed variables */ + BHND_NVRAM_DATA_CAP_DEVPATHS = (1<<2), +}; + +/** + * A standard set of probe priorities returned by bhnd_nvram_data_probe(). + * + * Priority is defined in ascending order, with 0 being the highest priority. + * Return values greater than zero are interpreted as regular unix error codes. + */ +enum { + BHND_NVRAM_DATA_PROBE_MAYBE = -40, /**< Possible match */ + BHND_NVRAM_DATA_PROBE_DEFAULT = -20, /**< Definite match of a base + OS-supplied data class */ + BHND_NVRAM_DATA_PROBE_SPECIFIC = 0, /**< Terminate search and use + this data class for + parsing */ +}; + +const char *bhnd_nvram_data_class_desc( + bhnd_nvram_data_class_t *cls); + +int bhnd_nvram_data_probe(bhnd_nvram_data_class_t *cls, + struct bhnd_nvram_io *io); +int bhnd_nvram_data_probe_classes( + struct bhnd_nvram_data **data, + struct bhnd_nvram_io *io, + bhnd_nvram_data_class_t *classes[], + size_t num_classes); + +int bhnd_nvram_data_new(bhnd_nvram_data_class_t *cls, + struct bhnd_nvram_data **nv, + struct bhnd_nvram_io *io); + +struct bhnd_nvram_data *bhnd_nvram_data_retain(struct bhnd_nvram_data *nv); +void bhnd_nvram_data_release(struct bhnd_nvram_data *nv); + +bhnd_nvram_data_class_t *bhnd_nvram_data_class(struct bhnd_nvram_data *nv); + +size_t bhnd_nvram_data_count(struct bhnd_nvram_data *nv); + +int bhnd_nvram_data_size(struct bhnd_nvram_data *nv, + size_t *size); + +int bhnd_nvram_data_serialize(struct bhnd_nvram_data *nv, + void *buf, size_t *len); + +uint32_t bhnd_nvram_data_caps(struct bhnd_nvram_data *nv); + +const char *bhnd_nvram_data_next(struct bhnd_nvram_data *nv, + void **cookiep); + +void *bhnd_nvram_data_find(struct bhnd_nvram_data *nv, + const char *name); + +int bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv, + void *cookiep, void *buf, size_t *len, + bhnd_nvram_type type); + +const void *bhnd_nvram_data_getvar_ptr(struct bhnd_nvram_data *nv, + void *cookiep, size_t *len, bhnd_nvram_type *type); + +const char *bhnd_nvram_data_getvar_name(struct bhnd_nvram_data *nv, + void *cookiep); + +#endif /* _BHND_NVRAM_BHND_NVRAM_DATA_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c new file mode 100644 index 00000000000..79cf8bb2aec --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c @@ -0,0 +1,748 @@ +/*- + * Copyright (c) 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 + +#else /* !_KERNEL */ + +#include +#include +#include +#include +#include + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_datavar.h" + +#include "bhnd_nvram_data_bcmreg.h" +#include "bhnd_nvram_data_bcmvar.h" + +/* + * Broadcom NVRAM data class. + * + * The Broadcom NVRAM NUL-delimited ASCII format is used by most + * Broadcom SoCs. + * + * The NVRAM data is encoded as a standard header, followed by series of + * NUL-terminated 'key=value' strings; the end of the stream is denoted + * by a single extra NUL character. + */ + +struct bhnd_nvram_bcm; + +static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_gethdrvar( + struct bhnd_nvram_bcm *bcm, + const char *name); +static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_to_hdrvar( + struct bhnd_nvram_bcm *bcm, + void *cookiep); +static size_t bhnd_nvram_bcm_hdrvar_index( + struct bhnd_nvram_bcm *bcm, + struct bhnd_nvram_bcm_hvar *hvar); +/* + * Set of BCM NVRAM header values that are required to be mirrored in the + * NVRAM data itself. + * + * If they're not included in the parsed NVRAM data, we need to vend the + * header-parsed values with their appropriate keys, and add them in any + * updates to the NVRAM data. + * + * If they're modified in NVRAM, we need to sync the changes with the + * the NVRAM header values. + */ +static const struct bhnd_nvram_bcm_hvar bhnd_nvram_bcm_hvars[] = { + { + .name = BCM_NVRAM_CFG0_SDRAM_INIT_VAR, + .type = BHND_NVRAM_TYPE_UINT16, + .len = sizeof(uint16_t), + .nelem = 1, + }, + { + .name = BCM_NVRAM_CFG1_SDRAM_CFG_VAR, + .type = BHND_NVRAM_TYPE_UINT16, + .len = sizeof(uint16_t), + .nelem = 1, + }, + { + .name = BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR, + .type = BHND_NVRAM_TYPE_UINT16, + .len = sizeof(uint16_t), + .nelem = 1, + }, + { + .name = BCM_NVRAM_SDRAM_NCDL_VAR, + .type = BHND_NVRAM_TYPE_UINT32, + .len = sizeof(uint32_t), + .nelem = 1, + }, +}; + +/** BCM NVRAM data class instance */ +struct bhnd_nvram_bcm { + struct bhnd_nvram_data nv; /**< common instance state */ + struct bhnd_nvram_io *data; /**< backing buffer */ + + /** BCM header values */ + struct bhnd_nvram_bcm_hvar hvars[nitems(bhnd_nvram_bcm_hvars)]; + + size_t count; /**< total variable count */ +}; + +BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", sizeof(struct bhnd_nvram_bcm)) + +static int +bhnd_nvram_bcm_probe(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_bcmhdr hdr; + int error; + + if ((error = bhnd_nvram_io_read(io, 0x0, &hdr, sizeof(hdr)))) + return (error); + + if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC) + return (ENXIO); + + return (BHND_NVRAM_DATA_PROBE_DEFAULT); +} + +/** + * Initialize @p bcm with the provided NVRAM data mapped by @p src. + * + * @param bcm A newly allocated data instance. + */ +static int +bhnd_nvram_bcm_init(struct bhnd_nvram_bcm *bcm, struct bhnd_nvram_io *src) +{ + struct bhnd_nvram_bcmhdr hdr; + uint8_t *p; + void *ptr; + size_t io_offset, io_size; + uint8_t crc, valid; + int error; + + if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr)))) + return (error); + + if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC) + return (ENXIO); + + /* Fetch the actual NVRAM image size */ + io_size = le32toh(hdr.size); + if (io_size < sizeof(hdr)) { + /* The header size must include the header itself */ + BHND_NV_LOG("corrupt header size: %zu\n", io_size); + return (EINVAL); + } + + if (io_size > bhnd_nvram_io_getsize(src)) { + BHND_NV_LOG("header size %zu exceeds input size %zu\n", + io_size, bhnd_nvram_io_getsize(src)); + return (EINVAL); + } + + /* Allocate a buffer large enough to hold the NVRAM image, and + * an extra EOF-signaling NUL (on the chance it's missing from the + * source data) */ + if (io_size == SIZE_MAX) + return (ENOMEM); + + bcm->data = bhnd_nvram_iobuf_empty(io_size, io_size + 1); + if (bcm->data == NULL) + return (ENOMEM); + + /* Fetch a pointer into our backing buffer and copy in the + * NVRAM image. */ + error = bhnd_nvram_io_write_ptr(bcm->data, 0x0, &ptr, io_size, NULL); + if (error) + return (error); + + p = ptr; + if ((error = bhnd_nvram_io_read(src, 0x0, p, io_size))) + return (error); + + /* Verify the CRC */ + valid = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC); + crc = bhnd_nvram_crc8(p + BCM_NVRAM_CRC_SKIP, + io_size - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL); + + if (crc != valid) { + BHND_NV_LOG("warning: NVRAM CRC error (crc=%#hhx, " + "expected=%hhx)\n", crc, valid); + } + + /* Populate header variable definitions */ +#define BCM_READ_HDR_VAR(_name, _dest, _swap) do { \ + struct bhnd_nvram_bcm_hvar *data; \ + data = bhnd_nvram_bcm_gethdrvar(bcm, _name ##_VAR); \ + BHND_NV_ASSERT(data != NULL, \ + ("no such header variable: " __STRING(_name))); \ + \ + \ + data->value. _dest = _swap(BCM_NVRAM_GET_BITS( \ + hdr. _name ## _FIELD, _name)); \ +} while(0) + + BCM_READ_HDR_VAR(BCM_NVRAM_CFG0_SDRAM_INIT, u16, le16toh); + BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_CFG, u16, le16toh); + BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_REFRESH, u16, le16toh); + BCM_READ_HDR_VAR(BCM_NVRAM_SDRAM_NCDL, u32, le32toh); + + _Static_assert(nitems(bcm->hvars) == 4, "missing initialization for" + "NVRAM header variable(s)"); + +#undef BCM_READ_HDR_VAR + + /* Process the buffer */ + bcm->count = 0; + io_offset = sizeof(hdr); + while (io_offset < io_size) { + char *envp; + const char *name, *value; + size_t envp_len; + size_t name_len, value_len; + + /* Parse the key=value string */ + envp = (char *) (p + io_offset); + envp_len = strnlen(envp, io_size - io_offset); + error = bhnd_nvram_parse_env(envp, envp_len, '=', &name, + &name_len, &value, &value_len); + if (error) { + BHND_NV_LOG("error parsing envp at offset %#zx: %d\n", + io_offset, error); + return (error); + } + + /* Insert a '\0' character, replacing the '=' delimiter and + * allowing us to vend references directly to the variable + * name */ + *(envp + name_len) = '\0'; + + /* Record any NVRAM variables that mirror our header variables. + * This is a brute-force search -- for the amount of data we're + * operating on, it shouldn't be an issue. */ + for (size_t i = 0; i < nitems(bcm->hvars); i++) { + struct bhnd_nvram_bcm_hvar *hvar; + union bhnd_nvram_bcm_hvar_value hval; + size_t hval_len; + + hvar = &bcm->hvars[i]; + + /* Already matched? */ + if (hvar->envp != NULL) + continue; + + /* Name matches? */ + if ((strcmp(name, hvar->name)) != 0) + continue; + + /* Save pointer to mirrored envp */ + hvar->envp = envp; + + /* Check for stale value */ + hval_len = sizeof(hval); + error = bhnd_nvram_value_coerce(value, value_len, + BHND_NVRAM_TYPE_STRING, &hval, &hval_len, + hvar->type); + if (error) { + /* If parsing fails, we can likely only make + * things worse by trying to synchronize the + * variables */ + BHND_NV_LOG("error parsing header variable " + "'%s=%s': %d\n", name, value, error); + } else if (hval_len != hvar->len) { + hvar->stale = true; + } else if (memcmp(&hval, &hvar->value, hval_len) != 0) { + hvar->stale = true; + } + } + + /* Seek past the value's terminating '\0' */ + io_offset += envp_len; + if (io_offset == io_size) { + BHND_NV_LOG("missing terminating NUL at offset %#zx\n", + io_offset); + return (EINVAL); + } + + if (*(p + io_offset) != '\0') { + BHND_NV_LOG("invalid terminator '%#hhx' at offset " + "%#zx\n", *(p + io_offset), io_offset); + return (EINVAL); + } + + /* Update variable count */ + bcm->count++; + + /* Seek to the next record */ + if (++io_offset == io_size) { + char ch; + + /* Hit EOF without finding a terminating NUL + * byte; we need to grow our buffer and append + * it */ + io_size++; + if ((error = bhnd_nvram_io_setsize(bcm->data, io_size))) + return (error); + + /* Write NUL byte */ + ch = '\0'; + error = bhnd_nvram_io_write(bcm->data, io_size-1, &ch, + sizeof(ch)); + if (error) + return (error); + } + + /* Check for explicit EOF (encoded as a single empty NUL + * terminated string) */ + if (*(p + io_offset) == '\0') + break; + } + + /* Add non-mirrored header variables to total count variable */ + for (size_t i = 0; i < nitems(bcm->hvars); i++) { + if (bcm->hvars[i].envp == NULL) + bcm->count++; + } + + return (0); +} + +static int +bhnd_nvram_bcm_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_bcm *bcm; + int error; + + bcm = (struct bhnd_nvram_bcm *)nv; + + /* Populate default BCM mirrored header variable set */ + _Static_assert(sizeof(bcm->hvars) == sizeof(bhnd_nvram_bcm_hvars), + "hvar declarations must match bhnd_nvram_bcm_hvars template"); + memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars)); + + /* Parse the BCM input data and initialize our backing + * data representation */ + if ((error = bhnd_nvram_bcm_init(bcm, io))) { + bhnd_nvram_bcm_free(nv); + return (error); + } + + return (0); +} + +static void +bhnd_nvram_bcm_free(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv; + + if (bcm->data != NULL) + bhnd_nvram_io_free(bcm->data); +} + +size_t +bhnd_nvram_bcm_count(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv; + return (bcm->count); +} + +static int +bhnd_nvram_bcm_size(struct bhnd_nvram_data *nv, size_t *size) +{ + return (bhnd_nvram_bcm_serialize(nv, NULL, size)); +} + +static int +bhnd_nvram_bcm_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len) +{ + struct bhnd_nvram_bcm *bcm; + struct bhnd_nvram_bcmhdr hdr; + void *cookiep; + const char *name; + size_t nbytes, limit; + uint8_t crc; + int error; + + bcm = (struct bhnd_nvram_bcm *)nv; + nbytes = 0; + + /* Save the output buffer limit */ + if (buf == NULL) + limit = 0; + else + limit = *len; + + /* Reserve space for the NVRAM header */ + nbytes += sizeof(struct bhnd_nvram_bcmhdr); + + /* Write all variables to the output buffer */ + cookiep = NULL; + while ((name = bhnd_nvram_data_next(nv, &cookiep))) { + uint8_t *outp; + size_t olen; + size_t name_len, val_len; + + if (limit > nbytes) { + outp = (uint8_t *)buf + nbytes; + olen = limit - nbytes; + } else { + outp = NULL; + olen = 0; + } + + /* Determine length of variable name */ + name_len = strlen(name) + 1; + + /* Write the variable name and '=' delimiter */ + if (olen >= name_len) { + /* Copy name */ + memcpy(outp, name, name_len - 1); + + /* Append '=' */ + *(outp + name_len - 1) = '='; + } + + /* Adjust byte counts */ + if (SIZE_MAX - name_len < nbytes) + return (ERANGE); + + nbytes += name_len; + + /* Reposition output */ + if (limit > nbytes) { + outp = (uint8_t *)buf + nbytes; + olen = limit - nbytes; + } else { + outp = NULL; + olen = 0; + } + + /* Coerce to NUL-terminated C string, writing to the output + * buffer (or just calculating the length if outp is NULL) */ + val_len = olen; + error = bhnd_nvram_data_getvar(nv, cookiep, outp, &val_len, + BHND_NVRAM_TYPE_STRING); + + if (error && error != ENOMEM) + return (error); + + /* Adjust byte counts */ + if (SIZE_MAX - val_len < nbytes) + return (ERANGE); + + nbytes += val_len; + } + + /* Write terminating NUL */ + if (nbytes < limit) + *((uint8_t *)buf + nbytes) = '\0'; + nbytes++; + + /* Provide actual size */ + *len = nbytes; + if (buf == NULL || nbytes > limit) { + if (buf != NULL) + return (ENOMEM); + + return (0); + } + + /* Fetch current NVRAM header */ + if ((error = bhnd_nvram_io_read(bcm->data, 0x0, &hdr, sizeof(hdr)))) + return (error); + + /* Update values covered by CRC and write to output buffer */ + hdr.size = htole32(*len); + memcpy(buf, &hdr, sizeof(hdr)); + + /* Calculate new CRC */ + crc = bhnd_nvram_crc8((uint8_t *)buf + BCM_NVRAM_CRC_SKIP, + *len - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL); + + /* Update header with valid CRC */ + hdr.cfg0 &= ~BCM_NVRAM_CFG0_CRC_MASK; + hdr.cfg0 |= (crc << BCM_NVRAM_CFG0_CRC_SHIFT); + memcpy(buf, &hdr, sizeof(hdr)); + + return (0); +} + +static uint32_t +bhnd_nvram_bcm_caps(struct bhnd_nvram_data *nv) +{ + return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); +} + +static const char * +bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep) +{ + struct bhnd_nvram_bcm *bcm; + struct bhnd_nvram_bcm_hvar *hvar, *hvar_next; + const void *ptr; + const char *envp, *basep; + size_t io_size, io_offset; + int error; + + bcm = (struct bhnd_nvram_bcm *)nv; + + io_offset = sizeof(struct bhnd_nvram_bcmhdr); + io_size = bhnd_nvram_io_getsize(bcm->data) - io_offset; + + /* Map backing buffer */ + error = bhnd_nvram_io_read_ptr(bcm->data, io_offset, &ptr, io_size, + NULL); + if (error) { + BHND_NV_LOG("error mapping backing buffer: %d\n", error); + return (NULL); + } + + basep = ptr; + + /* If cookiep pointers into our header variable array, handle as header + * variable iteration. */ + hvar = bhnd_nvram_bcm_to_hdrvar(bcm, *cookiep); + if (hvar != NULL) { + size_t idx; + + /* Advance to next entry, if any */ + idx = bhnd_nvram_bcm_hdrvar_index(bcm, hvar) + 1; + + /* Find the next header-defined variable that isn't defined in + * the NVRAM data, start iteration there */ + for (size_t i = idx; i < nitems(bcm->hvars); i++) { + hvar_next = &bcm->hvars[i]; + if (hvar_next->envp != NULL && !hvar_next->stale) + continue; + + *cookiep = hvar_next; + return (hvar_next->name); + } + + /* No further header-defined variables; iteration + * complete */ + return (NULL); + } + + /* Handle standard NVRAM data iteration */ + if (*cookiep == NULL) { + /* Start at the first NVRAM data record */ + envp = basep; + } else { + /* Seek to next record */ + envp = *cookiep; + envp += strlen(envp) + 1; /* key + '\0' */ + envp += strlen(envp) + 1; /* value + '\0' */ + } + + /* + * Skip entries that have an existing header variable entry that takes + * precedence over the NVRAM data value. + * + * The header's value will be provided when performing header variable + * iteration + */ + while ((size_t)(envp - basep) < io_size && *envp != '\0') { + /* Locate corresponding header variable */ + hvar = NULL; + for (size_t i = 0; i < nitems(bcm->hvars); i++) { + if (bcm->hvars[i].envp != envp) + continue; + + hvar = &bcm->hvars[i]; + break; + } + + /* If no corresponding hvar entry, or the entry does not take + * precedence over this NVRAM value, we can safely return this + * value as-is. */ + if (hvar == NULL || !hvar->stale) + break; + + /* Seek to next record */ + envp += strlen(envp) + 1; /* key + '\0' */ + envp += strlen(envp) + 1; /* value + '\0' */ + } + + /* On NVRAM data EOF, try switching to header variables */ + if ((size_t)(envp - basep) == io_size || *envp == '\0') { + /* Find first valid header variable */ + for (size_t i = 0; i < nitems(bcm->hvars); i++) { + if (bcm->hvars[i].envp != NULL) + continue; + + *cookiep = &bcm->hvars[i]; + return (bcm->hvars[i].name); + } + + /* No header variables */ + return (NULL); + } + + *cookiep = (void *)(uintptr_t)envp; + return (envp); +} + +static void * +bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name) +{ + return (bhnd_nvram_data_generic_find(nv, name)); +} + +static int +bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, + size_t *len, bhnd_nvram_type type) +{ + return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); +} + +static const void * +bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type) +{ + struct bhnd_nvram_bcm *bcm; + struct bhnd_nvram_bcm_hvar *hvar; + const char *envp; + + bcm = (struct bhnd_nvram_bcm *)nv; + + /* Handle header variables */ + if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) { + BHND_NV_ASSERT( + hvar->len % bhnd_nvram_value_size(hvar->type, NULL, 0, + hvar->nelem) == 0, + ("length is not aligned to type width")); + + *type = hvar->type; + *len = hvar->len; + return (&hvar->value); + } + + /* Cookie points to key\0value\0 -- get the value address */ + BHND_NV_ASSERT(cookiep != NULL, ("NULL cookiep")); + + envp = cookiep; + envp += strlen(envp) + 1; /* key + '\0' */ + *len = strlen(envp) + 1; /* value + '\0' */ + *type = BHND_NVRAM_TYPE_STRING; + + return (envp); +} + +static const char * +bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) +{ + struct bhnd_nvram_bcm *bcm; + struct bhnd_nvram_bcm_hvar *hvar; + + bcm = (struct bhnd_nvram_bcm *)nv; + + /* Handle header variables */ + if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) { + return (hvar->name); + } + + /* Cookie points to key\0value\0 */ + return (cookiep); +} + +/** + * Return the internal BCM data reference for a header-defined variable + * with @p name, or NULL if none exists. + */ +static struct bhnd_nvram_bcm_hvar * +bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm *bcm, const char *name) +{ + for (size_t i = 0; i < nitems(bcm->hvars); i++) { + if (strcmp(bcm->hvars[i].name, name) == 0) + return (&bcm->hvars[i]); + } + + /* Not found */ + return (NULL); +} + +/** + * If @p cookiep references a header-defined variable, return the + * internal BCM data reference. Otherwise, returns NULL. + */ +static struct bhnd_nvram_bcm_hvar * +bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm *bcm, void *cookiep) +{ +#ifdef BHND_NVRAM_INVARIANTS + uintptr_t base, ptr; +#endif + + /* If the cookie falls within the hvar array, it's a + * header variable cookie */ + if (nitems(bcm->hvars) == 0) + return (NULL); + + if (cookiep < (void *)&bcm->hvars[0]) + return (NULL); + + if (cookiep > (void *)&bcm->hvars[nitems(bcm->hvars)-1]) + return (NULL); + +#ifdef BHND_NVRAM_INVARIANTS + base = (uintptr_t)bcm->hvars; + ptr = (uintptr_t)cookiep; + + BHND_NV_ASSERT((ptr - base) % sizeof(bcm->hvars[0]) == 0, + ("misaligned hvar pointer %p/%p", cookiep, bcm->hvars)); +#endif /* INVARIANTS */ + + return ((struct bhnd_nvram_bcm_hvar *)cookiep); +} + +/** + * Return the index of @p hdrvar within @p bcm's backing hvars array. + */ +static size_t +bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm *bcm, + struct bhnd_nvram_bcm_hvar *hdrvar) +{ + BHND_NV_ASSERT(bhnd_nvram_bcm_to_hdrvar(bcm, (void *)hdrvar) != NULL, + ("%p is not a valid hdrvar reference", hdrvar)); + + return (hdrvar - &bcm->hvars[0]); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c new file mode 100644 index 00000000000..f673fa19f2e --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c @@ -0,0 +1,380 @@ +/*- + * Copyright (c) 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$"); + +#ifdef _KERNEL + +#include +#include +#include +#include + +#else /* !_KERNEL */ + +#include +#include +#include +#include +#include + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_datavar.h" + +/* + * Broadcom-RAW NVRAM data class. + * + * The Broadcom NVRAM NUL-delimited ASCII format is used by most + * Broadcom SoCs. + * + * The NVRAM data is encoded as a stream of of NUL-terminated 'key=value' + * strings; the end of the stream is denoted by a single extra NUL character. + */ + +struct bhnd_nvram_bcmraw; + +/** BCM-RAW NVRAM data class instance */ +struct bhnd_nvram_bcmraw { + struct bhnd_nvram_data nv; /**< common instance state */ + char *data; /**< backing buffer */ + size_t size; /**< buffer size */ + size_t count; /**< variable count */ +}; + +BHND_NVRAM_DATA_CLASS_DEFN(bcmraw, "Broadcom (RAW)", + sizeof(struct bhnd_nvram_bcmraw)) + +static int +bhnd_nvram_bcmraw_probe(struct bhnd_nvram_io *io) +{ + char envp[16]; + size_t envp_len; + int error; + + /* + * Fetch the initial bytes to try to identify BCM data. + * + * We always assert a low probe priority, as we only scan the initial + * bytes of the file. + */ + envp_len = bhnd_nv_ummin(sizeof(envp), bhnd_nvram_io_getsize(io)); + if ((error = bhnd_nvram_io_read(io, 0x0, envp, envp_len))) + return (error); + + /* A zero-length BCM-RAW buffer should contain a single terminating + * NUL */ + if (envp_len == 0) + return (ENXIO); + + if (envp_len == 1) { + if (envp[0] != '\0') + return (ENXIO); + + return (BHND_NVRAM_DATA_PROBE_MAYBE); + } + + /* Don't match on non-ASCII, non-printable data */ + for (size_t i = 0; i < envp_len; i++) { + char c = envp[i]; + if (envp[i] == '\0') + break; + + if (!bhnd_nv_isprint(c)) + return (ENXIO); + } + + /* The first character should be a valid key char */ + if (!bhnd_nv_isalpha(envp[0])) + return (ENXIO); + + return (BHND_NVRAM_DATA_PROBE_MAYBE); +} + +/** + * Initialize @p bcm with the provided NVRAM data mapped by @p src. + * + * @param bcm A newly allocated data instance. + */ +static int +bhnd_nvram_bcmraw_init(struct bhnd_nvram_bcmraw *bcm, struct bhnd_nvram_io *src) +{ + size_t io_size; + size_t capacity, offset; + int error; + + /* Fetch the input image size */ + io_size = bhnd_nvram_io_getsize(src); + + /* Allocate a buffer large enough to hold the NVRAM image, and + * an extra EOF-signaling NUL (on the chance it's missing from the + * source data) */ + if (io_size == SIZE_MAX) + return (ENOMEM); + + capacity = io_size + 1 /* room for extra NUL */; + bcm->size = io_size; + if ((bcm->data = bhnd_nv_malloc(capacity)) == NULL) + return (ENOMEM); + + /* Copy in the NVRAM image */ + if ((error = bhnd_nvram_io_read(src, 0x0, bcm->data, io_size))) + return (error); + + /* Process the buffer */ + bcm->count = 0; + for (offset = 0; offset < bcm->size; offset++) { + char *envp; + const char *name, *value; + size_t envp_len; + size_t name_len, value_len; + + /* Parse the key=value string */ + envp = (char *) (bcm->data + offset); + envp_len = strnlen(envp, bcm->size - offset); + error = bhnd_nvram_parse_env(envp, envp_len, '=', &name, + &name_len, &value, &value_len); + if (error) { + BHND_NV_LOG("error parsing envp at offset %#zx: %d\n", + offset, error); + return (error); + } + + /* Insert a '\0' character, replacing the '=' delimiter and + * allowing us to vend references directly to the variable + * name */ + *(envp + name_len) = '\0'; + + /* Add to variable count */ + bcm->count++; + + /* Seek past the value's terminating '\0' */ + offset += envp_len; + if (offset == io_size) { + BHND_NV_LOG("missing terminating NUL at offset %#zx\n", + offset); + return (EINVAL); + } + + /* If we hit EOF without finding a terminating NUL + * byte, we need to append it */ + if (++offset == bcm->size) { + BHND_NV_ASSERT(offset < capacity, + ("appending past end of buffer")); + bcm->size++; + *(bcm->data + offset) = '\0'; + } + + /* Check for explicit EOF (encoded as a single empty NUL + * terminated string) */ + if (*(bcm->data + offset) == '\0') + break; + } + + /* Reclaim any unused space in he backing buffer */ + if (offset < bcm->size) { + bcm->data = bhnd_nv_reallocf(bcm->data, bcm->size); + if (bcm->data == NULL) + return (ENOMEM); + } + + return (0); +} + +static int +bhnd_nvram_bcmraw_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_bcmraw *bcm; + int error; + + bcm = (struct bhnd_nvram_bcmraw *)nv; + + /* Parse the BCM input data and initialize our backing + * data representation */ + if ((error = bhnd_nvram_bcmraw_init(bcm, io))) { + bhnd_nvram_bcmraw_free(nv); + return (error); + } + + return (0); +} + +static void +bhnd_nvram_bcmraw_free(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv; + + if (bcm->data != NULL) + bhnd_nv_free(bcm->data); +} + +static size_t +bhnd_nvram_bcmraw_count(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv; + + return (bcm->count); +} + +static int +bhnd_nvram_bcmraw_size(struct bhnd_nvram_data *nv, size_t *size) +{ + return (bhnd_nvram_bcmraw_serialize(nv, NULL, size)); +} + +static int +bhnd_nvram_bcmraw_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len) +{ + struct bhnd_nvram_bcmraw *bcm; + char * const p = (char *)buf; + size_t limit; + size_t offset; + + bcm = (struct bhnd_nvram_bcmraw *)nv; + + /* Save the output buffer limit */ + if (buf == NULL) + limit = 0; + else + limit = *len; + + /* The serialized form will be exactly the length + * of our backing buffer representation */ + *len = bcm->size; + + /* Skip serialization if not requested, or report ENOMEM if + * buffer is too small */ + if (buf == NULL) { + return (0); + } else if (*len > limit) { + return (ENOMEM); + } + + /* Write all variables to the output buffer */ + memcpy(buf, bcm->data, *len); + + /* Rewrite all '\0' delimiters back to '=' */ + offset = 0; + while (offset < bcm->size) { + size_t name_len, value_len; + + name_len = strlen(p + offset); + + /* EOF? */ + if (name_len == 0) { + BHND_NV_ASSERT(*(p + offset) == '\0', + ("no NUL terminator")); + + offset++; + break; + } + + /* Rewrite 'name\0' to 'name=' */ + offset += name_len; + BHND_NV_ASSERT(*(p + offset) == '\0', ("incorrect offset")); + + *(p + offset) = '='; + offset++; + + value_len = strlen(p + offset); + offset += value_len + 1; + } + + return (0); +} + +static uint32_t +bhnd_nvram_bcmraw_caps(struct bhnd_nvram_data *nv) +{ + return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); +} + +static const char * +bhnd_nvram_bcmraw_next(struct bhnd_nvram_data *nv, void **cookiep) +{ + struct bhnd_nvram_bcmraw *bcm; + const char *envp; + + bcm = (struct bhnd_nvram_bcmraw *)nv; + + if (*cookiep == NULL) { + /* Start at the first NVRAM data record */ + envp = bcm->data; + } else { + /* Seek to next record */ + envp = *cookiep; + envp += strlen(envp) + 1; /* key + '\0' */ + envp += strlen(envp) + 1; /* value + '\0' */ + } + + /* EOF? */ + if (*envp == '\0') + return (NULL); + + *cookiep = (void *)(uintptr_t)envp; + return (envp); +} + +static void * +bhnd_nvram_bcmraw_find(struct bhnd_nvram_data *nv, const char *name) +{ + return (bhnd_nvram_data_generic_find(nv, name)); +} + +static int +bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, + size_t *len, bhnd_nvram_type type) +{ + return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); +} + +static const void * +bhnd_nvram_bcmraw_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type) +{ + const char *envp; + + /* Cookie points to key\0value\0 -- get the value address */ + envp = cookiep; + envp += strlen(envp) + 1; /* key + '\0' */ + *len = strlen(envp) + 1; /* value + '\0' */ + *type = BHND_NVRAM_TYPE_STRING; + + return (envp); +} + +static const char * +bhnd_nvram_bcmraw_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) +{ + /* Cookie points to key\0value\0 */ + return (cookiep); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h new file mode 100644 index 00000000000..ee458338602 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h @@ -0,0 +1,73 @@ +/*- + * 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_BCMREG_H_ +#define _BHND_NVRAM_BHND_NVRAM_BCMREG_H_ + +#define BCM_NVRAM_GET_BITS(_value, _field) \ + ((_value & _field ## _MASK) >> _field ## _SHIFT) + +/* BCM NVRAM header fields */ +#define BCM_NVRAM_MAGIC 0x48534C46 /* 'FLSH' */ +#define BCM_NVRAM_VERSION 1 + +#define BCM_NVRAM_CRC_SKIP 9 /* skip magic, size, and crc8 */ + +#define BCM_NVRAM_CFG0_CRC_MASK 0x000000FF +#define BCM_NVRAM_CFG0_CRC_SHIFT 0 +#define BCM_NVRAM_CFG0_VER_MASK 0x0000FF00 +#define BCM_NVRAM_CFG0_VER_SHIFT 8 + +#define BCM_NVRAM_CFG0_SDRAM_INIT_FIELD cfg0 +#define BCM_NVRAM_CFG0_SDRAM_INIT_MASK 0xFFFF0000 +#define BCM_NVRAM_CFG0_SDRAM_INIT_SHIFT 16 +#define BCM_NVRAM_CFG0_SDRAM_INIT_VAR "sdram_init" +#define BCM_NVRAM_CFG0_SDRAM_INIT_FMT "0x%04x" + +#define BCM_NVRAM_CFG1_SDRAM_CFG_FIELD cfg1 +#define BCM_NVRAM_CFG1_SDRAM_CFG_MASK 0x0000FFFF +#define BCM_NVRAM_CFG1_SDRAM_CFG_SHIFT 0 +#define BCM_NVRAM_CFG1_SDRAM_CFG_VAR "sdram_config" +#define BCM_NVRAM_CFG1_SDRAM_CFG_FMT "0x%04x" + +#define BCM_NVRAM_CFG1_SDRAM_REFRESH_FIELD cfg1 +#define BCM_NVRAM_CFG1_SDRAM_REFRESH_MASK 0xFFFF0000 +#define BCM_NVRAM_CFG1_SDRAM_REFRESH_SHIFT 16 +#define BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR "sdram_refresh" +#define BCM_NVRAM_CFG1_SDRAM_REFRESH_FMT "0x%04x" + +#define BCM_NVRAM_SDRAM_NCDL_FIELD sdram_ncdl +#define BCM_NVRAM_SDRAM_NCDL_MASK UINT32_MAX +#define BCM_NVRAM_SDRAM_NCDL_SHIFT 0 +#define BCM_NVRAM_SDRAM_NCDL_VAR "sdram_ncdl" +#define BCM_NVRAM_SDRAM_NCDL_FMT "0x%08x" + +#endif /* _BHND_NVRAM_BHND_NVRAM_BCMREG_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_sprom_parservar.h b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h similarity index 55% rename from sys/dev/bhnd/nvram/bhnd_sprom_parservar.h rename to sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h index 7e519f829dc..ab5b2a44e60 100644 --- a/sys/dev/bhnd/nvram/bhnd_sprom_parservar.h +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,40 +25,46 @@ * 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_SPROM_PARSERVAR_H_ -#define _BHND_NVRAM_SPROM_PARSERVAR_H_ +#ifndef _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ +#define _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ -#include "bhnd_sprom_parser.h" +/** + * BCM NVRAM header value data. + */ +union bhnd_nvram_bcm_hvar_value { + uint16_t u16; + uint32_t u32; +}; -#define SPROM_SZ_R1_3 128 /**< SPROM image size (rev 1-3) */ -#define SPROM_SZ_R4_8_9 440 /**< SPROM image size (rev 4, 8-9) */ -#define SPROM_SZ_R10 460 /**< SPROM image size (rev 10) */ -#define SPROM_SZ_R11 468 /**< SPROM image size (rev 11) */ +/** + * Internal representation of BCM NVRAM values that mirror (and must be + * vended as) NVRAM variables. + */ +struct bhnd_nvram_bcm_hvar { + const char *name; /**< variable name */ + bhnd_nvram_type type; /**< value type */ + size_t nelem; /**< value element count */ + size_t len; /**< value length */ + const char *envp; /**< Pointer to the NVRAM variable mirroring + this header value, or NULL. */ + bool stale; /**< header value does not match + mirrored NVRAM value */ -/** Maximum supported SPROM image size */ -#define SPROM_SZ_MAX SPROM_SZ_R11 + /** variable data */ + union bhnd_nvram_bcm_hvar_value value; +}; + +/** BCM NVRAM header */ +struct bhnd_nvram_bcmhdr { + uint32_t magic; + uint32_t size; + uint32_t cfg0; /**< crc:8, version:8, sdram_init:16 */ + uint32_t cfg1; /**< sdram_config:16, sdram_refresh:16 */ + uint32_t sdram_ncdl; /**< sdram_ncdl */ +} __packed; -#define SPROM_SIG_NONE 0x0 -#define SPROM_SIG_NONE_OFF 0x0 - -/** SPROM signature (rev 4) */ -#define SPROM_SIG_R4 0x5372 -#define SPROM_SIG_R4_OFF 64 /**< SPROM signature offset (rev 4) */ - -/** SPROM signature (rev 8, 9) */ -#define SPROM_SIG_R8_9 SPROM_SIG_R4 -#define SPROM_SIG_R8_9_OFF 128 /**< SPROM signature offset (rev 8-9) */ - -/** SPROM signature (rev 10) */ -#define SPROM_SIG_R10 SPROM_SIG_R4 -#define SPROM_SIG_R10_OFF 438 /**< SPROM signature offset (rev 10) */ - -/** SPROM signature (rev 11) */ -#define SPROM_SIG_R11 0x0634 -#define SPROM_SIG_R11_OFF 128 /**< SPROM signature offset (rev 11) */ - -#endif /* _BHND_NVRAM_SPROM_PARSERVAR_H_ */ +#endif /* _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c b/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c new file mode 100644 index 00000000000..efd68a276a7 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c @@ -0,0 +1,586 @@ +/*- + * 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 + +#else /* !_KERNEL */ + +#include +#include +#include +#include + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_datavar.h" + +#include "bhnd_nvram_data_bcmreg.h" /* for BCM_NVRAM_MAGIC */ + +/** + * Broadcom "Board Text" data class. + * + * This format is used to provide external NVRAM data for some + * fullmac WiFi devices, and as an input format when programming + * NVRAM/SPROM/OTP. + */ + +struct bhnd_nvram_btxt { + struct bhnd_nvram_data nv; /**< common instance state */ + struct bhnd_nvram_io *data; /**< memory-backed board text data */ + size_t count; /**< variable count */ +}; + +BHND_NVRAM_DATA_CLASS_DEFN(btxt, "Broadcom Board Text", + sizeof(struct bhnd_nvram_btxt)) + +/** Minimal identification header */ +union bhnd_nvram_btxt_ident { + uint32_t bcm_magic; + char btxt[8]; +}; + +static size_t bhnd_nvram_btxt_io_offset(struct bhnd_nvram_btxt *btxt, + void *cookiep); + +static int bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io, + size_t offset, size_t *line_len, size_t *env_len); +static int bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io, + size_t *offset); +static int bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io, + size_t *offset); + +static int +bhnd_nvram_btxt_probe(struct bhnd_nvram_io *io) +{ + union bhnd_nvram_btxt_ident ident; + char c; + int error; + + /* Look at the initial header for something that looks like + * an ASCII board text file */ + if ((error = bhnd_nvram_io_read(io, 0x0, &ident, sizeof(ident)))) + return (error); + + /* The BCM NVRAM format uses a 'FLSH' little endian magic value, which + * shouldn't be interpreted as BTXT */ + if (le32toh(ident.bcm_magic) == BCM_NVRAM_MAGIC) + return (ENXIO); + + /* Don't match on non-ASCII/non-printable data */ + for (size_t i = 0; i < nitems(ident.btxt); i++) { + c = ident.btxt[i]; + if (!bhnd_nv_isprint(c)) + return (ENXIO); + } + + /* The first character should either be a valid key char (alpha), + * whitespace, or the start of a comment ('#') */ + c = ident.btxt[0]; + if (!bhnd_nv_isspace(c) && !bhnd_nv_isalpha(c) && c != '#') + return (ENXIO); + + /* We assert a low priority, given that we've only scanned an + * initial few bytes of the file. */ + return (BHND_NVRAM_DATA_PROBE_MAYBE); +} + +/** + * Initialize @p btxt with the provided board text data mapped by @p src. + * + * @param btxt A newly allocated data instance. + */ +static int +bhnd_nvram_btxt_init(struct bhnd_nvram_btxt *btxt, struct bhnd_nvram_io *src) +{ + const void *ptr; + const char *name, *value; + size_t name_len, value_len; + size_t line_len, env_len; + size_t io_offset, io_size, str_size; + int error; + + BHND_NV_ASSERT(btxt->data == NULL, ("btxt data already allocated")); + + if ((btxt->data = bhnd_nvram_iobuf_copy(src)) == NULL) + return (ENOMEM); + + io_size = bhnd_nvram_io_getsize(btxt->data); + io_offset = 0; + + /* Fetch a pointer mapping the entirity of the board text data */ + error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_size, NULL); + if (error) + return (error); + + /* Determine the actual size, minus any terminating NUL. We + * parse NUL-terminated C strings, but do not include NUL termination + * in our internal or serialized representations */ + str_size = strnlen(ptr, io_size); + + /* If the terminating NUL is not found at the end of the buffer, + * this is BCM-RAW or other NUL-delimited NVRAM format. */ + if (str_size < io_size && str_size + 1 < io_size) + return (EINVAL); + + /* Adjust buffer size to account for NUL termination (if any) */ + io_size = str_size; + if ((error = bhnd_nvram_io_setsize(btxt->data, io_size))) + return (error); + + /* Process the buffer */ + btxt->count = 0; + while (io_offset < io_size) { + const void *envp; + + /* Seek to the next key=value entry */ + if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset))) + return (error); + + /* Determine the entry and line length */ + error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset, + &line_len, &env_len); + if (error) + return (error); + + /* EOF? */ + if (env_len == 0) { + BHND_NV_ASSERT(io_offset == io_size, + ("zero-length record returned from " + "bhnd_nvram_btxt_seek_next()")); + break; + } + + /* Fetch a pointer to the line start */ + error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &envp, + env_len, NULL); + if (error) + return (error); + + /* Parse the key=value string */ + error = bhnd_nvram_parse_env(envp, env_len, '=', &name, + &name_len, &value, &value_len); + if (error) { + return (error); + } + + /* Insert a '\0' character, replacing the '=' delimiter and + * allowing us to vend references directly to the variable + * name */ + error = bhnd_nvram_io_write(btxt->data, io_offset+name_len, + &(char){'\0'}, 1); + if (error) + return (error); + + /* Add to variable count */ + btxt->count++; + + /* Advance past EOL */ + io_offset += line_len; + } + + return (0); +} + +static int +bhnd_nvram_btxt_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_btxt *btxt; + int error; + + /* Allocate and initialize the BTXT data instance */ + btxt = (struct bhnd_nvram_btxt *)nv; + + /* Parse the BTXT input data and initialize our backing + * data representation */ + if ((error = bhnd_nvram_btxt_init(btxt, io))) { + bhnd_nvram_btxt_free(nv); + return (error); + } + + return (0); +} + +static void +bhnd_nvram_btxt_free(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv; + if (btxt->data != NULL) + bhnd_nvram_io_free(btxt->data); +} + +size_t +bhnd_nvram_btxt_count(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv; + return (btxt->count); +} + +static int +bhnd_nvram_btxt_size(struct bhnd_nvram_data *nv, size_t *size) +{ + struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv; + + /* The serialized form will be identical in length + * to our backing buffer representation */ + *size = bhnd_nvram_io_getsize(btxt->data); + return (0); +} + +static int +bhnd_nvram_btxt_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len) +{ + struct bhnd_nvram_btxt *btxt; + size_t limit; + int error; + + btxt = (struct bhnd_nvram_btxt *)nv; + + limit = *len; + + /* Provide actual output size */ + if ((error = bhnd_nvram_data_size(nv, len))) + return (error); + + if (buf == NULL) { + return (0); + } else if (limit < *len) { + return (ENOMEM); + } + + /* Copy our internal representation to the output buffer */ + if ((error = bhnd_nvram_io_read(btxt->data, 0x0, buf, *len))) + return (error); + + /* Restore the original key=value format, rewriting all '\0' + * key\0value delimiters back to '=' */ + for (char *p = buf; (size_t)(p - (char *)buf) < *len; p++) { + if (*p == '\0') + *p = '='; + } + + return (0); +} + +static uint32_t +bhnd_nvram_btxt_caps(struct bhnd_nvram_data *nv) +{ + return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); +} + +static void * +bhnd_nvram_btxt_find(struct bhnd_nvram_data *nv, const char *name) +{ + return (bhnd_nvram_data_generic_find(nv, name)); +} + +static const char * +bhnd_nvram_btxt_next(struct bhnd_nvram_data *nv, void **cookiep) +{ + struct bhnd_nvram_btxt *btxt; + const void *nptr; + size_t io_offset, io_size; + int error; + + btxt = (struct bhnd_nvram_btxt *)nv; + + io_size = bhnd_nvram_io_getsize(btxt->data); + io_offset = bhnd_nvram_btxt_io_offset(btxt, *cookiep); + + /* Already at EOF? */ + if (io_offset == io_size) + return (NULL); + + /* Seek to the next entry (if any) */ + if ((error = bhnd_nvram_btxt_seek_eol(btxt->data, &io_offset))) { + BHND_NV_LOG("unexpected error in seek_eol(): %d\n", error); + return (NULL); + } + + if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset))) { + BHND_NV_LOG("unexpected error in seek_next(): %d\n", error); + return (NULL); + } + + /* Provide the new cookie for this offset */ + if (io_offset > UINTPTR_MAX) { + BHND_NV_LOG("io_offset > UINPTR_MAX!\n"); + return (NULL); + } + + *cookiep = (void *)(uintptr_t)io_offset; + + /* Hit EOF? */ + if (io_offset == io_size) + return (NULL); + + /* Fetch the name pointer; it must be at least 1 byte long */ + error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &nptr, 1, NULL); + if (error) { + BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error); + return (NULL); + } + + /* Return the name pointer */ + return (nptr); +} + +static int +bhnd_nvram_btxt_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, + size_t *len, bhnd_nvram_type type) +{ + return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); +} + +const void * +bhnd_nvram_btxt_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type) +{ + struct bhnd_nvram_btxt *btxt; + const void *eptr; + const char *vptr; + size_t io_offset, io_size; + size_t line_len, env_len; + int error; + + btxt = (struct bhnd_nvram_btxt *)nv; + + io_size = bhnd_nvram_io_getsize(btxt->data); + io_offset = bhnd_nvram_btxt_io_offset(btxt, cookiep); + + /* At EOF? */ + if (io_offset == io_size) + return (NULL); + + /* Determine the entry length */ + error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset, &line_len, + &env_len); + if (error) { + BHND_NV_LOG("unexpected error in entry_len(): %d\n", error); + return (NULL); + } + + /* Fetch the entry's value pointer and length */ + error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &eptr, env_len, + NULL); + if (error) { + BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error); + return (NULL); + } + + error = bhnd_nvram_parse_env(eptr, env_len, '\0', NULL, NULL, &vptr, + len); + if (error) { + BHND_NV_LOG("unexpected error in parse_env(): %d\n", error); + return (NULL); + } + + /* Type is always CSTR */ + *type = BHND_NVRAM_TYPE_STRING; + + return (vptr); +} + +static const char * +bhnd_nvram_btxt_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) +{ + struct bhnd_nvram_btxt *btxt; + const void *ptr; + size_t io_offset, io_size; + int error; + + btxt = (struct bhnd_nvram_btxt *)nv; + + io_size = bhnd_nvram_io_getsize(btxt->data); + io_offset = bhnd_nvram_btxt_io_offset(btxt, cookiep); + + /* At EOF? */ + if (io_offset == io_size) + BHND_NV_PANIC("invalid cookiep: %p", cookiep); + + /* Variable name is found directly at the given offset; trailing + * NUL means we can assume that it's at least 1 byte long */ + error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &ptr, 1, NULL); + if (error) + BHND_NV_PANIC("unexpected error in read_ptr(): %d\n", error); + + return (ptr); +} + +/* Convert cookie back to an I/O offset */ +static size_t +bhnd_nvram_btxt_io_offset(struct bhnd_nvram_btxt *btxt, void *cookiep) +{ + size_t io_size; + uintptr_t cval; + + io_size = bhnd_nvram_io_getsize(btxt->data); + cval = (uintptr_t)cookiep; + + BHND_NV_ASSERT(cval < SIZE_MAX, ("cookie > SIZE_MAX)")); + BHND_NV_ASSERT(cval <= io_size, ("cookie > io_size)")); + + return ((size_t)cval); +} + +/* Determine the entry length and env 'key=value' string length of the entry + * at @p offset */ +static int +bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io, size_t offset, + size_t *line_len, size_t *env_len) +{ + const uint8_t *baseptr, *p; + const void *rbuf; + size_t nbytes; + int error; + + /* Fetch read buffer */ + if ((error = bhnd_nvram_io_read_ptr(io, offset, &rbuf, 0, &nbytes))) + return (error); + + /* Find record termination (EOL, or '#') */ + p = rbuf; + baseptr = rbuf; + while ((size_t)(p - baseptr) < nbytes) { + if (*p == '#' || *p == '\n' || *p == '\r') + break; + + p++; + } + + /* Got line length, now trim any trailing whitespace to determine + * actual env length */ + *line_len = p - baseptr; + *env_len = *line_len; + + for (size_t i = 0; i < *line_len; i++) { + char c = baseptr[*line_len - i - 1]; + if (!bhnd_nv_isspace(c)) + break; + + *env_len -= 1; + } + + return (0); +} + +/* Seek past the next line ending (\r, \r\n, or \n) */ +static int +bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io, size_t *offset) +{ + const uint8_t *baseptr, *p; + const void *rbuf; + size_t nbytes; + int error; + + /* Fetch read buffer */ + if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes))) + return (error); + + baseptr = rbuf; + p = rbuf; + while ((size_t)(p - baseptr) < nbytes) { + char c = *p; + + /* Advance to next char. The next position may be EOF, in which + * case a read will be invalid */ + p++; + + if (c == '\r') { + /* CR, check for optional LF */ + if ((size_t)(p - baseptr) < nbytes) { + if (*p == '\n') + p++; + } + + break; + } else if (c == '\n') { + break; + } + } + + /* Hit newline or EOF */ + *offset += (p - baseptr); + return (0); +} + +/* Seek to the next valid non-comment line (or EOF) */ +static int +bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io, size_t *offset) +{ + const uint8_t *baseptr, *p; + const void *rbuf; + size_t nbytes; + int error; + + /* Fetch read buffer */ + if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes))) + return (error); + + /* Skip leading whitespace and comments */ + baseptr = rbuf; + p = rbuf; + while ((size_t)(p - baseptr) < nbytes) { + char c = *p; + + /* Skip whitespace */ + if (bhnd_nv_isspace(c)) { + p++; + continue; + } + + /* Skip entire comment line */ + if (c == '#') { + size_t line_off = *offset + (p - baseptr); + + if ((error = bhnd_nvram_btxt_seek_eol(io, &line_off))) + return (error); + + p = baseptr + (line_off - *offset); + continue; + } + + /* Non-whitespace, non-comment */ + break; + } + + *offset += (p - baseptr); + return (0); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c b/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c new file mode 100644 index 00000000000..4b5825dcbf8 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c @@ -0,0 +1,1988 @@ +/*- + * 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 +#else /* !_KERNEL */ +#include +#include +#include +#include +#include +#include +#include +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_datavar.h" + +#include "bhnd_nvram_data_spromvar.h" + +/* + * BHND SPROM NVRAM data class + * + * The SPROM data format is a fixed-layout, non-self-descriptive binary format, + * used on Broadcom wireless and wired adapters, that provides a subset of the + * variables defined by Broadcom SoC NVRAM formats. + */ +BHND_NVRAM_DATA_CLASS_DEFN(sprom, "Broadcom SPROM", + sizeof(struct bhnd_nvram_sprom)) + +static int sprom_sort_idx(const void *lhs, const void *rhs); + +static int sprom_opcode_state_init(struct sprom_opcode_state *state, + const struct bhnd_sprom_layout *layout); +static int sprom_opcode_state_reset(struct sprom_opcode_state *state); +static int sprom_opcode_state_seek(struct sprom_opcode_state *state, + struct sprom_opcode_idx *indexed); + +static int sprom_opcode_next_var(struct sprom_opcode_state *state); +static int sprom_opcode_parse_var(struct sprom_opcode_state *state, + struct sprom_opcode_idx *indexed); + +static int sprom_opcode_next_binding(struct sprom_opcode_state *state); + +static int sprom_opcode_set_type(struct sprom_opcode_state *state, + bhnd_nvram_type type); + +static int sprom_opcode_set_var(struct sprom_opcode_state *state, + size_t vid); +static int sprom_opcode_clear_var(struct sprom_opcode_state *state); +static int sprom_opcode_flush_bind(struct sprom_opcode_state *state); +static int sprom_opcode_read_opval32(struct sprom_opcode_state *state, + uint8_t type, uint32_t *opval); +static int sprom_opcode_apply_scale(struct sprom_opcode_state *state, + uint32_t *value); + +static int sprom_opcode_step(struct sprom_opcode_state *state, + uint8_t *opcode); + +#define SPROM_OP_BAD(_state, _fmt, ...) \ + BHND_NV_LOG("bad encoding at %td: " _fmt, \ + (_state)->input - (_state)->layout->bindings, ##__VA_ARGS__) + +#define SPROM_COOKIE_TO_NVRAM(_cookie) \ + bhnd_nvram_get_vardefn(((struct sprom_opcode_idx *)_cookie)->vid) + +/** + * Read the magic value from @p io, and verify that it matches + * the @p layout's expected magic value. + * + * If @p layout does not defined a magic value, @p magic is set to 0x0 + * and success is returned. + * + * @param io An I/O context mapping the SPROM data to be identified. + * @param layout The SPROM layout against which @p io should be verified. + * @param[out] magic On success, the SPROM magic value. + * + * @retval 0 success + * @retval non-zero If checking @p io otherwise fails, a regular unix + * error code will be returned. + */ +static int +bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io *io, + const struct bhnd_sprom_layout *layout, uint16_t *magic) +{ + int error; + + /* Skip if layout does not define a magic value */ + if (layout->flags & SPROM_LAYOUT_MAGIC_NONE) + return (0); + + /* Read the magic value */ + error = bhnd_nvram_io_read(io, layout->magic_offset, magic, + sizeof(*magic)); + if (error) + return (error); + + *magic = le16toh(*magic); + + /* If the signature does not match, skip to next layout */ + if (*magic != layout->magic_value) + return (ENXIO); + + return (0); +} + +/** + * Attempt to identify the format of the SPROM data mapped by @p io. + * + * The SPROM data format does not provide any identifying information at a + * known offset, instead requiring that we iterate over the known SPROM image + * sizes until we are able to compute a valid checksum (and, for later + * revisions, validate a signature at a revision-specific offset). + * + * @param io An I/O context mapping the SPROM data to be identified. + * @param[out] ident On success, the identified SPROM layout. + * @param[out] shadow On success, a correctly sized iobuf instance mapping + * a copy of the identified SPROM image. The caller is + * responsible for deallocating this instance via + * bhnd_nvram_io_free() + * + * @retval 0 success + * @retval non-zero If identifying @p io otherwise fails, a regular unix + * error code will be returned. + */ +static int +bhnd_nvram_sprom_ident(struct bhnd_nvram_io *io, + const struct bhnd_sprom_layout **ident, struct bhnd_nvram_io **shadow) +{ + struct bhnd_nvram_io *buf; + uint8_t crc; + size_t crc_errors; + size_t sprom_sz_max; + int error; + + /* Find the largest SPROM layout size */ + sprom_sz_max = 0; + for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) { + sprom_sz_max = bhnd_nv_ummax(sprom_sz_max, + bhnd_sprom_layouts[i].size); + } + + /* Allocate backing buffer and initialize CRC state */ + buf = bhnd_nvram_iobuf_empty(0, sprom_sz_max); + crc = BHND_NVRAM_CRC8_INITIAL; + crc_errors = 0; + + /* We iterate the SPROM layouts smallest to largest, allowing us to + * perform incremental checksum calculation */ + for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) { + const struct bhnd_sprom_layout *layout; + void *ptr; + size_t nbytes, nr; + uint16_t magic; + uint8_t srev; + bool crc_valid; + bool have_magic; + + layout = &bhnd_sprom_layouts[i]; + nbytes = bhnd_nvram_io_getsize(buf); + + if ((layout->flags & SPROM_LAYOUT_MAGIC_NONE)) { + have_magic = false; + } else { + have_magic = true; + } + + /* Layout instances must be ordered from smallest to largest by + * the nvram_map compiler */ + if (nbytes > layout->size) + BHND_NV_PANIC("SPROM layout is defined out-of-order"); + + /* Calculate number of additional bytes to be read */ + nr = layout->size - nbytes; + + /* Adjust the buffer size and fetch a write pointer */ + if ((error = bhnd_nvram_io_setsize(buf, layout->size))) + goto failed; + + error = bhnd_nvram_io_write_ptr(buf, nbytes, &ptr, nr, NULL); + if (error) + goto failed; + + /* Read image data and update CRC (errors are reported + * after the signature check) */ + if ((error = bhnd_nvram_io_read(io, nbytes, ptr, nr))) + goto failed; + + crc = bhnd_nvram_crc8(ptr, nr, crc); + crc_valid = (crc == BHND_NVRAM_CRC8_VALID); + if (!crc_valid) + crc_errors++; + + /* Fetch SPROM revision */ + error = bhnd_nvram_io_read(buf, layout->srev_offset, &srev, + sizeof(srev)); + if (error) + goto failed; + + /* Early sromrev 1 devices (specifically some BCM440x enet + * cards) are reported to have been incorrectly programmed + * with a revision of 0x10. */ + if (layout->rev == 1 && srev == 0x10) + srev = 0x1; + + /* Check revision against the layout definition */ + if (srev != layout->rev) + continue; + + /* Check the magic value, skipping to the next layout on + * failure. */ + error = bhnd_nvram_sprom_check_magic(buf, layout, &magic); + if (error) { + /* If the CRC is was valid, log the mismatch */ + if (crc_valid || BHND_NV_VERBOSE) { + BHND_NV_LOG("invalid sprom %hhu signature: " + "0x%hx (expected 0x%hx)\n", srev, + magic, layout->magic_value); + + error = ENXIO; + goto failed; + } + + continue; + } + + /* Check for an earlier CRC error */ + if (!crc_valid) { + /* If the magic check succeeded, then we may just have + * data corruption -- log the CRC error */ + if (have_magic || BHND_NV_VERBOSE) { + BHND_NV_LOG("sprom %hhu CRC error (crc=%#hhx, " + "expected=%#x)\n", srev, crc, + BHND_NVRAM_CRC8_VALID); + } + + continue; + } + + /* Identified */ + *shadow = buf; + *ident = layout; + return (0); + } + + /* No match -- set error and fallthrough */ + error = ENXIO; + if (crc_errors > 0 && BHND_NV_VERBOSE) { + BHND_NV_LOG("sprom parsing failed with %zu CRC errors\n", + crc_errors); + } + +failed: + bhnd_nvram_io_free(buf); + return (error); +} + +static int +bhnd_nvram_sprom_probe(struct bhnd_nvram_io *io) +{ + const struct bhnd_sprom_layout *layout; + struct bhnd_nvram_io *shadow; + int error; + + /* Try to parse the input */ + if ((error = bhnd_nvram_sprom_ident(io, &layout, &shadow))) + return (error); + + /* Clean up the shadow iobuf */ + bhnd_nvram_io_free(shadow); + + return (BHND_NVRAM_DATA_PROBE_DEFAULT); +} + +static int +bhnd_nvram_sprom_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_sprom *sp; + size_t num_vars; + int error; + + sp = (struct bhnd_nvram_sprom *)nv; + + /* Identify the SPROM input data */ + if ((error = bhnd_nvram_sprom_ident(io, &sp->layout, &sp->data))) + goto failed; + + /* Initialize SPROM binding eval state */ + if ((error = sprom_opcode_state_init(&sp->state, sp->layout))) + goto failed; + + /* Allocate our opcode index */ + sp->num_idx = sp->layout->num_vars; + if ((sp->idx = bhnd_nv_calloc(sp->num_idx, sizeof(*sp->idx))) == NULL) + goto failed; + + /* Parse out index entries from our stateful opcode stream */ + for (num_vars = 0; num_vars < sp->num_idx; num_vars++) { + size_t opcodes; + + /* Seek to next entry */ + if ((error = sprom_opcode_next_var(&sp->state))) { + SPROM_OP_BAD(&sp->state, + "error reading expected variable entry: %d\n", + error); + goto failed; + } + + /* We limit the SPROM index representations to the minimal + * type widths capable of covering all known layouts */ + + /* Save SPROM image offset */ + if (sp->state.offset > UINT16_MAX) { + SPROM_OP_BAD(&sp->state, + "cannot index large offset %u\n", sp->state.offset); + } + sp->idx[num_vars].offset = sp->state.offset; + + /* Save current variable ID */ + if (sp->state.vid > UINT16_MAX) { + SPROM_OP_BAD(&sp->state, + "cannot index large vid %zu\n", sp->state.vid); + } + sp->idx[num_vars].vid = sp->state.vid; + + /* Save opcode position */ + opcodes = (sp->state.input - sp->layout->bindings); + if (opcodes > UINT16_MAX) { + SPROM_OP_BAD(&sp->state, + "cannot index large opcode offset %zu\n", opcodes); + } + sp->idx[num_vars].opcodes = opcodes; + } + + /* Should have reached end of binding table; next read must return + * ENOENT */ + if ((error = sprom_opcode_next_var(&sp->state)) != ENOENT) { + BHND_NV_LOG("expected EOF parsing binding table: %d\n", error); + goto failed; + } + + /* Sort index by variable ID, ascending */ + qsort(sp->idx, sp->num_idx, sizeof(sp->idx[0]), sprom_sort_idx); + + return (0); + +failed: + if (sp->data != NULL) + bhnd_nvram_io_free(sp->data); + + if (sp->idx != NULL) + bhnd_nv_free(sp->idx); + + return (error); +} + +/* sort function for sprom_opcode_idx values */ +static int +sprom_sort_idx(const void *lhs, const void *rhs) +{ + const struct sprom_opcode_idx *l, *r; + + l = lhs; + r = rhs; + + if (l->vid < r->vid) + return (-1); + if (l->vid > r->vid) + return (1); + return (0); +} + +static void +bhnd_nvram_sprom_free(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_sprom *sp = (struct bhnd_nvram_sprom *)nv; + + bhnd_nvram_io_free(sp->data); + bhnd_nv_free(sp->idx); +} + +size_t +bhnd_nvram_sprom_count(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv; + return (sprom->layout->num_vars); +} + +static int +bhnd_nvram_sprom_size(struct bhnd_nvram_data *nv, size_t *size) +{ + struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv; + + /* The serialized form will be identical in length + * to our backing buffer representation */ + *size = bhnd_nvram_io_getsize(sprom->data); + return (0); +} + +static int +bhnd_nvram_sprom_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len) +{ + struct bhnd_nvram_sprom *sprom; + size_t limit, req_len; + int error; + + sprom = (struct bhnd_nvram_sprom *)nv; + limit = *len; + + /* Provide the required size */ + if ((error = bhnd_nvram_sprom_size(nv, &req_len))) + return (error); + + *len = req_len; + + if (buf == NULL) { + return (0); + } else if (*len > limit) { + return (ENOMEM); + } + + /* Write to the output buffer */ + return (bhnd_nvram_io_read(sprom->data, 0x0, buf, *len)); +} + +static uint32_t +bhnd_nvram_sprom_caps(struct bhnd_nvram_data *nv) +{ + return (BHND_NVRAM_DATA_CAP_INDEXED); +} + +static const char * +bhnd_nvram_sprom_next(struct bhnd_nvram_data *nv, void **cookiep) +{ + struct bhnd_nvram_sprom *sp; + struct sprom_opcode_idx *idx_entry; + size_t idx_next; + const struct bhnd_nvram_vardefn *var; + + sp = (struct bhnd_nvram_sprom *)nv; + + /* Seek to appropriate starting point */ + if (*cookiep == NULL) { + /* Start search at first index entry */ + idx_next = 0; + } else { + /* Determine current index position */ + idx_entry = *cookiep; + idx_next = (size_t)(idx_entry - sp->idx); + BHND_NV_ASSERT(idx_next < sp->num_idx, + ("invalid index %zu; corrupt cookie?", idx_next)); + + /* Advance to next entry */ + idx_next++; + + /* Check for EOF */ + if (idx_next == sp->num_idx) + return (NULL); + } + + /* Skip entries that are disabled by virtue of IGNALL1 */ + for (; idx_next < sp->num_idx; idx_next++) { + /* Fetch index entry and update cookiep */ + idx_entry = &sp->idx[idx_next]; + *cookiep = idx_entry; + + /* Fetch variable definition */ + var = bhnd_nvram_get_vardefn(idx_entry->vid); + + /* We might need to parse the variable's value to determine + * whether it should be treated as unset */ + if (var->flags & BHND_NVRAM_VF_IGNALL1) { + int error; + size_t len; + + error = bhnd_nvram_sprom_getvar(nv, *cookiep, NULL, + &len, var->type); + if (error) { + BHND_NV_ASSERT(error == ENOENT, ("unexpected " + "error parsing variable: %d", error)); + + continue; + } + } + + /* Found! */ + return (var->name); + } + + /* Reached end of index entries */ + return (NULL); +} + +/* bsearch function used by bhnd_nvram_sprom_find() */ +static int +bhnd_nvram_sprom_find_vid_compare(const void *key, const void *rhs) +{ + const struct sprom_opcode_idx *r; + size_t l; + + l = *(const size_t *)key; + r = rhs; + + if (l < r->vid) + return (-1); + if (l > r->vid) + return (1); + return (0); +} + +static void * +bhnd_nvram_sprom_find(struct bhnd_nvram_data *nv, const char *name) +{ + struct bhnd_nvram_sprom *sp; + const struct bhnd_nvram_vardefn *var; + size_t vid; + + sp = (struct bhnd_nvram_sprom *)nv; + + /* Determine the variable ID for the given name */ + if ((var = bhnd_nvram_find_vardefn(name)) == NULL) + return (NULL); + + vid = bhnd_nvram_get_vardefn_id(var); + + /* Search our index for the variable ID */ + return (bsearch(&vid, sp->idx, sp->num_idx, sizeof(sp->idx[0]), + bhnd_nvram_sprom_find_vid_compare)); +} + +/** + * Read the value of @p type from the SPROM data at @p offset, apply @p mask + * and @p shift, and OR with the existing @p value. + * + * @param sp The SPROM data instance. + * @param var The NVRAM variable definition + * @param type The type to read at @p offset + * @param offset The data offset to be read. + * @param mask The mask to be applied to the value read at @p offset. + * @param shift The shift to be applied after masking; if positive, a right + * shift will be applied, if negative, a left shift. + * @param value The read destination; the parsed value will be OR'd with the + * current contents of @p value. + */ +static int +bhnd_nvram_sprom_read_offset(struct bhnd_nvram_sprom *sp, + const struct bhnd_nvram_vardefn *var, bhnd_nvram_type type, + size_t offset, uint32_t mask, int8_t shift, + union bhnd_nvram_sprom_intv *value) +{ + size_t sp_width; + int error; + union { + uint8_t u8; + uint16_t u16; + uint32_t u32; + int8_t s8; + int16_t s16; + int32_t s32; + } sp_value; + + /* Determine type width */ + sp_width = bhnd_nvram_value_size(type, NULL, 0, 1); + if (sp_width == 0) { + /* Variable-width types are unsupported */ + BHND_NV_LOG("invalid %s SPROM offset type %d\n", var->name, + type); + return (EFTYPE); + } + + /* Perform read */ + error = bhnd_nvram_io_read(sp->data, offset, &sp_value, + sp_width); + if (error) { + BHND_NV_LOG("error reading %s SPROM offset %#zx: %d\n", + var->name, offset, error); + return (EFTYPE); + } + +#define NV_PARSE_INT(_type, _src, _dest, _swap) do { \ + /* Swap to host byte order */ \ + sp_value. _src = (_type) _swap(sp_value. _src); \ + \ + /* Mask and shift the value */ \ + sp_value. _src &= mask; \ + if (shift > 0) { \ + sp_value. _src >>= shift; \ + } else if (shift < 0) { \ + sp_value. _src <<= -shift; \ + } \ + \ + /* Emit output, widening to 32-bit representation */ \ + value-> _dest |= sp_value. _src; \ +} while(0) + + /* Apply mask/shift and widen to a common 32bit representation */ + switch (type) { + case BHND_NVRAM_TYPE_UINT8: + NV_PARSE_INT(uint8_t, u8, u32, ); + break; + case BHND_NVRAM_TYPE_UINT16: + NV_PARSE_INT(uint16_t, u16, u32, le16toh); + break; + case BHND_NVRAM_TYPE_UINT32: + NV_PARSE_INT(uint32_t, u32, u32, le32toh); + break; + case BHND_NVRAM_TYPE_INT8: + NV_PARSE_INT(int8_t, s8, s32, ); + break; + case BHND_NVRAM_TYPE_INT16: + NV_PARSE_INT(int16_t, s16, s32, le16toh); + break; + case BHND_NVRAM_TYPE_INT32: + NV_PARSE_INT(int32_t, s32, s32, le32toh); + break; + case BHND_NVRAM_TYPE_CHAR: + NV_PARSE_INT(uint8_t, u8, u32, ); + break; + + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_INT64: + case BHND_NVRAM_TYPE_STRING: + /* fallthrough (unused by SPROM) */ + default: + BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type); + return (EFTYPE); + } + + return (0); +} + +static int +bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, + size_t *len, bhnd_nvram_type otype) +{ + bhnd_nvram_val_t val; + struct bhnd_nvram_sprom *sp; + struct sprom_opcode_idx *idx; + const struct bhnd_nvram_vardefn *var; + union bhnd_nvram_sprom_storage storage; + union bhnd_nvram_sprom_storage *inp; + union bhnd_nvram_sprom_intv intv; + bhnd_nvram_type var_btype; + size_t ilen, ipos, iwidth; + size_t nelem; + bool all_bits_set; + int error; + + sp = (struct bhnd_nvram_sprom *)nv; + idx = cookiep; + + BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep")); + + /* Fetch canonical variable definition */ + var = SPROM_COOKIE_TO_NVRAM(cookiep); + BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep)); + + /* + * Fetch the array length from the SPROM variable definition. + * + * This generally be identical to the array length provided by the + * canonical NVRAM variable definition, but some SPROM layouts may + * define a smaller element count. + */ + if ((error = sprom_opcode_parse_var(&sp->state, idx))) { + BHND_NV_LOG("variable evaluation failed: %d\n", error); + return (error); + } + + nelem = sp->state.var.nelem; + if (nelem > var->nelem) { + BHND_NV_LOG("SPROM array element count %zu cannot be " + "represented by '%s' element count of %hhu\n", nelem, + var->name, var->nelem); + return (EFTYPE); + } + + /* Fetch the var's base element type */ + var_btype = bhnd_nvram_base_type(var->type); + + /* Calculate total byte length of the native encoding */ + if ((iwidth = bhnd_nvram_value_size(var_btype, NULL, 0, 1)) == 0) { + /* SPROM does not use (and we do not support) decoding of + * variable-width data types */ + BHND_NV_LOG("invalid SPROM data type: %d", var->type); + return (EFTYPE); + } + ilen = nelem * iwidth; + + /* Decode into our own local storage. */ + inp = &storage; + if (ilen > sizeof(storage)) { + BHND_NV_LOG("error decoding '%s', SPROM_ARRAY_MAXLEN " + "incorrect\n", var->name); + return (EFTYPE); + } + + /* Zero-initialize our decode buffer; any output elements skipped + * during decode should default to zero. */ + memset(inp, 0, ilen); + + /* + * Decode the SPROM data, iteratively decoding up to nelem values. + */ + if ((error = sprom_opcode_state_seek(&sp->state, idx))) { + BHND_NV_LOG("variable seek failed: %d\n", error); + return (error); + } + + ipos = 0; + intv.u32 = 0x0; + if (var->flags & BHND_NVRAM_VF_IGNALL1) + all_bits_set = true; + else + all_bits_set = false; + while ((error = sprom_opcode_next_binding(&sp->state)) == 0) { + struct sprom_opcode_bind *binding; + struct sprom_opcode_var *binding_var; + bhnd_nvram_type intv_type; + size_t offset; + size_t nbyte; + uint32_t skip_in_bytes; + void *ptr; + + BHND_NV_ASSERT( + sp->state.var_state >= SPROM_OPCODE_VAR_STATE_OPEN, + ("invalid var state")); + BHND_NV_ASSERT(sp->state.var.have_bind, ("invalid bind state")); + + binding_var = &sp->state.var; + binding = &sp->state.var.bind; + + if (ipos >= nelem) { + BHND_NV_LOG("output skip %u positioned " + "%zu beyond nelem %zu\n", + binding->skip_out, ipos, nelem); + return (EINVAL); + } + + /* Calculate input skip bytes for this binding */ + skip_in_bytes = binding->skip_in; + error = sprom_opcode_apply_scale(&sp->state, &skip_in_bytes); + if (error) + return (error); + + /* Bind */ + offset = sp->state.offset; + for (size_t i = 0; i < binding->count; i++) { + /* Read the offset value, OR'ing with the current + * value of intv */ + error = bhnd_nvram_sprom_read_offset(sp, var, + binding_var->base_type, + offset, + binding_var->mask, + binding_var->shift, + &intv); + if (error) + return (error); + + /* If IGNALL1, record whether value does not have + * all bits set. */ + if (var->flags & BHND_NVRAM_VF_IGNALL1 && + all_bits_set) + { + uint32_t all1; + + all1 = binding_var->mask; + if (binding_var->shift > 0) + all1 >>= binding_var->shift; + else if (binding_var->shift < 0) + all1 <<= -binding_var->shift; + + if ((intv.u32 & all1) != all1) + all_bits_set = false; + } + + /* Adjust input position; this was already verified to + * not overflow/underflow during SPROM opcode + * evaluation */ + if (binding->skip_in_negative) { + offset -= skip_in_bytes; + } else { + offset += skip_in_bytes; + } + + /* Skip writing to inp if additional bindings are + * required to fully populate intv */ + if (binding->skip_out == 0) + continue; + + /* We use bhnd_nvram_value_coerce() to perform + * overflow-checked coercion from the widened + * uint32/int32 intv value to the requested output + * type */ + if (bhnd_nvram_is_signed_type(var_btype)) + intv_type = BHND_NVRAM_TYPE_INT32; + else + intv_type = BHND_NVRAM_TYPE_UINT32; + + /* Calculate address of the current element output + * position */ + ptr = (uint8_t *)inp + (iwidth * ipos); + + /* Perform coercion of the array element */ + nbyte = iwidth; + error = bhnd_nvram_value_coerce(&intv, sizeof(intv), + intv_type, ptr, &nbyte, var_btype); + if (error) + return (error); + + /* Clear temporary state */ + intv.u32 = 0x0; + + /* Advance output position */ + if (SIZE_MAX - binding->skip_out < ipos) { + BHND_NV_LOG("output skip %u would overflow " + "%zu\n", binding->skip_out, ipos); + return (EINVAL); + } + + ipos += binding->skip_out; + } + } + + /* Did we iterate all bindings until hitting end of the variable + * definition? */ + BHND_NV_ASSERT(error != 0, ("loop terminated early")); + if (error != ENOENT) { + return (error); + } + + /* If marked IGNALL1 and all bits are set, treat variable as + * unavailable */ + if ((var->flags & BHND_NVRAM_VF_IGNALL1) && all_bits_set) + return (ENOENT); + + + /* Perform value coercion from our local representation */ + error = bhnd_nvram_val_init(&val, var->fmt, inp, ilen, var->type, + BHND_NVRAM_VAL_BORROW_DATA); + if (error) + return (error); + + error = bhnd_nvram_val_encode(&val, buf, len, otype); + + /* Clean up */ + bhnd_nvram_val_release(&val); + return (error); +} + +static const void * +bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type) +{ + /* Unsupported */ + return (NULL); +} + +static const char * +bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) +{ + const struct bhnd_nvram_vardefn *var; + + BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep")); + + var = SPROM_COOKIE_TO_NVRAM(cookiep); + BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep)); + + return (var->name); +} + +/** + * Initialize SPROM opcode evaluation state. + * + * @param state The opcode state to be initialized. + * @param layout The SPROM layout to be parsed by this instance. + * + * + * @retval 0 success + * @retval non-zero If initialization fails, a regular unix error code will be + * returned. + */ +static int +sprom_opcode_state_init(struct sprom_opcode_state *state, + const struct bhnd_sprom_layout *layout) +{ + memset(state, 0, sizeof(*state)); + + state->layout = layout; + state->input = layout->bindings; + state->var_state = SPROM_OPCODE_VAR_STATE_NONE; + + bit_set(state->revs, layout->rev); + + return (0); +} + +/** + * Reset SPROM opcode evaluation state; future evaluation will be performed + * starting at the first opcode. + * + * @param state The opcode state to be reset. + * + * @retval 0 success + * @retval non-zero If reset fails, a regular unix error code will be returned. + */ +static int +sprom_opcode_state_reset(struct sprom_opcode_state *state) +{ + return (sprom_opcode_state_init(state, state->layout)); +} + +/** + * Reset SPROM opcode evaluation state and seek to the @p indexed position. + * + * @param state The opcode state to be reset. + * @param indexed The indexed location to which we'll seek the opcode state. + */ +static int +sprom_opcode_state_seek(struct sprom_opcode_state *state, + struct sprom_opcode_idx *indexed) +{ + int error; + + BHND_NV_ASSERT(indexed->opcodes < state->layout->bindings_size, + ("index entry references invalid opcode position")); + + /* Reset state */ + if ((error = sprom_opcode_state_reset(state))) + return (error); + + /* Seek to the indexed sprom opcode offset */ + state->input = state->layout->bindings + indexed->opcodes; + + /* Restore the indexed sprom data offset and VID */ + state->offset = indexed->offset; + + /* Restore the indexed sprom variable ID */ + if ((error = sprom_opcode_set_var(state, indexed->vid))) + return (error); + + return (0); +} + +/** + * Set the current revision range for @p state. This also resets + * variable state. + * + * @param state The opcode state to update + * @param start The first revision in the range. + * @param end The last revision in the range. + * + * @retval 0 success + * @retval non-zero If updating @p state fails, a regular unix error code will + * be returned. + */ +static inline int +sprom_opcode_set_revs(struct sprom_opcode_state *state, uint8_t start, + uint8_t end) +{ + int error; + + /* Validate the revision range */ + if (start > SPROM_OP_REV_MAX || + end > SPROM_OP_REV_MAX || + end < start) + { + SPROM_OP_BAD(state, "invalid revision range: %hhu-%hhu\n", + start, end); + return (EINVAL); + } + + /* Clear variable state */ + if ((error = sprom_opcode_clear_var(state))) + return (error); + + /* Reset revision mask */ + memset(state->revs, 0x0, sizeof(state->revs)); + bit_nset(state->revs, start, end); + + return (0); +} + +/** + * Set the current variable's value mask for @p state. + * + * @param state The opcode state to update + * @param mask The mask to be set + * + * @retval 0 success + * @retval non-zero If updating @p state fails, a regular unix error code will + * be returned. + */ +static inline int +sprom_opcode_set_mask(struct sprom_opcode_state *state, uint32_t mask) +{ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "no open variable definition\n"); + return (EINVAL); + } + + state->var.mask = mask; + return (0); +} + +/** + * Set the current variable's value shift for @p state. + * + * @param state The opcode state to update + * @param shift The shift to be set + * + * @retval 0 success + * @retval non-zero If updating @p state fails, a regular unix error code will + * be returned. + */ +static inline int +sprom_opcode_set_shift(struct sprom_opcode_state *state, int8_t shift) +{ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "no open variable definition\n"); + return (EINVAL); + } + + state->var.shift = shift; + return (0); +} + +/** + * Register a new BIND/BINDN operation with @p state. + * + * @param state The opcode state to update. + * @param count The number of elements to be bound. + * @param skip_in The number of input elements to skip after each bind. + * @param skip_in_negative If true, the input skip should be subtracted from + * the current offset after each bind. If false, the input skip should be + * added. + * @param skip_out The number of output elements to skip after each bind. + * + * @retval 0 success + * @retval EINVAL if a variable definition is not open. + * @retval EINVAL if @p skip_in and @p count would trigger an overflow or + * underflow when applied to the current input offset. + * @retval ERANGE if @p skip_in would overflow uint32_t when multiplied by + * @p count and the scale value. + * @retval ERANGE if @p skip_out would overflow uint32_t when multiplied by + * @p count and the scale value. + * @retval non-zero If updating @p state otherwise fails, a regular unix error + * code will be returned. + */ +static inline int +sprom_opcode_set_bind(struct sprom_opcode_state *state, uint8_t count, + uint8_t skip_in, bool skip_in_negative, uint8_t skip_out) +{ + uint32_t iskip_total; + uint32_t iskip_scaled; + int error; + + /* Must have an open variable */ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "no open variable definition\n"); + SPROM_OP_BAD(state, "BIND outside of variable definition\n"); + return (EINVAL); + } + + /* Cannot overwite an existing bind definition */ + if (state->var.have_bind) { + SPROM_OP_BAD(state, "BIND overwrites existing definition\n"); + return (EINVAL); + } + + /* Must have a count of at least 1 */ + if (count == 0) { + SPROM_OP_BAD(state, "BIND with zero count\n"); + return (EINVAL); + } + + /* Scale skip_in by the current type width */ + iskip_scaled = skip_in; + if ((error = sprom_opcode_apply_scale(state, &iskip_scaled))) + return (error); + + /* Calculate total input bytes skipped: iskip_scaled * count) */ + if (iskip_scaled > 0 && UINT32_MAX / iskip_scaled < count) { + SPROM_OP_BAD(state, "skip_in %hhu would overflow", skip_in); + return (EINVAL); + } + + iskip_total = iskip_scaled * count; + + /* Verify that the skip_in value won't under/overflow the current + * input offset. */ + if (skip_in_negative) { + if (iskip_total > state->offset) { + SPROM_OP_BAD(state, "skip_in %hhu would underflow " + "offset %u\n", skip_in, state->offset); + return (EINVAL); + } + } else { + if (UINT32_MAX - iskip_total < state->offset) { + SPROM_OP_BAD(state, "skip_in %hhu would overflow " + "offset %u\n", skip_in, state->offset); + return (EINVAL); + } + } + + /* Set the actual count and skip values */ + state->var.have_bind = true; + state->var.bind.count = count; + state->var.bind.skip_in = skip_in; + state->var.bind.skip_out = skip_out; + + state->var.bind.skip_in_negative = skip_in_negative; + + /* Update total bind count for the current variable */ + state->var.bind_total++; + + return (0); +} + + +/** + * Apply and clear the current opcode bind state, if any. + * + * @param state The opcode state to update. + * + * @retval 0 success + * @retval non-zero If updating @p state otherwise fails, a regular unix error + * code will be returned. + */ +static int +sprom_opcode_flush_bind(struct sprom_opcode_state *state) +{ + int error; + uint32_t skip; + + /* Nothing to do? */ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN || + !state->var.have_bind) + return (0); + + /* Apply SPROM offset adjustment */ + if (state->var.bind.count > 0) { + skip = state->var.bind.skip_in * state->var.bind.count; + if ((error = sprom_opcode_apply_scale(state, &skip))) + return (error); + + if (state->var.bind.skip_in_negative) { + state->offset -= skip; + } else { + state->offset += skip; + } + } + + /* Clear bind state */ + memset(&state->var.bind, 0, sizeof(state->var.bind)); + state->var.have_bind = false; + + return (0); +} + +/** + * Set the current type to @p type, and reset type-specific + * stream state. + * + * @param state The opcode state to update. + * @param type The new type. + * + * @retval 0 success + * @retval EINVAL if @p vid is not a valid variable ID. + */ +static int +sprom_opcode_set_type(struct sprom_opcode_state *state, bhnd_nvram_type type) +{ + bhnd_nvram_type base_type; + size_t width; + uint32_t mask; + + /* Must have an open variable definition */ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "type set outside variable definition\n"); + return (EINVAL); + } + + /* Fetch type width for use as our scale value */ + width = bhnd_nvram_value_size(type, NULL, 0, 1); + if (width == 0) { + SPROM_OP_BAD(state, "unsupported variable-width type: %d\n", + type); + return (EINVAL); + } else if (width > UINT32_MAX) { + SPROM_OP_BAD(state, "invalid type width %zu for type: %d\n", + width, type); + return (EINVAL); + } + + /* Determine default mask value for the element type */ + base_type = bhnd_nvram_base_type(type); + switch (base_type) { + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_CHAR: + mask = UINT8_MAX; + break; + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_INT16: + mask = UINT16_MAX; + break; + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_INT32: + mask = UINT32_MAX; + break; + case BHND_NVRAM_TYPE_STRING: + /* fallthrough (unused by SPROM) */ + default: + SPROM_OP_BAD(state, "unsupported type: %d\n", type); + return (EINVAL); + } + + /* Update state */ + state->var.base_type = base_type; + state->var.mask = mask; + state->var.scale = (uint32_t)width; + + return (0); +} + +/** + * Clear current variable state, if any. + * + * @param state The opcode state to update. + */ +static int +sprom_opcode_clear_var(struct sprom_opcode_state *state) +{ + if (state->var_state == SPROM_OPCODE_VAR_STATE_NONE) + return (0); + + BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE, + ("incomplete variable definition")); + BHND_NV_ASSERT(!state->var.have_bind, ("stale bind state")); + + memset(&state->var, 0, sizeof(state->var)); + state->var_state = SPROM_OPCODE_VAR_STATE_NONE; + + return (0); +} + +/** + * Set the current variable's array element count to @p nelem. + * + * @param state The opcode state to update. + * @param nelem The new array length. + * + * @retval 0 success + * @retval EINVAL if no open variable definition exists. + * @retval EINVAL if @p nelem is zero. + * @retval ENXIO if @p nelem is greater than one, and the current variable does + * not have an array type. + * @retval ENXIO if @p nelem exceeds the array length of the NVRAM variable + * definition. + */ +static int +sprom_opcode_set_nelem(struct sprom_opcode_state *state, uint8_t nelem) +{ + const struct bhnd_nvram_vardefn *var; + + /* Must have a defined variable */ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "array length set without open variable " + "state"); + return (EINVAL); + } + + /* Locate the actual variable definition */ + if ((var = bhnd_nvram_get_vardefn(state->vid)) == NULL) { + SPROM_OP_BAD(state, "unknown variable ID: %zu\n", state->vid); + return (EINVAL); + } + + /* Must be greater than zero */ + if (nelem == 0) { + SPROM_OP_BAD(state, "invalid nelem: %hhu\n", nelem); + return (EINVAL); + } + + /* If the variable is not an array-typed value, the array length + * must be 1 */ + if (!bhnd_nvram_is_array_type(var->type) && nelem != 1) { + SPROM_OP_BAD(state, "nelem %hhu on non-array %zu\n", nelem, + state->vid); + return (ENXIO); + } + + /* Cannot exceed the variable's defined array length */ + if (nelem > var->nelem) { + SPROM_OP_BAD(state, "nelem %hhu exceeds %zu length %hhu\n", + nelem, state->vid, var->nelem); + return (ENXIO); + } + + /* Valid length; update state */ + state->var.nelem = nelem; + + return (0); +} + +/** + * Set the current variable ID to @p vid, and reset variable-specific + * stream state. + * + * @param state The opcode state to update. + * @param vid The new variable ID. + * + * @retval 0 success + * @retval EINVAL if @p vid is not a valid variable ID. + */ +static int +sprom_opcode_set_var(struct sprom_opcode_state *state, size_t vid) +{ + const struct bhnd_nvram_vardefn *var; + int error; + + BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_NONE, + ("overwrite of open variable definition")); + + /* Locate the variable definition */ + if ((var = bhnd_nvram_get_vardefn(vid)) == NULL) { + SPROM_OP_BAD(state, "unknown variable ID: %zu\n", vid); + return (EINVAL); + } + + /* Update vid and var state */ + state->vid = vid; + state->var_state = SPROM_OPCODE_VAR_STATE_OPEN; + + /* Initialize default variable record values */ + memset(&state->var, 0x0, sizeof(state->var)); + + /* Set initial base type */ + if ((error = sprom_opcode_set_type(state, var->type))) + return (error); + + /* Set default array length */ + if ((error = sprom_opcode_set_nelem(state, var->nelem))) + return (error); + + return (0); +} + +/** + * Mark the currently open variable definition as complete. + * + * @param state The opcode state to update. + * + * @retval 0 success + * @retval EINVAL if no incomplete open variable definition exists. + */ +static int +sprom_opcode_end_var(struct sprom_opcode_state *state) +{ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "no open variable definition\n"); + return (EINVAL); + } + + state->var_state = SPROM_OPCODE_VAR_STATE_DONE; + return (0); +} + +/** + * Apply the current scale to @p value. + * + * @param state The SPROM opcode state. + * @param[in,out] value The value to scale + * + * @retval 0 success + * @retval EINVAL if no open variable definition exists. + * @retval EINVAL if applying the current scale would overflow. + */ +static int +sprom_opcode_apply_scale(struct sprom_opcode_state *state, uint32_t *value) +{ + /* Must have a defined variable (and thus, scale) */ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "scaled value encoded without open " + "variable state"); + return (EINVAL); + } + + /* Applying the scale value must not overflow */ + if (UINT32_MAX / state->var.scale < *value) { + SPROM_OP_BAD(state, "cannot represent %" PRIu32 " * %" PRIu32 + "\n", *value, state->var.scale); + return (EINVAL); + } + + *value = (*value) * state->var.scale; + return (0); +} + +/** + * Read a SPROM_OP_DATA_* value from @p opcodes. + * + * @param state The SPROM opcode state. + * @param type The SROM_OP_DATA_* type to be read. + * @param opval On success, the 32bit data representation. If @p type is signed, + * the value will be appropriately sign extended and may be directly cast to + * int32_t. + * + * @retval 0 success + * @retval non-zero If reading the value otherwise fails, a regular unix error + * code will be returned. + */ +static int +sprom_opcode_read_opval32(struct sprom_opcode_state *state, uint8_t type, + uint32_t *opval) +{ + const uint8_t *p; + int error; + + p = state->input; + switch (type) { + case SPROM_OP_DATA_I8: + /* Convert to signed value first, then sign extend */ + *opval = (int32_t)(int8_t)(*p); + p += 1; + break; + case SPROM_OP_DATA_U8: + *opval = *p; + p += 1; + break; + case SPROM_OP_DATA_U8_SCALED: + *opval = *p; + + if ((error = sprom_opcode_apply_scale(state, opval))) + return (error); + + p += 1; + break; + case SPROM_OP_DATA_U16: + *opval = le16dec(p); + p += 2; + break; + case SPROM_OP_DATA_U32: + *opval = le32dec(p); + p += 4; + break; + default: + SPROM_OP_BAD(state, "unsupported data type: %hhu\n", type); + return (EINVAL); + } + + /* Update read address */ + state->input = p; + + return (0); +} + +/** + * Return true if our layout revision is currently defined by the SPROM + * opcode state. + * + * This may be used to test whether the current opcode stream state applies + * to the layout that we are actually parsing. + * + * A given opcode stream may cover multiple layout revisions, switching + * between them prior to defining a set of variables. + */ +static inline bool +sprom_opcode_matches_layout_rev(struct sprom_opcode_state *state) +{ + return (bit_test(state->revs, state->layout->rev)); +} + +/** + * When evaluating @p state and @p opcode, rewrite @p opcode and the current + * evaluation state, as required. + * + * If @p opcode is rewritten, it should be returned from + * sprom_opcode_step() instead of the opcode parsed from @p state's opcode + * stream. + * + * If @p opcode remains unmodified, then sprom_opcode_step() should proceed + * to standard evaluation. + */ +static int +sprom_opcode_rewrite_opcode(struct sprom_opcode_state *state, uint8_t *opcode) +{ + uint8_t op; + int error; + + op = SPROM_OPCODE_OP(*opcode); + switch (state->var_state) { + case SPROM_OPCODE_VAR_STATE_NONE: + /* No open variable definition */ + return (0); + + case SPROM_OPCODE_VAR_STATE_OPEN: + /* Open variable definition; check for implicit closure. */ + + /* + * If a variable definition contains no explicit bind + * instructions prior to closure, we must generate a DO_BIND + * instruction with count and skip values of 1. + */ + if (SPROM_OP_IS_VAR_END(op) && + state->var.bind_total == 0) + { + uint8_t count, skip_in, skip_out; + bool skip_in_negative; + + /* Create bind with skip_in/skip_out of 1, count of 1 */ + count = 1; + skip_in = 1; + skip_out = 1; + skip_in_negative = false; + + error = sprom_opcode_set_bind(state, count, skip_in, + skip_in_negative, skip_out); + if (error) + return (error); + + /* Return DO_BIND */ + *opcode = SPROM_OPCODE_DO_BIND | + (0 << SPROM_OP_BIND_SKIP_IN_SIGN) | + (1 << SPROM_OP_BIND_SKIP_IN_SHIFT) | + (1 << SPROM_OP_BIND_SKIP_OUT_SHIFT); + + return (0); + } + + /* + * If a variable is implicitly closed (e.g. by a new variable + * definition), we must generate a VAR_END instruction. + */ + if (SPROM_OP_IS_IMPLICIT_VAR_END(op)) { + /* Mark as complete */ + if ((error = sprom_opcode_end_var(state))) + return (error); + + /* Return VAR_END */ + *opcode = SPROM_OPCODE_VAR_END; + return (0); + } + break; + + + case SPROM_OPCODE_VAR_STATE_DONE: + /* Previously completed variable definition. Discard variable + * state */ + return (sprom_opcode_clear_var(state)); + } + + /* Nothing to do */ + return (0); +} + +/** + * Evaluate one opcode from @p state. + * + * @param state The opcode state to be evaluated. + * @param[out] opcode On success, the evaluated opcode + * + * @retval 0 success + * @retval ENOENT if EOF is reached + * @retval non-zero if evaluation otherwise fails, a regular unix error + * code will be returned. + */ +static int +sprom_opcode_step(struct sprom_opcode_state *state, uint8_t *opcode) +{ + int error; + + while (*state->input != SPROM_OPCODE_EOF) { + uint32_t val; + uint8_t op, rewrite, immd; + + /* Fetch opcode */ + *opcode = *state->input; + op = SPROM_OPCODE_OP(*opcode); + immd = SPROM_OPCODE_IMM(*opcode); + + /* Clear any existing bind state */ + if ((error = sprom_opcode_flush_bind(state))) + return (error); + + /* Insert local opcode based on current state? */ + rewrite = *opcode; + if ((error = sprom_opcode_rewrite_opcode(state, &rewrite))) + return (error); + + if (rewrite != *opcode) { + /* Provide rewritten opcode */ + *opcode = rewrite; + + /* We must keep evaluating until we hit a state + * applicable to the SPROM revision we're parsing */ + if (!sprom_opcode_matches_layout_rev(state)) + continue; + + return (0); + } + + /* Advance input */ + state->input++; + + switch (op) { + case SPROM_OPCODE_VAR_IMM: + if ((error = sprom_opcode_set_var(state, immd))) + return (error); + break; + + case SPROM_OPCODE_VAR_REL_IMM: + error = sprom_opcode_set_var(state, state->vid + immd); + if (error) + return (error); + break; + + case SPROM_OPCODE_VAR: + error = sprom_opcode_read_opval32(state, immd, &val); + if (error) + return (error); + + if ((error = sprom_opcode_set_var(state, val))) + return (error); + + break; + + case SPROM_OPCODE_VAR_END: + if ((error = sprom_opcode_end_var(state))) + return (error); + break; + + case SPROM_OPCODE_NELEM: + immd = *state->input; + if ((error = sprom_opcode_set_nelem(state, immd))) + return (error); + + state->input++; + break; + + case SPROM_OPCODE_DO_BIND: + case SPROM_OPCODE_DO_BINDN: { + uint8_t count, skip_in, skip_out; + bool skip_in_negative; + + /* Fetch skip arguments */ + skip_in = (immd & SPROM_OP_BIND_SKIP_IN_MASK) >> + SPROM_OP_BIND_SKIP_IN_SHIFT; + + skip_in_negative = + ((immd & SPROM_OP_BIND_SKIP_IN_SIGN) != 0); + + skip_out = (immd & SPROM_OP_BIND_SKIP_OUT_MASK) >> + SPROM_OP_BIND_SKIP_OUT_SHIFT; + + /* Fetch count argument (if any) */ + if (op == SPROM_OPCODE_DO_BINDN) { + /* Count is provided as trailing U8 */ + count = *state->input; + state->input++; + } else { + count = 1; + } + + /* Set BIND state */ + error = sprom_opcode_set_bind(state, count, skip_in, + skip_in_negative, skip_out); + if (error) + return (error); + + break; + } + case SPROM_OPCODE_DO_BINDN_IMM: { + uint8_t count, skip_in, skip_out; + bool skip_in_negative; + + /* Implicit skip_in/skip_out of 1, count encoded as immd + * value */ + count = immd; + skip_in = 1; + skip_out = 1; + skip_in_negative = false; + + error = sprom_opcode_set_bind(state, count, skip_in, + skip_in_negative, skip_out); + if (error) + return (error); + break; + } + + case SPROM_OPCODE_REV_IMM: + if ((error = sprom_opcode_set_revs(state, immd, immd))) + return (error); + break; + + case SPROM_OPCODE_REV_RANGE: { + uint8_t range; + uint8_t rstart, rend; + + /* Revision range is encoded in next byte, as + * { uint8_t start:4, uint8_t end:4 } */ + range = *state->input; + rstart = (range & SPROM_OP_REV_START_MASK) >> + SPROM_OP_REV_START_SHIFT; + rend = (range & SPROM_OP_REV_END_MASK) >> + SPROM_OP_REV_END_SHIFT; + + /* Update revision bitmask */ + error = sprom_opcode_set_revs(state, rstart, rend); + if (error) + return (error); + + /* Advance input */ + state->input++; + break; + } + case SPROM_OPCODE_MASK_IMM: + if ((error = sprom_opcode_set_mask(state, immd))) + return (error); + break; + + case SPROM_OPCODE_MASK: + error = sprom_opcode_read_opval32(state, immd, &val); + if (error) + return (error); + + if ((error = sprom_opcode_set_mask(state, val))) + return (error); + break; + + case SPROM_OPCODE_SHIFT_IMM: + if ((error = sprom_opcode_set_shift(state, immd * 2))) + return (error); + break; + + case SPROM_OPCODE_SHIFT: { + int8_t shift; + + if (immd == SPROM_OP_DATA_I8) { + shift = (int8_t)(*state->input); + } else if (immd == SPROM_OP_DATA_U8) { + val = *state->input; + if (val > INT8_MAX) { + SPROM_OP_BAD(state, "invalid shift " + "value: %#x\n", val); + } + + shift = val; + } else { + SPROM_OP_BAD(state, "unsupported shift data " + "type: %#hhx\n", immd); + return (EINVAL); + } + + if ((error = sprom_opcode_set_shift(state, shift))) + return (error); + + state->input++; + break; + } + case SPROM_OPCODE_OFFSET_REL_IMM: + /* Fetch unscaled relative offset */ + val = immd; + + /* Apply scale */ + if ((error = sprom_opcode_apply_scale(state, &val))) + return (error); + + /* Adding val must not overflow our offset */ + if (UINT32_MAX - state->offset < val) { + BHND_NV_LOG("offset out of range\n"); + return (EINVAL); + } + + /* Adjust offset */ + state->offset += val; + break; + case SPROM_OPCODE_OFFSET: + error = sprom_opcode_read_opval32(state, immd, &val); + if (error) + return (error); + + state->offset = val; + break; + + case SPROM_OPCODE_TYPE: + /* Type follows as U8 */ + immd = *state->input; + state->input++; + + /* fall through */ + case SPROM_OPCODE_TYPE_IMM: + switch (immd) { + 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: + error = sprom_opcode_set_type(state, + (bhnd_nvram_type)immd); + if (error) + return (error); + break; + default: + BHND_NV_LOG("unrecognized type %#hhx\n", immd); + return (EINVAL); + } + break; + + default: + BHND_NV_LOG("unrecognized opcode %#hhx\n", *opcode); + return (EINVAL); + } + + /* We must keep evaluating until we hit a state applicable to + * the SPROM revision we're parsing */ + if (sprom_opcode_matches_layout_rev(state)) + return (0); + } + + /* End of opcode stream */ + return (ENOENT); +} + +/** + * Reset SPROM opcode evaluation state, seek to the @p indexed position, + * and perform complete evaluation of the variable's opcodes. + * + * @param state The opcode state to be to be evaluated. + * @param indexed The indexed variable location. + * + * @retval 0 success + * @retval non-zero If evaluation fails, a regular unix error code will be + * returned. + */ +static int +sprom_opcode_parse_var(struct sprom_opcode_state *state, + struct sprom_opcode_idx *indexed) +{ + uint8_t opcode; + int error; + + /* Seek to entry */ + if ((error = sprom_opcode_state_seek(state, indexed))) + return (error); + + /* Parse full variable definition */ + while ((error = sprom_opcode_step(state, &opcode)) == 0) { + /* Iterate until VAR_END */ + if (SPROM_OPCODE_OP(opcode) != SPROM_OPCODE_VAR_END) + continue; + + BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE, + ("incomplete variable definition")); + + return (0); + } + + /* Error parsing definition */ + return (error); +} + +/** + * Evaluate @p state until the next variable definition is found. + * + * @param state The opcode state to be evaluated. + * + * @retval 0 success + * @retval ENOENT if no additional variable definitions are available. + * @retval non-zero if evaluation otherwise fails, a regular unix error + * code will be returned. + */ +static int +sprom_opcode_next_var(struct sprom_opcode_state *state) +{ + uint8_t opcode; + int error; + + /* Step until we hit a variable opcode */ + while ((error = sprom_opcode_step(state, &opcode)) == 0) { + switch (SPROM_OPCODE_OP(opcode)) { + case SPROM_OPCODE_VAR: + case SPROM_OPCODE_VAR_IMM: + case SPROM_OPCODE_VAR_REL_IMM: + BHND_NV_ASSERT( + state->var_state == SPROM_OPCODE_VAR_STATE_OPEN, + ("missing variable definition")); + + return (0); + default: + continue; + } + } + + /* Reached EOF, or evaluation failed */ + return (error); +} + +/** + * Evaluate @p state until the next binding for the current variable definition + * is found. + * + * @param state The opcode state to be evaluated. + * + * @retval 0 success + * @retval ENOENT if no additional binding opcodes are found prior to reaching + * a new variable definition, or the end of @p state's binding opcodes. + * @retval non-zero if evaluation otherwise fails, a regular unix error + * code will be returned. + */ +static int +sprom_opcode_next_binding(struct sprom_opcode_state *state) +{ + uint8_t opcode; + int error; + + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) + return (EINVAL); + + /* Step until we hit a bind opcode, or a new variable */ + while ((error = sprom_opcode_step(state, &opcode)) == 0) { + switch (SPROM_OPCODE_OP(opcode)) { + case SPROM_OPCODE_DO_BIND: + case SPROM_OPCODE_DO_BINDN: + case SPROM_OPCODE_DO_BINDN_IMM: + /* Found next bind */ + BHND_NV_ASSERT( + state->var_state == SPROM_OPCODE_VAR_STATE_OPEN, + ("missing variable definition")); + BHND_NV_ASSERT(state->var.have_bind, ("missing bind")); + + return (0); + + case SPROM_OPCODE_VAR_END: + /* No further binding opcodes */ + BHND_NV_ASSERT( + state->var_state == SPROM_OPCODE_VAR_STATE_DONE, + ("variable definition still available")); + return (ENOENT); + } + } + + /* Not found, or evaluation failed */ + return (error); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h b/sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h new file mode 100644 index 00000000000..82a51b13911 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h @@ -0,0 +1,150 @@ +/*- + * 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_SPROMVAR_H_ +#define _BHND_NVRAM_BHND_NVRAM_SPROMVAR_H_ + +#ifdef _KERNEL +#include +#else +#include +#endif + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_datavar.h" +#include "bhnd_nvram_io.h" + +/** The maximum number of array elements encoded in a single SPROM variable */ +#define SPROM_ARRAY_MAXLEN 12 + +/** + * SPROM opcode per-bind evaluation state. + */ +struct sprom_opcode_bind { + uint8_t count; + uint32_t skip_in; /**< input element skips */ + bool skip_in_negative; /**< skip_in should be subtracted */ + uint32_t skip_out; /**< output element skip */ +}; + +/** + * SPROM opcode per-variable evaluation state. + */ +struct sprom_opcode_var { + uint8_t nelem; /**< variable array length */ + uint32_t mask; /**< current bind input mask */ + int8_t shift; /**< current bind input shift */ + bhnd_nvram_type base_type; /**< current bind input type */ + uint32_t scale; /**< current scale to apply to scaled encodings */ + struct sprom_opcode_bind bind; /**< current bind state */ + bool have_bind; /**< if bind state is defined */ + size_t bind_total; /**< total count of bind operations performed */ +}; + +/** + * SPROM opcode variable definition states. + * + * Ordered to support inequality comparisons + * (e.g. >= SPROM_OPCODE_VAR_STATE_OPEN) + */ +typedef enum { + SPROM_OPCODE_VAR_STATE_NONE = 1, /**< no variable entry available */ + SPROM_OPCODE_VAR_STATE_OPEN = 2, /**< currently parsing a variable entry */ + SPROM_OPCODE_VAR_STATE_DONE = 3 /**< full variable entry has been parsed */ +} sprom_opcode_var_state; + +/** + * SPROM opcode evaluation state + */ +struct sprom_opcode_state { + const struct bhnd_sprom_layout *layout; /**< SPROM layout */ + + /** Current SPROM revision range */ + bitstr_t bit_decl(revs, SPROM_OP_REV_MAX); + + const uint8_t *input; /**< opcode input position */ + + /* State preserved across variable definitions */ + uint32_t offset; /**< SPROM offset */ + size_t vid; /**< Variable ID */ + + /* State reset after end of each variable definition */ + struct sprom_opcode_var var; /**< variable record (if any) */ + sprom_opcode_var_state var_state; /**< variable record state */ +}; + +/** + * SPROM opcode variable index entry + */ +struct sprom_opcode_idx { + uint16_t vid; /**< SPROM variable ID */ + uint16_t offset; /**< SPROM input offset */ + uint16_t opcodes; /**< SPROM opcode offset */ +}; + +/** + * SPROM value storage. + * + * Sufficient for representing the native encoding of any defined SPROM + * variable. + */ +union bhnd_nvram_sprom_storage { + uint8_t u8[SPROM_ARRAY_MAXLEN]; + uint16_t u16[SPROM_ARRAY_MAXLEN]; + uint32_t u32[SPROM_ARRAY_MAXLEN]; + int8_t i8[SPROM_ARRAY_MAXLEN]; + int16_t i16[SPROM_ARRAY_MAXLEN]; + int32_t i32[SPROM_ARRAY_MAXLEN]; + char ch[SPROM_ARRAY_MAXLEN]; +}; + +/** + * SPROM common integer value representation. + */ +union bhnd_nvram_sprom_intv { + uint32_t u32; + int32_t s32; +}; + +/** + * SPROM data class instance state. + */ +struct bhnd_nvram_sprom { + struct bhnd_nvram_data nv; /**< common instance state */ + struct bhnd_nvram_io *data; /**< backing SPROM image */ + const struct bhnd_sprom_layout *layout; /**< layout definition */ + struct sprom_opcode_state state; /**< opcode eval state */ + struct sprom_opcode_idx *idx; /**< opcode index entries */ + size_t num_idx; /**< opcode index entry count */ +}; + +#endif /* _BHND_NVRAM_BHND_NVRAM_SPROMVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c b/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c new file mode 100644 index 00000000000..6d1a5700df4 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c @@ -0,0 +1,662 @@ +/*- + * Copyright (c) 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$"); + +#ifdef _KERNEL +#include +#include +#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_data_tlvreg.h" + +/* + * CFE TLV NVRAM data class. + * + * The CFE-defined TLV NVRAM format is used on the WGT634U. + */ + +struct bhnd_nvram_tlv { + struct bhnd_nvram_data nv; /**< common instance state */ + struct bhnd_nvram_io *data; /**< backing buffer */ + size_t count; /**< variable count */ +}; + +BHND_NVRAM_DATA_CLASS_DEFN(tlv, "WGT634U", sizeof(struct bhnd_nvram_tlv)) + +/** Minimal TLV_ENV record header */ +struct bhnd_nvram_tlv_env_hdr { + uint8_t tag; + uint8_t size; +} __packed; + +/** Minimal TLV_ENV record */ +struct bhnd_nvram_tlv_env { + struct bhnd_nvram_tlv_env_hdr hdr; + uint8_t flags; + char envp[]; +} __packed; + +/* Return the length in bytes of an TLV_ENV's envp data */ +#define NVRAM_TLV_ENVP_DATA_LEN(_env) \ + (((_env)->hdr.size < sizeof((_env)->flags)) ? 0 : \ + ((_env)->hdr.size - sizeof((_env)->flags))) + + +static int bhnd_nvram_tlv_parse_size( + struct bhnd_nvram_io *io, + size_t *size); + +static int bhnd_nvram_tlv_next_record( + struct bhnd_nvram_io *io, + size_t *next, size_t *offset, + uint8_t *tag); + +static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_next_env( + struct bhnd_nvram_tlv *tlv, + size_t *next, void **cookiep); + +static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_get_env( + struct bhnd_nvram_tlv *tlv, + void *cookiep); + +static void *bhnd_nvram_tlv_to_cookie( + struct bhnd_nvram_tlv *tlv, + size_t io_offset); +static size_t bhnd_nvram_tlv_to_offset( + struct bhnd_nvram_tlv *tlv, + void *cookiep); + +static int +bhnd_nvram_tlv_probe(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_tlv_env ident; + size_t nbytes; + int error; + + nbytes = bhnd_nvram_io_getsize(io); + + /* Handle what might be an empty TLV image */ + if (nbytes < sizeof(ident)) { + uint8_t tag; + + /* Fetch just the first tag */ + error = bhnd_nvram_io_read(io, 0x0, &tag, sizeof(tag)); + if (error) + return (error); + + /* This *could* be an empty TLV image, but all we're + * testing for here is a single 0x0 byte followed by EOF */ + if (tag == NVRAM_TLV_TYPE_END) + return (BHND_NVRAM_DATA_PROBE_MAYBE); + + return (ENXIO); + } + + /* Otherwise, look at the initial header for a valid TLV ENV tag, + * plus one byte of the entry data */ + error = bhnd_nvram_io_read(io, 0x0, &ident, + sizeof(ident) + sizeof(ident.envp[0])); + if (error) + return (error); + + /* First entry should be a variable record (which we statically + * assert as being defined to use a single byte size field) */ + if (ident.hdr.tag != NVRAM_TLV_TYPE_ENV) + return (ENXIO); + + _Static_assert(NVRAM_TLV_TYPE_ENV & NVRAM_TLV_TF_U8_LEN, + "TYPE_ENV is not a U8-sized field"); + + /* The entry must be at least 3 characters ('x=\0') in length */ + if (ident.hdr.size < 3) + return (ENXIO); + + /* The first character should be a valid key char (alpha) */ + if (!bhnd_nv_isalpha(ident.envp[0])) + return (ENXIO); + + return (BHND_NVRAM_DATA_PROBE_DEFAULT); +} + +/** + * Initialize @p tlv with the provided NVRAM TLV data mapped by @p src. + * + * @param tlv A newly allocated data instance. + */ +static int +bhnd_nvram_tlv_init(struct bhnd_nvram_tlv *tlv, struct bhnd_nvram_io *src) +{ + struct bhnd_nvram_tlv_env *env; + size_t size; + size_t next; + int error; + + BHND_NV_ASSERT(tlv->data == NULL, ("tlv data already initialized")); + + /* Determine the actual size of the TLV source data */ + if ((error = bhnd_nvram_tlv_parse_size(src, &size))) + return (error); + + /* Copy to our own internal buffer */ + if ((tlv->data = bhnd_nvram_iobuf_copy_range(src, 0x0, size)) == NULL) + return (ENOMEM); + + /* Initialize our backing buffer */ + tlv->count = 0; + next = 0; + while ((env = bhnd_nvram_tlv_next_env(tlv, &next, NULL)) != NULL) { + size_t env_len; + size_t name_len; + + /* TLV_ENV data must not be empty */ + env_len = NVRAM_TLV_ENVP_DATA_LEN(env); + if (env_len == 0) { + BHND_NV_LOG("cannot parse zero-length TLV_ENV record " + "data\n"); + return (EINVAL); + } + + /* Parse the key=value string, and then replace the '=' + * delimiter with '\0' to allow us to provide direct + * name pointers from our backing buffer */ + error = bhnd_nvram_parse_env(env->envp, env_len, '=', NULL, + &name_len, NULL, NULL); + if (error) { + BHND_NV_LOG("error parsing TLV_ENV data: %d\n", error); + return (error); + } + + /* Replace '=' with '\0' */ + *(env->envp + name_len) = '\0'; + + /* Add to variable count */ + tlv->count++; + }; + + return (0); +} + +static int +bhnd_nvram_tlv_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) +{ + + struct bhnd_nvram_tlv *tlv; + int error; + + /* Allocate and initialize the TLV data instance */ + tlv = (struct bhnd_nvram_tlv *)nv; + + /* Parse the TLV input data and initialize our backing + * data representation */ + if ((error = bhnd_nvram_tlv_init(tlv, io))) { + bhnd_nvram_tlv_free(nv); + return (error); + } + + return (0); +} + +static void +bhnd_nvram_tlv_free(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv; + if (tlv->data != NULL) + bhnd_nvram_io_free(tlv->data); +} + +size_t +bhnd_nvram_tlv_count(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv; + return (tlv->count); +} + +static int +bhnd_nvram_tlv_size(struct bhnd_nvram_data *nv, size_t *size) +{ + /* Let the serialization implementation calculate the length */ + return (bhnd_nvram_data_serialize(nv, NULL, size)); +} + +static int +bhnd_nvram_tlv_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len) +{ + struct bhnd_nvram_tlv *tlv; + size_t limit; + size_t next; + uint8_t tag; + int error; + + tlv = (struct bhnd_nvram_tlv *)nv; + + /* Save the buffer capacity */ + if (buf == NULL) + limit = 0; + else + limit = *len; + + /* Write all of our TLV records to the output buffer (or just + * calculate the buffer size that would be required) */ + next = 0; + do { + struct bhnd_nvram_tlv_env *env; + uint8_t *p; + size_t name_len; + size_t rec_offset, rec_size; + + /* Parse the TLV record */ + error = bhnd_nvram_tlv_next_record(tlv->data, &next, + &rec_offset, &tag); + if (error) + return (error); + + rec_size = next - rec_offset; + + /* Calculate our output pointer */ + if (rec_offset > limit || limit - rec_offset < rec_size) { + /* buffer is full; cannot write */ + p = NULL; + } else { + p = (uint8_t *)buf + rec_offset; + } + + /* If not writing, nothing further to do for this record */ + if (p == NULL) + continue; + + /* Copy to the output buffer */ + error = bhnd_nvram_io_read(tlv->data, rec_offset, p, rec_size); + if (error) + return (error); + + /* All further processing is TLV_ENV-specific */ + if (tag != NVRAM_TLV_TYPE_ENV) + continue; + + /* Restore the original key=value format, rewriting '\0' + * delimiter back to '=' */ + env = (struct bhnd_nvram_tlv_env *)p; + name_len = strlen(env->envp); /* skip variable name */ + *(env->envp + name_len) = '='; /* set '=' */ + } while (tag != NVRAM_TLV_TYPE_END); + + /* The 'next' offset should now point at EOF, and represents + * the total length of the serialized output. */ + *len = next; + + if (buf != NULL && limit < *len) + return (ENOMEM); + + return (0); +} + +static uint32_t +bhnd_nvram_tlv_caps(struct bhnd_nvram_data *nv) +{ + return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); +} + +static const char * +bhnd_nvram_tlv_next(struct bhnd_nvram_data *nv, void **cookiep) +{ + struct bhnd_nvram_tlv *tlv; + struct bhnd_nvram_tlv_env *env; + size_t io_offset; + + tlv = (struct bhnd_nvram_tlv *)nv; + + /* Seek past the TLV_ENV record referenced by cookiep */ + io_offset = bhnd_nvram_tlv_to_offset(tlv, *cookiep); + if (bhnd_nvram_tlv_next_env(tlv, &io_offset, NULL) == NULL) + BHND_NV_PANIC("invalid cookiep: %p\n", cookiep); + + /* Fetch the next TLV_ENV record */ + if ((env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep)) == NULL) { + /* No remaining ENV records */ + return (NULL); + } + + /* Return the NUL terminated name */ + return (env->envp); +} + +static void * +bhnd_nvram_tlv_find(struct bhnd_nvram_data *nv, const char *name) +{ + return (bhnd_nvram_data_generic_find(nv, name)); +} + +static int +bhnd_nvram_tlv_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, + size_t *len, bhnd_nvram_type type) +{ + return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); +} + +static const void * +bhnd_nvram_tlv_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type) +{ + struct bhnd_nvram_tlv *tlv; + struct bhnd_nvram_tlv_env *env; + const char *val; + int error; + + tlv = (struct bhnd_nvram_tlv *)nv; + + /* Fetch pointer to the TLV_ENV record */ + if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL) + BHND_NV_PANIC("invalid cookiep: %p", cookiep); + + /* Parse value pointer and length from key\0value data */ + error = bhnd_nvram_parse_env(env->envp, NVRAM_TLV_ENVP_DATA_LEN(env), + '\0', NULL, NULL, &val, len); + if (error) + BHND_NV_PANIC("unexpected error parsing '%s'", env->envp); + + /* Type is always CSTR */ + *type = BHND_NVRAM_TYPE_STRING; + + return (val); +} + +static const char * +bhnd_nvram_tlv_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) +{ + struct bhnd_nvram_tlv *tlv; + const struct bhnd_nvram_tlv_env *env; + + tlv = (struct bhnd_nvram_tlv *)nv; + + /* Fetch pointer to the TLV_ENV record */ + if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL) + BHND_NV_PANIC("invalid cookiep: %p", cookiep); + + /* Return name pointer */ + return (&env->envp[0]); +} + +/** + * Iterate over the records starting at @p next, returning the parsed + * record's @p tag, @p size, and @p offset. + * + * @param io The I/O context to parse. + * @param[in,out] next The next offset to be parsed, or 0x0 + * to begin parsing. Upon successful + * return, will be set to the offset of the + * next record (or EOF, if + * NVRAM_TLV_TYPE_END was parsed). + * @param[out] offset The record's value offset. + * @param[out] tag The record's tag. + * + * @retval 0 success + * @retval EINVAL if parsing @p io as TLV fails. + * @retval non-zero if reading @p io otherwise fails, a regular unix error + * code will be returned. + */ +static int +bhnd_nvram_tlv_next_record(struct bhnd_nvram_io *io, size_t *next, size_t + *offset, uint8_t *tag) +{ + size_t io_offset, io_size; + uint16_t parsed_len; + uint8_t len_hdr[2]; + int error; + + io_offset = *next; + io_size = bhnd_nvram_io_getsize(io); + + /* Save the record offset */ + if (offset != NULL) + *offset = io_offset; + + /* Fetch initial tag */ + error = bhnd_nvram_io_read(io, io_offset, tag, sizeof(*tag)); + if (error) + return (error); + io_offset++; + + /* EOF */ + if (*tag == NVRAM_TLV_TYPE_END) { + *next = io_offset; + return (0); + } + + /* Read length field */ + if (*tag & NVRAM_TLV_TF_U8_LEN) { + error = bhnd_nvram_io_read(io, io_offset, &len_hdr, + sizeof(len_hdr[0])); + if (error) { + BHND_NV_LOG("error reading TLV record size: %d\n", + error); + return (error); + } + + parsed_len = len_hdr[0]; + io_offset++; + } else { + error = bhnd_nvram_io_read(io, io_offset, &len_hdr, + sizeof(len_hdr)); + if (error) { + BHND_NV_LOG("error reading 16-bit TLV record " + "size: %d\n", error); + return (error); + } + + parsed_len = (len_hdr[0] << 8) | len_hdr[1]; + io_offset += 2; + } + + /* Advance to next record */ + if (parsed_len > io_size || io_size - parsed_len < io_offset) { + /* Hit early EOF */ + BHND_NV_LOG("TLV record length %hu truncated by input " + "size of %zu\n", parsed_len, io_size); + return (EINVAL); + } + + *next = io_offset + parsed_len; + + /* Valid record found */ + return (0); +} + +/** + * Parse the TLV data in @p io to determine the total size of the TLV + * data mapped by @p io (which may be less than the size of @p io). + */ +static int +bhnd_nvram_tlv_parse_size(struct bhnd_nvram_io *io, size_t *size) +{ + size_t next; + uint8_t tag; + int error; + + /* We have to perform a minimal parse to determine the actual length */ + next = 0x0; + *size = 0x0; + + /* Iterate over the input until we hit END tag or the read fails */ + do { + error = bhnd_nvram_tlv_next_record(io, &next, NULL, &tag); + if (error) + return (error); + } while (tag != NVRAM_TLV_TYPE_END); + + /* Offset should now point to EOF */ + BHND_NV_ASSERT(next <= bhnd_nvram_io_getsize(io), + ("parse returned invalid EOF offset")); + + *size = next; + return (0); +} + +/** + * Iterate over the records in @p tlv, returning a pointer to the next + * NVRAM_TLV_TYPE_ENV record, or NULL if EOF is reached. + * + * @param tlv The TLV instance. + * @param[in,out] next The next offset to be parsed, or 0x0 + * to begin parsing. Upon successful + * return, will be set to the offset of the + * next record. + */ +static struct bhnd_nvram_tlv_env * +bhnd_nvram_tlv_next_env(struct bhnd_nvram_tlv *tlv, size_t *next, + void **cookiep) +{ + uint8_t tag; + int error; + + /* Find the next TLV_ENV record, starting at @p next */ + do { + void *c; + size_t offset; + + /* Fetch the next TLV record */ + error = bhnd_nvram_tlv_next_record(tlv->data, next, &offset, + &tag); + if (error) { + BHND_NV_LOG("unexpected error in next_record(): %d\n", + error); + return (NULL); + } + + /* Only interested in ENV records */ + if (tag != NVRAM_TLV_TYPE_ENV) + continue; + + /* Map and return TLV_ENV record pointer */ + c = bhnd_nvram_tlv_to_cookie(tlv, offset); + + /* Provide the cookiep value for the returned record */ + if (cookiep != NULL) + *cookiep = c; + + return (bhnd_nvram_tlv_get_env(tlv, c)); + } while (tag != NVRAM_TLV_TYPE_END); + + /* No remaining ENV records */ + return (NULL); +} + +/** + * Return a pointer to the TLV_ENV record for @p cookiep, or NULL + * if none vailable. + */ +static struct bhnd_nvram_tlv_env * +bhnd_nvram_tlv_get_env(struct bhnd_nvram_tlv *tlv, void *cookiep) +{ + struct bhnd_nvram_tlv_env *env; + void *ptr; + size_t navail; + size_t io_offset, io_size; + int error; + + io_size = bhnd_nvram_io_getsize(tlv->data); + io_offset = bhnd_nvram_tlv_to_offset(tlv, cookiep); + + /* At EOF? */ + if (io_offset == io_size) + return (NULL); + + /* Fetch non-const pointer to the record entry */ + error = bhnd_nvram_io_write_ptr(tlv->data, io_offset, &ptr, + sizeof(env->hdr), &navail); + if (error) { + /* Should never occur with a valid cookiep */ + BHND_NV_LOG("error mapping record for cookiep: %d\n", error); + return (NULL); + } + + /* Validate the record pointer */ + env = ptr; + if (env->hdr.tag != NVRAM_TLV_TYPE_ENV) { + /* Should never occur with a valid cookiep */ + BHND_NV_LOG("non-ENV record mapped for %p\n", cookiep); + return (NULL); + } + + /* Is the required variable name data is mapped? */ + if (navail < sizeof(struct bhnd_nvram_tlv_env_hdr) + env->hdr.size || + env->hdr.size == sizeof(env->flags)) + { + /* Should never occur with a valid cookiep */ + BHND_NV_LOG("TLV_ENV variable data not mapped for %p\n", + cookiep); + return (NULL); + } + + return (env); +} + +/** + * Return a cookiep for the given I/O offset. + */ +static void * +bhnd_nvram_tlv_to_cookie(struct bhnd_nvram_tlv *tlv, size_t io_offset) +{ + BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(tlv->data), + ("io_offset %zu out-of-range", io_offset)); + BHND_NV_ASSERT(io_offset < UINTPTR_MAX, + ("io_offset %#zx exceeds UINTPTR_MAX", io_offset)); + + return ((void *)(uintptr_t)(io_offset)); +} + +/* Convert a cookiep back to an I/O offset */ +static size_t +bhnd_nvram_tlv_to_offset(struct bhnd_nvram_tlv *tlv, void *cookiep) +{ + size_t io_size; + uintptr_t cval; + + io_size = bhnd_nvram_io_getsize(tlv->data); + cval = (uintptr_t)cookiep; + + BHND_NV_ASSERT(cval < SIZE_MAX, ("cookie > SIZE_MAX)")); + BHND_NV_ASSERT(cval <= io_size, ("cookie > io_size)")); + + return ((size_t)cval); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_parserreg.h b/sys/dev/bhnd/nvram/bhnd_nvram_data_tlvreg.h similarity index 58% rename from sys/dev/bhnd/nvram/bhnd_nvram_parserreg.h rename to sys/dev/bhnd/nvram/bhnd_nvram_data_tlvreg.h index defcf90d1d4..0daa745aa56 100644 --- a/sys/dev/bhnd/nvram/bhnd_nvram_parserreg.h +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_tlvreg.h @@ -29,47 +29,12 @@ * $FreeBSD$ */ -#ifndef _BHND_NVRAM_BHND_NVRAM_PARSERREG_H_ -#define _BHND_NVRAM_BHND_NVRAM_PARSERREG_H_ - - - -#define NVRAM_GET_BITS(_value, _field) \ - ((_value & _field ## _MASK) >> _field ## _SHIFT) - -/* NVRAM header fields */ -#define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */ -#define NVRAM_VERSION 1 - -#define NVRAM_CRC_SKIP 9 /* skip magic, size, and crc8 */ - -#define NVRAM_CFG0_CRC_MASK 0x000000FF -#define NVRAM_CFG0_CRC_SHIFT 0 -#define NVRAM_CFG0_VER_MASK 0x0000FF00 -#define NVRAM_CFG0_VER_SHIFT 8 -#define NVRAM_CFG0_SDRAM_INIT_MASK 0xFFFF0000 -#define NVRAM_CFG0_SDRAM_INIT_SHIFT 16 -#define NVRAM_CFG0_SDRAM_INIT_VAR "sdram_init" -#define NVRAM_CFG0_SDRAM_INIT_FMT "0x%04x" - -#define NVRAM_CFG1_SDRAM_CFG_MASK 0x0000FFFF -#define NVRAM_CFG1_SDRAM_CFG_SHIFT 0 -#define NVRAM_CFG1_SDRAM_CFG_VAR "sdram_config" -#define NVRAM_CFG1_SDRAM_CFG_FMT "0x%04x" - -#define NVRAM_CFG1_SDRAM_REFRESH_MASK 0xFFFF0000 -#define NVRAM_CFG1_SDRAM_REFRESH_SHIFT 16 -#define NVRAM_CFG1_SDRAM_REFRESH_VAR "sdram_refresh" -#define NVRAM_CFG1_SDRAM_REFRESH_FMT "0x%04x" - -#define NVRAM_SDRAM_NCDL_MASK UINT32_MAX -#define NVRAM_SDRAM_NCDL_SHIFT 0 -#define NVRAM_SDRAM_NCDL_VAR "sdram_ncdl" -#define NVRAM_SDRAM_NCDL_FMT "0x%08x" +#ifndef _BHND_NVRAM_BHND_NVRAM_TLVREG_H_ +#define _BHND_NVRAM_BHND_NVRAM_TLVREG_H_ /* WGT634U-specific TLV encoding */ #define NVRAM_TLV_TF_U8_LEN 0x01 /**< type has 8-bit length */ #define NVRAM_TLV_TYPE_END 0x00 /**< end of table */ #define NVRAM_TLV_TYPE_ENV 0x01 /**< variable record */ -#endif /* _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_ */ +#endif /* _BHND_NVRAM_BHND_NVRAM_TLVREG_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h b/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h new file mode 100644 index 00000000000..50a9f44338f --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h @@ -0,0 +1,188 @@ +/*- + * Copyright (c) 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_DATAVAR_H_ +#define _BHND_NVRAM_BHND_NVRAM_DATAVAR_H_ + +#include +#include +#include + +#include "bhnd_nvram_io.h" + +#include "bhnd_nvram_data.h" + +/** Registered NVRAM parser class instances. */ +SET_DECLARE(bhnd_nvram_data_class_set, bhnd_nvram_data_class_t); + +void *bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv, + const char *name); +int bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, + void *cookiep, void *outp, size_t *olen, bhnd_nvram_type otype); + +/** @see bhnd_nvram_data_class_desc() */ +typedef const char *(bhnd_nvram_data_op_class_desc)(void); + +/** @see bhnd_nvram_data_probe() */ +typedef int (bhnd_nvram_data_op_probe)(struct bhnd_nvram_io *io); + +/** @see bhnd_nvram_data_new() */ +typedef int (bhnd_nvram_data_op_new)(struct bhnd_nvram_data *nv, + struct bhnd_nvram_io *io); + +/** Free all resources associated with @p nv. Called by + * bhnd_nvram_data_release() when the reference count reaches zero. */ +typedef void (bhnd_nvram_data_op_free)(struct bhnd_nvram_data *nv); + +/** @see bhnd_nvram_data_count() */ +typedef size_t (bhnd_nvram_data_op_count)(struct bhnd_nvram_data *nv); + +/** @see bhnd_nvram_data_size() */ +typedef int (bhnd_nvram_data_op_size)(struct bhnd_nvram_data *nv, + size_t *len); + +/** @see bhnd_nvram_data_serialize() */ +typedef int (bhnd_nvram_data_op_serialize)( + struct bhnd_nvram_data *nv, void *buf, + size_t *len); + +/** @see bhnd_nvram_data_caps() */ +typedef uint32_t (bhnd_nvram_data_op_caps)(struct bhnd_nvram_data *nv); + +/** @see bhnd_nvram_data_next() */ +typedef const char *(bhnd_nvram_data_op_next)(struct bhnd_nvram_data *nv, + void **cookiep); + +/** @see bhnd_nvram_data_find() */ +typedef void *(bhnd_nvram_data_op_find)(struct bhnd_nvram_data *nv, + const char *name); + +/** @see bhnd_nvram_data_getvar_name() */ +typedef const char *(bhnd_nvram_data_op_getvar_name)( + struct bhnd_nvram_data *nv, void *cookiep); + +/** @see bhnd_nvram_data_getvar() */ +typedef int (bhnd_nvram_data_op_getvar)(struct bhnd_nvram_data *nv, + void *cookiep, void *buf, size_t *len, + bhnd_nvram_type type); + +/** @see bhnd_nvram_data_getvar_ptr() */ +typedef const void *(bhnd_nvram_data_op_getvar_ptr)( + struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type); + +/** + * NVRAM data class. + */ +struct bhnd_nvram_data_class { + const char *desc; /**< description */ + size_t size; /**< instance size */ + bhnd_nvram_data_op_probe *op_probe; + bhnd_nvram_data_op_new *op_new; + bhnd_nvram_data_op_free *op_free; + bhnd_nvram_data_op_count *op_count; + bhnd_nvram_data_op_size *op_size; + bhnd_nvram_data_op_serialize *op_serialize; + bhnd_nvram_data_op_caps *op_caps; + bhnd_nvram_data_op_next *op_next; + bhnd_nvram_data_op_find *op_find; + bhnd_nvram_data_op_getvar *op_getvar; + bhnd_nvram_data_op_getvar_ptr *op_getvar_ptr; + bhnd_nvram_data_op_getvar_name *op_getvar_name; +}; + +/** + * NVRAM data instance. + */ +struct bhnd_nvram_data { + struct bhnd_nvram_data_class *cls; + volatile u_int refs; +}; + +/* + * Helper macro for BHND_NVRAM_DATA_CLASS_DEFN(). + * + * Declares a bhnd_nvram_data_class method implementation with class name + * _cname and method name _mname + */ +#define BHND_NVRAM_DATA_CLASS_DECL_METHOD(_cname, _mname) \ + static bhnd_nvram_data_op_ ## _mname \ + bhnd_nvram_ ## _cname ## _ ## _mname; \ + +/* + * Helper macro for BHND_NVRAM_DATA_CLASS_DEFN(). + * + * Assign a bhnd_nvram_data_class method implementation with class name + * @p _cname and method name @p _mname + */ +#define BHND_NVRAM_DATA_CLASS_ASSIGN_METHOD(_cname, _mname) \ + .op_ ## _mname = bhnd_nvram_ ## _cname ## _ ## _mname, + +/* + * Helper macro for BHND_NVRAM_DATA_CLASS_DEFN(). + * + * Iterate over all bhnd_nvram_data_class method names, calling + * _macro with the class name _cname as the first argument, and + * a bhnd_nvram_data_class method name as the second. + */ +#define BHND_NVRAM_DATA_CLASS_ITER_METHODS(_cname, _macro) \ + _macro(_cname, probe) \ + _macro(_cname, new) \ + _macro(_cname, free) \ + _macro(_cname, count) \ + _macro(_cname, size) \ + _macro(_cname, serialize) \ + _macro(_cname, caps) \ + _macro(_cname, next) \ + _macro(_cname, find) \ + _macro(_cname, getvar) \ + _macro(_cname, getvar_ptr) \ + _macro(_cname, getvar_name) + +/** + * Define a bhnd_nvram_data_class with class name @p _n and description + * @p _desc, and register with bhnd_nvram_data_class_set. + */ +#define BHND_NVRAM_DATA_CLASS_DEFN(_cname, _desc, _size) \ + BHND_NVRAM_DATA_CLASS_ITER_METHODS(_cname, \ + BHND_NVRAM_DATA_CLASS_DECL_METHOD) \ + \ + struct bhnd_nvram_data_class bhnd_nvram_## _cname ## _class = { \ + .desc = (_desc), \ + .size = (_size), \ + BHND_NVRAM_DATA_CLASS_ITER_METHODS(_cname, \ + BHND_NVRAM_DATA_CLASS_ASSIGN_METHOD) \ + }; \ + \ + DATA_SET(bhnd_nvram_data_class_set, \ + bhnd_nvram_## _cname ## _class); + +#endif /* _BHND_NVRAM_BHND_NVRAM_DATAVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_io.c b/sys/dev/bhnd/nvram/bhnd_nvram_io.c new file mode 100644 index 00000000000..7ceb33e5cbc --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_io.c @@ -0,0 +1,205 @@ +/*- + * Copyright (c) 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$"); + +#ifdef _KERNEL +#include +#else /* !_KERNEL */ +#include +#include +#include +#endif /* _KERNEL */ + +#include "bhnd_nvram_io.h" +#include "bhnd_nvram_iovar.h" + +/** + * Read exactly @p nbytes from @p io at @p offset. + * + * @param io NVRAM I/O context. + * @param offset The offset within @p io at which to perform the read. + * @param[out] buffer Output buffer to which @p nbytes from @p io will be + * written. + * @param nbytes The maximum number of bytes to be read from @p io. + * + * @retval 0 success + * @retval EIO if an input error occured reading @p io. + * @retval ENXIO if the request for @p offset or @p nbytes exceeds the size + * of @p io. + * @retval EFAULT if @p io requires I/O request alignment and @p offset is + * misaligned. + * @retval EFAULT if @p io requires I/O request alignment and @p nbytes is + * misaligned and cannot be rounded down to an aligned non-zero value. + */ +int +bhnd_nvram_io_read(struct bhnd_nvram_io *io, size_t offset, void *buffer, + size_t nbytes) +{ + return (io->iops->read(io, offset, buffer, nbytes)); +} + + +/** + * Attempt to fetch a pointer to @p io's internal read buffer, if + * supported by @p io. + * + * The returned pointer is only gauranteed to be valid until the next I/O + * operation performed on @p io; concrete implementations of bhnd_nvram_io + * may provide stronger gaurantees. + * + * @param io NVRAM I/O context. + * @param offset The offset within @p io for which to return a buffer pointer. + * @param[out] ptr On success, will be initialized with a pointer to @p io's + * internal read buffer. + * @param nbytes The minimum number of bytes that must be readable at @p offset. + * @param[out] navail The actual number of readable bytes, which may be greater + * than @p nbytes. If this value is not required, a NULL pointer may be + * provided. + * + * @retval 0 success + * @retval EIO if an input error occured reading @p io. + * @retval ENODEV if @p io does not support direct access to its backing read + * buffer. + * @retval ENXIO if the request exceeds the size of @p io. + * @retval EFAULT if @p io requires I/O request alignment and @p offset or + * @p nbytes are misaligned. + */ +int +bhnd_nvram_io_read_ptr(struct bhnd_nvram_io *io, size_t offset, + const void **ptr, size_t nbytes, size_t *navail) +{ + return (io->iops->read_ptr(io, offset, ptr, nbytes, navail)); +} + +/** + * Write @p nbytes to @p io at @p offset. + * + * @param io NVRAM I/O context. + * @param offset The offset within @p io at which to perform the write. + * @param buffer Data to be written to @p io. + * @param nbytes The number of bytes to be written from @p buffer. + * + * @retval 0 success + * @retval EIO if an output error occurs writing to @p io. + * @retval ENODEV if @p io does not support writing. + * @retval ENXIO if @p io does not support writes beyond the existing + * end-of-file, and a write at @p offset would exceed the size of the @p io + * backing data store. + * @retval EFAULT if @p io requires I/O request alignment and @p offset or + * @p nbytes are misaligned. + */ +int +bhnd_nvram_io_write(struct bhnd_nvram_io *io, size_t offset, void *buffer, + size_t nbytes) +{ + return (io->iops->write(io, offset, buffer, nbytes)); +} + +/** + * Attempt to fetch a writable pointer to @p io's internal write buffer, if + * supported by @p io. + * + * The returned pointer is only gauranteed to be valid until the next I/O + * operation performed on @p io; concrete implementations of bhnd_nvram_io + * may provide stronger gaurantees. + * + * @param io NVRAM I/O context. + * @param offset The offset within @p io for which to return a buffer pointer. + * @param[in,out] ptr On success, will be initialized with a pointer to @p io's + * internal buffer at which up to @p nbytes may be written. + * @param nbytes The minimum number of bytes that must be writable at @p offset. + * @param[out] navail The actual number of writable bytes, which may be greater + * than @p nbytes. If this value is not required, a NULL pointer may be + * provided. + * + * @retval 0 success + * @retval EIO if an output error occurs preparing @p io's write buffer. + * @retval ENODEV if @p io does not support direct access to its backing write + * buffer. + * @retval ENXIO if @p io does not support writes beyond the existing + * end-of-file, and a write at @p offset of @p nbytes would exceed the size of + * the @p io backing data store. + * @retval EFAULT if @p io requires I/O request alignment and @p offset or + * @p nbytes are misaligned. + */ +int +bhnd_nvram_io_write_ptr(struct bhnd_nvram_io *io, size_t offset, void **ptr, + size_t nbytes, size_t *navail) +{ + return (io->iops->write_ptr(io, offset, ptr, nbytes, navail)); +} + +/** + * Return the total number of bytes readable via @p io. + * + * @param io NVRAM I/O context. + */ +size_t +bhnd_nvram_io_getsize(struct bhnd_nvram_io *io) +{ + return (io->iops->getsize(io)); +} + +/** + * Attempt to set the size of @p io to @p size. + * + * If the total size of @p io is increased, the contents of the newly mapped + * bytes are undefined; concrete implementations of bhnd_nvram_io may + * provide stronger gaurantees. + * + * @param io NVRAM I/O context. + * @param size The new size. + * + * @retval 0 success + * @retval EIO if an I/O error occurs resizing @p io. + * @retval ENODEV if @p io does not support resizing. + * @retval ENXIO if @p size exceeds the capacity or other limits of @p io. + * @retval EFAULT if @p io requires I/O request alignment and @p size is + * misaligned. + */ +int +bhnd_nvram_io_setsize(struct bhnd_nvram_io *io, size_t size) +{ + return (io->iops->setsize(io, size)); +} + +/** + * Free a previously allocated I/O context, releasing all associated + * resources. + * + * @param io The I/O context to be freed. + */ +void +bhnd_nvram_io_free(struct bhnd_nvram_io *io) +{ + return (io->iops->free(io)); +} + diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_io.h b/sys/dev/bhnd/nvram/bhnd_nvram_io.h new file mode 100644 index 00000000000..fcec2d7b1dd --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_io.h @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 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_IO_H_ +#define _BHND_NVRAM_BHND_NVRAM_IO_H_ + +#ifdef _KERNEL +#include + +#include +#else /* !_KERNEL */ +#include + +#include +#include +#include +#endif /* _KERNEL */ + +struct bhnd_nvram_io; + +struct bhnd_nvram_io *bhnd_nvram_iobuf_new(const void *buffer, size_t size); +struct bhnd_nvram_io *bhnd_nvram_iobuf_empty(size_t size, size_t capacity); +struct bhnd_nvram_io *bhnd_nvram_iobuf_copy(struct bhnd_nvram_io *src); +struct bhnd_nvram_io *bhnd_nvram_iobuf_copy_range(struct bhnd_nvram_io *src, + size_t offset, size_t size); + +#ifdef _KERNEL +struct bhnd_nvram_io *bhnd_nvram_iores_new(struct bhnd_resource *r, + bus_size_t offset, bus_size_t size, + u_int bus_width); +#endif /* _KERNEL */ + +size_t bhnd_nvram_io_getsize(struct bhnd_nvram_io *io); +int bhnd_nvram_io_setsize(struct bhnd_nvram_io *io, + size_t size); + +int bhnd_nvram_io_read(struct bhnd_nvram_io *io, + size_t offset, void *buffer, size_t nbytes); +int bhnd_nvram_io_read_ptr(struct bhnd_nvram_io *io, + size_t offset, const void **ptr, size_t nbytes, + size_t *navail); + +int bhnd_nvram_io_write(struct bhnd_nvram_io *io, + size_t offset, void *buffer, size_t nbytes); +int bhnd_nvram_io_write_ptr(struct bhnd_nvram_io *io, + size_t offset, void **ptr, size_t nbytes, + size_t *navail); + +void bhnd_nvram_io_free(struct bhnd_nvram_io *io); + +#endif /* _BHND_NVRAM_BHND_NVRAM_IO_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_iobuf.c b/sys/dev/bhnd/nvram/bhnd_nvram_iobuf.c new file mode 100644 index 00000000000..a36cbb36c63 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_iobuf.c @@ -0,0 +1,341 @@ +/*- + * Copyright (c) 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$"); + +#ifdef _KERNEL +#include +#include +#include +#else /* !_KERNEL */ +#include +#include +#include +#include +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_io.h" +#include "bhnd_nvram_iovar.h" + +/** + * Buffer-backed NVRAM I/O context. + * + * iobuf instances are gauranteed to provide persistent references to its + * backing contigious buffer via bhnd_nvram_io_read_ptr() and + * bhnd_nvram_io_write_ptr(). + */ +struct bhnd_nvram_iobuf { + struct bhnd_nvram_io io; /**< common I/O instance state */ + void *buf; /**< backing buffer. if inline-allocated, will + be a reference to data[]. */ + size_t size; /**< size of @p buf */ + size_t capacity; /**< capacity of @p buf */ + uint8_t data[]; /**< inline buffer allocation */ +}; + +BHND_NVRAM_IOPS_DEFN(iobuf) + +/** + * Allocate and return a new I/O context with an uninitialized + * buffer of @p size and @p capacity. + * + * The caller is responsible for deallocating the returned I/O context via + * bhnd_nvram_io_free(). + * + * If @p capacity is less than @p size, a capacity of @p size will be used. + * + * @param size The initial size of the I/O context. + * @param capacity The total capacity of the I/O context buffer; + * the returned I/O context may be resized up to + * @p capacity via bhnd_nvram_io_setsize(). + * + * @retval bhnd_nvram_iobuf success. + * @retval NULL allocation failed. + * @retval NULL the requested @p capacity is less than + * @p size. + */ +struct bhnd_nvram_io * +bhnd_nvram_iobuf_empty(size_t size, size_t capacity) +{ + struct bhnd_nvram_iobuf *iobuf; + size_t iosz; + bool inline_alloc; + + /* Sanity check the capacity */ + if (size > capacity) + return (NULL); + + /* Would sizeof(iobuf)+capacity overflow? */ + if (SIZE_MAX - sizeof(*iobuf) < capacity) { + inline_alloc = false; + iosz = sizeof(*iobuf); + } else { + inline_alloc = true; + iosz = sizeof(*iobuf) + capacity; + } + + /* Allocate I/O context */ + iobuf = bhnd_nv_malloc(iosz); + if (iobuf == NULL) + return (NULL); + + iobuf->io.iops = &bhnd_nvram_iobuf_ops; + iobuf->buf = NULL; + iobuf->size = size; + iobuf->capacity = capacity; + + /* Either allocate our backing buffer, or initialize the + * backing buffer with a reference to our inline allocation. */ + if (inline_alloc) + iobuf->buf = &iobuf->data; + else + iobuf->buf = bhnd_nv_malloc(iobuf->capacity); + + + if (iobuf->buf == NULL) { + bhnd_nv_free(iobuf); + return (NULL); + } + + return (&iobuf->io); +} + +/** + * Allocate and return a new I/O context, copying @p size from @p buffer. + * + * The caller is responsible for deallocating the returned I/O context via + * bhnd_nvram_io_free(). + * + * @param buffer The buffer data be copied by the returned I/O context. + * @param size The size of @p buffer, in bytes. + * + * @retval bhnd_nvram_io success. + * @retval NULL allocation failed. + */ +struct bhnd_nvram_io * +bhnd_nvram_iobuf_new(const void *buffer, size_t size) +{ + struct bhnd_nvram_io *io; + struct bhnd_nvram_iobuf *iobuf; + + /* Allocate the iobuf */ + if ((io = bhnd_nvram_iobuf_empty(size, size)) == NULL) + return (NULL); + + /* Copy the input to our new iobuf instance */ + iobuf = (struct bhnd_nvram_iobuf *)io; + memcpy(iobuf->buf, buffer, iobuf->size); + + return (io); +} + +/** + * Allocate and return a new I/O context providing an in-memory copy + * of the data mapped by @p src. + * + * The caller is responsible for deallocating the returned I/O context via + * bhnd_nvram_io_free(). + * + * @param src The I/O context to be copied. + * + * @retval bhnd_nvram_io success. + * @retval NULL allocation failed. + * @retval NULL copying @p src failed. + */ +struct bhnd_nvram_io * +bhnd_nvram_iobuf_copy(struct bhnd_nvram_io *src) +{ + return (bhnd_nvram_iobuf_copy_range(src, 0x0, + bhnd_nvram_io_getsize(src))); +} + +/** + * Allocate and return a new I/O context providing an in-memory copy + * of @p size bytes mapped at @p offset by @p src. + * + * The caller is responsible for deallocating the returned I/O context via + * bhnd_nvram_io_free(). + * + * @param src The I/O context to be copied. + * @param offset The offset of the bytes to be copied from @p src. + * @param size The number of bytes to copy at @p offset from @p src. + * + * @retval bhnd_nvram_io success. + * @retval NULL allocation failed. + * @retval NULL copying @p src failed. + */ +struct bhnd_nvram_io * +bhnd_nvram_iobuf_copy_range(struct bhnd_nvram_io *src, size_t offset, + size_t size) +{ + struct bhnd_nvram_io *io; + struct bhnd_nvram_iobuf *iobuf; + int error; + + /* Check if offset+size would overflow */ + if (SIZE_MAX - size < offset) + return (NULL); + + /* Allocate the iobuf instance */ + if ((io = bhnd_nvram_iobuf_empty(size, size)) == NULL) + return (NULL); + + /* Copy the input I/O context */ + iobuf = (struct bhnd_nvram_iobuf *)io; + if ((error = bhnd_nvram_io_read(src, offset, iobuf->buf, size))) { + bhnd_nvram_io_free(&iobuf->io); + return (NULL); + } + + return (io); +} + + +static void +bhnd_nvram_iobuf_free(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io; + + /* Free the backing buffer if it wasn't allocated inline */ + if (iobuf->buf != &iobuf->data) + bhnd_nv_free(iobuf->buf); + + bhnd_nv_free(iobuf); +} + +static size_t +bhnd_nvram_iobuf_getsize(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io; + return (iobuf->size); +} + +static int +bhnd_nvram_iobuf_setsize(struct bhnd_nvram_io *io, size_t size) +{ + struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io; + + /* Can't exceed the actual capacity */ + if (size > iobuf->capacity) + return (ENXIO); + + iobuf->size = size; + return (0); +} + +/* Common iobuf_(read|write)_ptr implementation */ +static int +bhnd_nvram_iobuf_ptr(struct bhnd_nvram_iobuf *iobuf, size_t offset, void **ptr, + size_t nbytes, size_t *navail) +{ + size_t avail; + + /* Verify offset+nbytes fall within the buffer range */ + if (offset > iobuf->size) + return (ENXIO); + + avail = iobuf->size - offset; + if (avail < nbytes) + return (ENXIO); + + /* Valid I/O range, provide a pointer to the buffer and the + * total count of available bytes */ + *ptr = ((uint8_t *)iobuf->buf) + offset; + if (navail != NULL) + *navail = avail; + + return (0); +} + +static int +bhnd_nvram_iobuf_read_ptr(struct bhnd_nvram_io *io, size_t offset, + const void **ptr, size_t nbytes, size_t *navail) +{ + struct bhnd_nvram_iobuf *iobuf; + void *ioptr; + int error; + + iobuf = (struct bhnd_nvram_iobuf *) io; + + /* Return a pointer into our backing buffer */ + error = bhnd_nvram_iobuf_ptr(iobuf, offset, &ioptr, nbytes, navail); + if (error) + return (error); + + *ptr = ioptr; + + return (0); +} + +static int +bhnd_nvram_iobuf_write_ptr(struct bhnd_nvram_io *io, size_t offset, + void **ptr, size_t nbytes, size_t *navail) +{ + struct bhnd_nvram_iobuf *iobuf; + + iobuf = (struct bhnd_nvram_iobuf *) io; + + /* Return a pointer into our backing buffer */ + return (bhnd_nvram_iobuf_ptr(iobuf, offset, ptr, nbytes, navail)); +} + +static int +bhnd_nvram_iobuf_read(struct bhnd_nvram_io *io, size_t offset, void *buffer, + size_t nbytes) +{ + const void *ptr; + int error; + + /* Try to fetch a direct pointer for at least nbytes */ + if ((error = bhnd_nvram_io_read_ptr(io, offset, &ptr, nbytes, NULL))) + return (error); + + /* Copy out the requested data */ + memcpy(buffer, ptr, nbytes); + return (0); +} + +static int +bhnd_nvram_iobuf_write(struct bhnd_nvram_io *io, size_t offset, + void *buffer, size_t nbytes) +{ + void *ptr; + int error; + + /* Try to fetch a direct pointer for at least nbytes */ + if ((error = bhnd_nvram_io_write_ptr(io, offset, &ptr, nbytes, NULL))) + return (error); + + /* Copy in the provided data */ + memcpy(ptr, buffer, nbytes); + return (0); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_iores.c b/sys/dev/bhnd/nvram/bhnd_nvram_iores.c new file mode 100644 index 00000000000..70d2d398cde --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_iores.c @@ -0,0 +1,302 @@ +/*- + * Copyright (c) 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 +#include +#include + +#include + +#include + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_io.h" +#include "bhnd_nvram_iovar.h" + +/** + * BHND resource-backed NVRAM I/O context. + */ +struct bhnd_nvram_iores { + struct bhnd_nvram_io io; /**< common I/O instance state */ + struct bhnd_resource *res; /**< backing resource (borrowed ref) */ + size_t offset; /**< offset within res */ + size_t size; /**< size relative to the base offset */ + u_int bus_width; /**< data type byte width to be used + when performing bus operations + on res. (1, 2, or 4 bytes) */ +}; + +BHND_NVRAM_IOPS_DEFN(iores); + +/** + * Allocate and return a new I/O context backed by a borrowed reference to @p r. + * + * The caller is responsible for deallocating the returned I/O context via + * bhnd_nvram_io_free(). + * + * @param r The resource to be mapped by the returned I/O + * context. + * @param offset Offset + * @param bus_width The required I/O width (1, 2, or 4 bytes) to be + * used when reading from @p r. + * + * @retval bhnd_nvram_io success. + * @retval NULL if allocation fails, or an invalid argument + * is supplied. + */ +struct bhnd_nvram_io * +bhnd_nvram_iores_new(struct bhnd_resource *r, bus_size_t offset, + bus_size_t size, u_int bus_width) +{ + struct bhnd_nvram_iores *iores; + rman_res_t r_start, r_size; + + /* Verify the bus width */ + switch (bus_width) { + case 1: + case 2: + case 4: + /* valid */ + break; + default: + BHND_NV_LOG("invalid bus width %u\n", bus_width); + return (NULL); + } + + /* offset/size must not exceed our internal size_t representation, + * or our bus_size_t usage (note that BUS_SPACE_MAXSIZE may be less + * than 2^(sizeof(bus_size_t) * 32). */ + if (size > SIZE_MAX || offset > SIZE_MAX) { + BHND_NV_LOG("offset %#jx+%#jx exceeds SIZE_MAX\n", + (uintmax_t)offset, (uintmax_t)offset); + return (NULL); + } + + if (size > BUS_SPACE_MAXSIZE || offset > BUS_SPACE_MAXSIZE) + { + BHND_NV_LOG("offset %#jx+%#jx exceeds BUS_SPACE_MAXSIZE\n", + (uintmax_t)offset, (uintmax_t)offset); + return (NULL); + } + + /* offset/size fall within the resource's mapped range */ + r_size = rman_get_size(r->res); + r_start = rman_get_start(r->res); + if (r_size < offset || r_size < size || r_size - size < offset) + return (NULL); + + /* offset/size must be bus_width aligned */ + if ((r_start + offset) % bus_width != 0) { + BHND_NV_LOG("base address %#jx+%#jx not aligned to bus width " + "%u\n", (uintmax_t)r_start, (uintmax_t)offset, bus_width); + return (NULL); + } + + if (size % bus_width != 0) { + BHND_NV_LOG("size %#jx not aligned to bus width %u\n", + (uintmax_t)size, bus_width); + return (NULL); + } + + /* Allocate and return the I/O context */ + iores = malloc(sizeof(*iores), M_BHND_NVRAM, M_WAITOK); + iores->io.iops = &bhnd_nvram_iores_ops; + iores->res = r; + iores->offset = offset; + iores->size = size; + iores->bus_width = bus_width; + + return (&iores->io); +} + +static void +bhnd_nvram_iores_free(struct bhnd_nvram_io *io) +{ + free(io, M_BHND_NVRAM); +} + +static size_t +bhnd_nvram_iores_getsize(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_iores *iores = (struct bhnd_nvram_iores *)io; + return (iores->size); +} + +static int +bhnd_nvram_iores_setsize(struct bhnd_nvram_io *io, size_t size) +{ + /* unsupported */ + return (ENODEV); +} + +static int +bhnd_nvram_iores_read_ptr(struct bhnd_nvram_io *io, size_t offset, + const void **ptr, size_t nbytes, size_t *navail) +{ + /* unsupported */ + return (ENODEV); +} + +static int +bhnd_nvram_iores_write_ptr(struct bhnd_nvram_io *io, size_t offset, + void **ptr, size_t nbytes, size_t *navail) +{ + /* unsupported */ + return (ENODEV); +} + +/** + * Validate @p offset and @p nbytes: + * + * - Verify that @p offset is mapped by the backing resource. + * - If less than @p nbytes are available at @p offset, write the actual number + * of bytes available to @p nbytes. + * - Verify that @p offset + @p nbytes are correctly aligned. + */ +static int +bhnd_nvram_iores_validate_req(struct bhnd_nvram_iores *iores, size_t offset, + size_t *nbytes) +{ + /* Verify offset falls within the resource range */ + if (offset > iores->size) + return (ENXIO); + + /* Check for eof */ + if (offset == iores->size) { + *nbytes = 0; + return (0); + } + + /* Verify offset alignment */ + if (offset % iores->bus_width != 0) + return (EFAULT); + + /* Limit nbytes to available range and verify size alignment */ + *nbytes = ummin(*nbytes, iores->size - offset); + if (*nbytes < iores->bus_width && *nbytes % iores->bus_width != 0) + return (EFAULT); + + return (0); +} + + +static int +bhnd_nvram_iores_read(struct bhnd_nvram_io *io, size_t offset, void *buffer, + size_t nbytes) +{ + struct bhnd_nvram_iores *iores; + bus_size_t r_offset; + size_t navail; + int error; + + iores = (struct bhnd_nvram_iores *)io; + + /* Validate the request and determine the actual number of readable + * bytes */ + navail = nbytes; + if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail))) + return (error); + + /* At least nbytes must be readable */ + if (navail < nbytes) + return (ENXIO); + + /* Handle zero length read */ + if (nbytes == 0) + return (0); + + /* Determine actual resource offset and perform the read */ + r_offset = iores->offset + offset; + switch (iores->bus_width) { + case 1: + bhnd_bus_read_region_stream_1(iores->res, r_offset, buffer, + nbytes); + break; + case 2: + bhnd_bus_read_region_stream_2(iores->res, r_offset, buffer, + nbytes / 2); + break; + case 4: + bhnd_bus_read_region_stream_4(iores->res, r_offset, buffer, + nbytes / 4); + break; + default: + panic("unreachable!"); + } + + return (0); +} + +static int +bhnd_nvram_iores_write(struct bhnd_nvram_io *io, size_t offset, + void *buffer, size_t nbytes) +{ + struct bhnd_nvram_iores *iores; + size_t navail; + bus_size_t r_offset; + int error; + + iores = (struct bhnd_nvram_iores *)io; + + /* Validate the request and determine the actual number of writable + * bytes */ + navail = nbytes; + if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail))) + return (error); + + /* At least nbytes must be writable */ + if (navail < nbytes) + return (ENXIO); + + /* Determine actual resource offset and perform the write */ + r_offset = iores->offset + offset; + switch (iores->bus_width) { + case 1: + bhnd_bus_write_region_stream_1(iores->res, r_offset, buffer, + nbytes); + break; + case 2: + bhnd_bus_write_region_stream_2(iores->res, r_offset, buffer, + nbytes / 2); + break; + case 4: + bhnd_bus_write_region_stream_4(iores->res, r_offset, buffer, + nbytes / 4); + break; + default: + panic("unreachable!"); + } + + return (0); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_iovar.h b/sys/dev/bhnd/nvram/bhnd_nvram_iovar.h new file mode 100644 index 00000000000..137f505dddb --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_iovar.h @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 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_IOVAR_H_ +#define _BHND_NVRAM_BHND_NVRAM_IOVAR_H_ + +#include + +#include "bhnd_nvram_io.h" + +/** @see bhnd_nvram_io_read() */ +typedef int (bhnd_nvram_iop_read)(struct bhnd_nvram_io *io, size_t offset, + void *buffer, size_t nbytes); + +/** @see bhnd_nvram_io_read_ptr() */ +typedef int (bhnd_nvram_iop_read_ptr)(struct bhnd_nvram_io *io, size_t offset, + const void **ptr, size_t nbytes, size_t *navail); + +/** @see bhnd_nvram_io_write() */ +typedef int (bhnd_nvram_iop_write)(struct bhnd_nvram_io *io, size_t offset, + void *buffer, size_t nbytes); + +/** @see bhnd_nvram_io_write_ptr() */ +typedef int (bhnd_nvram_iop_write_ptr)(struct bhnd_nvram_io *io, size_t offset, + void **ptr, size_t nbytes, size_t *navail); + +/** @see bhnd_nvram_io_getsize() */ +typedef size_t (bhnd_nvram_iop_getsize)(struct bhnd_nvram_io *io); + +/** @see bhnd_nvram_io_setsize() */ +typedef int (bhnd_nvram_iop_setsize)(struct bhnd_nvram_io *io, size_t size); + +/** @see bhnd_nvram_io_free() */ +typedef void (bhnd_nvram_iop_free)(struct bhnd_nvram_io *io); + +/** + * NVRAM abstract I/O operations. + */ +struct bhnd_nvram_iops { + bhnd_nvram_iop_read *read; /**< read() implementation */ + bhnd_nvram_iop_read_ptr *read_ptr; /**< read_ptr() implementation */ + bhnd_nvram_iop_getsize *getsize; /**< getsize() implementation */ + bhnd_nvram_iop_setsize *setsize; /**< setsize() implementation */ + bhnd_nvram_iop_write *write; /**< write() implementation */ + bhnd_nvram_iop_write_ptr *write_ptr; /**< write_ptr() implementation */ + bhnd_nvram_iop_free *free; /**< free() implementation */ +}; + +/** + * NVRAM abstract I/O context. + */ +struct bhnd_nvram_io { + const struct bhnd_nvram_iops *iops; +}; + +/** + * Declare a bhnd_nvram_iops class with name @p _n. + */ +#define BHND_NVRAM_IOPS_DEFN(_n) \ + static bhnd_nvram_iop_read bhnd_nvram_ ## _n ## _read; \ + static bhnd_nvram_iop_read_ptr bhnd_nvram_ ## _n ## _read_ptr; \ + static bhnd_nvram_iop_write bhnd_nvram_ ## _n ## _write; \ + static bhnd_nvram_iop_write_ptr bhnd_nvram_ ## _n ## _write_ptr;\ + static bhnd_nvram_iop_getsize bhnd_nvram_ ## _n ## _getsize; \ + static bhnd_nvram_iop_setsize bhnd_nvram_ ## _n ## _setsize; \ + static bhnd_nvram_iop_free bhnd_nvram_ ## _n ## _free; \ + \ + static struct bhnd_nvram_iops bhnd_nvram_ ## _n ## _ops = { \ + .read = bhnd_nvram_ ## _n ## _read, \ + .read_ptr = bhnd_nvram_ ## _n ## _read_ptr, \ + .write = bhnd_nvram_ ## _n ## _write, \ + .write_ptr = bhnd_nvram_ ## _n ## _write_ptr, \ + .getsize = bhnd_nvram_ ## _n ## _getsize, \ + .setsize = bhnd_nvram_ ## _n ## _setsize, \ + .free = bhnd_nvram_ ## _n ## _free \ + }; + +#endif /* _BHND_NVRAM_BHND_NVRAM_IOVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_parser.c b/sys/dev/bhnd/nvram/bhnd_nvram_parser.c deleted file mode 100644 index a23efae074f..00000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram_parser.c +++ /dev/null @@ -1,1578 +0,0 @@ -/*- - * 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 -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "bhnd_nvram_parserreg.h" -#include "bhnd_nvram_parservar.h" - -/* - * BHND NVRAM Parser - * - * Provides identification, decoding, and encoding of BHND NVRAM data. - */ - -static const struct bhnd_nvram_ops *bhnd_nvram_find_ops(bhnd_nvram_format fmt); - -static int bhnd_nvram_find_var(struct bhnd_nvram *sc, const char *name, - const char **value, size_t *value_len); - -static int bhnd_nvram_keycmp(const char *lhs, size_t lhs_len, - const char *rhs, size_t rhs_len); -static int bhnd_nvram_sort_idx(void *ctx, const void *lhs, - const void *rhs); -static int bhnd_nvram_generate_index(struct bhnd_nvram *sc); - -static int bhnd_nvram_index_lookup(struct bhnd_nvram *sc, - struct bhnd_nvram_idx *idx, const char *name, - const char **env, size_t *len, const char **value, - size_t *value_len); -static int bhnd_nvram_buffer_lookup(struct bhnd_nvram *sc, - const char *name, const char **env, size_t *env_len, - const char **value, size_t *value_len); - -static bool bhnd_nvram_bufptr_valid(struct bhnd_nvram *sc, const void *ptr, - size_t nbytes, bool log_error); - -static int bhnd_nvram_parse_env(struct bhnd_nvram *sc, const char *env, - size_t len, const char **key, size_t *key_len, - const char **val, size_t *val_len); - -/** - * Calculate the size of the NVRAM data in @p data. - * - * @param data Pointer to NVRAM data to be parsed. - * @param[in,out] size On input, the total size of @p data. On - * successful parsing of @p data, will be set to - * the parsed size (which may be larger). - */ -typedef int (*bhnd_nvram_op_getsize)(const void *data, size_t *size); - -/** Perform format-specific initialization. */ -typedef int (*bhnd_nvram_op_init)(struct bhnd_nvram *sc); - -/** Initialize any format-specific default values. */ -typedef int (*bhnd_nvram_op_init_defaults)(struct bhnd_nvram *sc); -typedef int (*bhnd_nvram_op_enum_buf)(struct bhnd_nvram *sc, - const char **env, size_t *len, const uint8_t *p, - uint8_t const **next); - -/* FMT_BCM ops */ -static int bhnd_nvram_bcm_getsize(const void *data, size_t *size); -static int bhnd_nvram_bcm_init(struct bhnd_nvram *sc); -static int bhnd_nvram_bcm_init_defaults(struct bhnd_nvram *sc); -static int bhnd_nvram_bcm_enum_buf(struct bhnd_nvram *sc, const char **env, - size_t *len, const uint8_t *p, uint8_t const **next); - -/* FMT_TLV ops */ -static int bhnd_nvram_tlv_getsize(const void *data, size_t *size); -static int bhnd_nvram_tlv_init(struct bhnd_nvram *sc); -static int bhnd_nvram_tlv_enum_buf(struct bhnd_nvram *sc, const char **env, - size_t *len, const uint8_t *p, uint8_t const **next); -/* FMT_TXT ops */ -static int bhnd_nvram_txt_getsize(const void *data, size_t *size); -static int bhnd_nvram_txt_init(struct bhnd_nvram *sc); -static int bhnd_nvram_txt_enum_buf(struct bhnd_nvram *sc, const char **env, - size_t *len, const uint8_t *p, uint8_t const **next); - -/** - * Format-specific operations. - */ -struct bhnd_nvram_ops { - bhnd_nvram_format fmt; /**< nvram format */ - bhnd_nvram_op_getsize getsize; /**< determine actual NVRAM size */ - bhnd_nvram_op_init init; /**< format-specific initialization */ - bhnd_nvram_op_enum_buf enum_buf; /**< enumerate backing buffer */ - bhnd_nvram_op_init_defaults init_defaults; /**< populate any default values */ -}; - -static const struct bhnd_nvram_ops bhnd_nvram_ops_table[] = { - { - BHND_NVRAM_FMT_BCM, - bhnd_nvram_bcm_getsize, - bhnd_nvram_bcm_init, - bhnd_nvram_bcm_enum_buf, - bhnd_nvram_bcm_init_defaults - }, - { - BHND_NVRAM_FMT_TLV, - bhnd_nvram_tlv_getsize, - bhnd_nvram_tlv_init, - bhnd_nvram_tlv_enum_buf, - NULL - }, - { - BHND_NVRAM_FMT_BTXT, - bhnd_nvram_txt_getsize, - bhnd_nvram_txt_init, - bhnd_nvram_txt_enum_buf, - NULL - }, -}; - -#define NVRAM_LOG(sc, fmt, ...) do { \ - if (sc->dev != NULL) \ - device_printf(sc->dev, fmt, ##__VA_ARGS__); \ - else \ - printf("bhnd_nvram: " fmt, ##__VA_ARGS__); \ -} while (0) - -/* Limit a size_t value to a suitable range for use as a printf string field - * width */ -#define NVRAM_PRINT_WIDTH(_len) \ - ((_len) > NVRAM_VAL_MAX ? NVRAM_VAL_MAX : (int)(_len)) - -/* Is _c a field terminating/delimiting character? */ -#define nvram_is_ftermc(_c) ((_c) == '\0' || nvram_is_fdelim(_c)) - -/* Is _c a field delimiting character? */ -#define nvram_is_fdelim(_c) ((_c) == ',') - -/** - * Identify @p ident. - * - * @param ident Initial header data to be used for identification. - * @param expected Expected format against which @p ident will be tested. - * - * @retval 0 If @p ident has the @p expected format. - * @retval ENODEV If @p ident does not match @p expected. - */ -int -bhnd_nvram_parser_identify(const union bhnd_nvram_ident *ident, - bhnd_nvram_format expected) -{ - uint32_t bcm_magic = le32toh(ident->bcm.magic); - - switch (expected) { - case BHND_NVRAM_FMT_BCM: - if (bcm_magic == NVRAM_MAGIC) - return (0); - - return (ENODEV); - case BHND_NVRAM_FMT_TLV: - if (bcm_magic == NVRAM_MAGIC) - return (ENODEV); - - if (ident->tlv.tag != NVRAM_TLV_TYPE_ENV) - return (ENODEV); - - return (0); - case BHND_NVRAM_FMT_BTXT: - for (size_t i = 0; i < nitems(ident->btxt); i++) { - char c = ident->btxt[i]; - if (!isprint(c) && !isspace(c)) - return (ENODEV); - } - return (0); - break; - default: - printf("%s: unknown format: %d\n", __FUNCTION__, expected); - return (ENODEV); - } -} - -/** Return the set of operations for @p fmt, if any */ -static const struct bhnd_nvram_ops * -bhnd_nvram_find_ops(bhnd_nvram_format fmt) -{ - const struct bhnd_nvram_ops *ops; - - /* Fetch format-specific operation callbacks */ - for (size_t i = 0; i < nitems(bhnd_nvram_ops_table); i++) { - ops = &bhnd_nvram_ops_table[i]; - - if (ops->fmt != fmt) - continue; - - /* found */ - return (ops); - } - - return (NULL); -} - -/** - * Identify the NVRAM format at @p offset within @p r, verify the - * CRC (if applicable), and allocate a local shadow copy of the NVRAM data. - * - * After initialization, no reference to @p input will be held by the - * NVRAM parser, and @p input may be safely deallocated. - * - * @param[out] sc The NVRAM parser state to be initialized. - * @param dev The parser's parent device, or NULL if none. - * @param data NVRAM data to be parsed. - * @param size Size of @p data. - * @param fmt Required format of @p input. - * - * @retval 0 success - * @retval ENOMEM If internal allocation of NVRAM state fails. - * @retval EINVAL If @p input parsing fails. - */ -int -bhnd_nvram_parser_init(struct bhnd_nvram *sc, device_t dev, const void *data, - size_t size, bhnd_nvram_format fmt) -{ - int error; - - /* Initialize NVRAM state */ - memset(sc, 0, sizeof(*sc)); - - sc->dev = dev; - LIST_INIT(&sc->devpaths); - - /* Verify data format and init operation callbacks */ - if (size < sizeof(union bhnd_nvram_ident)) - return (EINVAL); - - error = bhnd_nvram_parser_identify( - (const union bhnd_nvram_ident *)data, fmt); - if (error) - return (error); - - if ((sc->ops = bhnd_nvram_find_ops(fmt)) == NULL) { - NVRAM_LOG(sc, "unsupported format: %d\n", fmt); - return (error); - } - - /* Determine appropriate size for backing buffer */ - sc->buf_size = size; - if ((error = sc->ops->getsize(data, &sc->buf_size))) - return (error); - - if (sc->buf_size > size) { - NVRAM_LOG(sc, "cannot parse %zu NVRAM bytes, would overrun " - "%zu byte input buffer\n", sc->buf_size, size); - return (EINVAL); - } - - /* Allocate and populate backing buffer */ - sc->buf = malloc(sc->buf_size, M_BHND_NVRAM, M_NOWAIT); - if (sc->buf == NULL) - return (ENOMEM); - memcpy(sc->buf, data, sc->buf_size); - - /* Allocate default/pending variable hash tables */ - error = bhnd_nvram_varmap_init(&sc->defaults, NVRAM_SMALL_HASH_SIZE, - M_NOWAIT); - if (error) - goto cleanup; - - error = bhnd_nvram_varmap_init(&sc->pending, NVRAM_SMALL_HASH_SIZE, - M_NOWAIT); - if (error) - goto cleanup; - - /* Perform format-specific initialization */ - if ((error = sc->ops->init(sc))) - goto cleanup; - - /* Generate all indices */ - if ((error = bhnd_nvram_generate_index(sc))) - goto cleanup; - - /* Add any format-specific default values */ - if (sc->ops->init_defaults != NULL) { - if ((error = sc->ops->init_defaults(sc))) - goto cleanup; - } - - return (0); - -cleanup: - bhnd_nvram_parser_fini(sc); - return (error); -} - - -/** - * Release all resources held by @p sc. - * - * @param sc A NVRAM instance previously initialized via - * bhnd_nvram_parser_init(). - */ -void -bhnd_nvram_parser_fini(struct bhnd_nvram *sc) -{ - struct bhnd_nvram_devpath *dpath, *dnext; - - LIST_FOREACH_SAFE(dpath, &sc->devpaths, dp_link, dnext) { - free(dpath->path, M_BHND_NVRAM); - free(dpath, M_BHND_NVRAM); - } - - if (sc->defaults.table != NULL) - bhnd_nvram_varmap_free(&sc->defaults); - - if (sc->pending.table != NULL) - bhnd_nvram_varmap_free(&sc->pending); - - if (sc->idx != NULL) - free(sc->idx, M_BHND_NVRAM); - - if (sc->buf != NULL) - free(sc->buf, M_BHND_NVRAM); - -} - -/** - * Identify the integer format of @p field. - * - * @param field Field to be identified. - * @param field_len Length of @p field. - * @param[out] base Integer base, or 0 if integer format unrecognized. - * @param[out] negated True if integer is prefixed with negation sign. - * - * @retval true if parsed successfully - * @retval false if the format of @p field cannot be determined. - */ -static bool -bhnd_nvram_identify_intfmt(const char *field, size_t field_len, int *base, - bool *negated) -{ - const char *p; - - /* Hex? */ - p = field; - if (field_len > 2 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { - bool valid; - - /* Check all field characters */ - valid = true; - for (p = field + 2; p - field < field_len; p++) { - if (isxdigit(*p)) - continue; - - valid = false; - break; - } - - if (valid) { - *base = 16; - *negated = false; - return (true); - } - } - - /* Decimal? */ - p = field; - if (field_len >= 1 && (*p == '-' || isdigit(*p))) { - bool valid; - - valid = true; - *negated = false; - for (p = field; p - field < field_len; p++) { - if (p == field && *p == '-') { - *negated = true; - continue; - } - - if (isdigit(*p)) - continue; - - valid = false; - break; - } - - if (valid) { - *base = 10; - return (true); - } - } - - /* No match */ - *base = 0; - *negated = false; - return (false); -} - -/** - * Search for a field delimiter or '\0' in @p value, returning the - * size of the first field (not including its terminating character). - * - * If no terminating character is found, @p value_len is returned. - * - * @param value The value to be searched. - * @param value_size The size of @p value. - */ -static size_t -bhnd_nvram_parse_field_len(const char *value, size_t value_size) -{ - for (const char *p = value; p - value < value_size; p++) { - if (nvram_is_ftermc(*p)) - return (p - value); - } - - return (value_size); -} - -/* Parse a string NVRAM variable, writing the NUL-terminated result - * to buf (if non-NULL). */ -static int -bhnd_nvram_parse_strvar(const char *value, size_t value_len, char *buf, - size_t *size) -{ - size_t str_len; - size_t req_size; - size_t max_size; - - if (buf != NULL) - max_size = *size; - else - max_size = 0; - - - /* Determine input and output sizes, including whether additional space - * is required for a trailing NUL */ - str_len = strnlen(value, value_len); - if (str_len == value_len) - req_size = str_len + 1; - else - req_size = value_len; - - /* Provide actual size to caller */ - *size = req_size; - if (max_size < req_size) { - if (buf != NULL) - return (ENOMEM); - else - return (0); - } - - /* Copy and NUL terminate output */ - memcpy(buf, value, str_len); - buf[str_len] = '\0'; - return (0); -} - -/** - * Read an NVRAM variable. - * - * @param sc The NVRAM parser state. - * @param name The NVRAM variable name. - * @param[out] buf On success, the requested value will be written - * to this buffer. This argment may be NULL if - * the value is not desired. - * @param[in,out] len The capacity of @p buf. On success, will be set - * to the actual size of the requested value. - * @param type The requested data type to be written to @p buf. - * - * @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_parser_getvar(struct bhnd_nvram *sc, const char *name, void *buf, - size_t *len, bhnd_nvram_type type) -{ - char *cstr, cstr_buf[NVRAM_VAL_MAX+1]; - const char *val; - size_t cstr_size; - size_t limit, nbytes; - size_t field_len, val_len; - int error; - - /* Verify name validity */ - if (!bhnd_nvram_validate_name(name, strlen(name))) - return (EINVAL); - - /* Fetch variable's string value */ - if ((error = bhnd_nvram_find_var(sc, name, &val, &val_len))) - return (error); - - nbytes = 0; - if (buf != NULL) - limit = *len; - else - limit = 0; - - /* Populate C string requests directly from the fetched value */ - if (type == BHND_NVRAM_TYPE_CSTR) - return (bhnd_nvram_parse_strvar(val, val_len, buf, len)); - - /* Determine actual string length. */ - val_len = strnlen(val, val_len); - - /* Try parsing as an octet string value (e.g. a MAC address) */ - if (bhnd_nvram_parse_octet_string(val, val_len, buf, len, type) == 0) - return (0); - - /* Otherwise, we need a NUL-terminated copy of the string value - * for parsing */ - cstr_size = val_len + 1; - if (cstr_size <= sizeof(cstr)) { - /* prefer stack allocated buffer */ - cstr = cstr_buf; - } else { - cstr = malloc(cstr_size, M_BHND_NVRAM, M_NOWAIT); - if (cstr == NULL) - return (EFBIG); - } - - /* Copy and NUL terminate */ - strncpy(cstr, val, val_len); - cstr[val_len] = '\0'; - - /* Parse */ - for (char *p = cstr; *p != '\0';) { - char *endp; - int base; - bool is_int, is_negated; - union { - unsigned long u32; - long s32; - } intv; - - /* Determine the field length */ - field_len = val_len - (p - cstr); - field_len = bhnd_nvram_parse_field_len(p, field_len); - - /* Skip any leading whitespace */ - while (field_len > 0 && isspace(*p)) { - p++; - field_len--; - } - - /* Empty field values cannot be parsed as a fixed - * data type */ - if (field_len == 0) { - NVRAM_LOG(sc, "error: cannot parse empty string in " - "'%s'\n", cstr); - return (EFTYPE); - } - - /* Attempt to identify the integer format */ - is_int = bhnd_nvram_identify_intfmt(p, field_len, &base, - &is_negated); - - /* Extract the field data */ -#define NV_READ_INT(_ctype, _max, _min, _dest, _strto) do { \ - if (!is_int) { \ - error = EFTYPE; \ - goto finished; \ - } \ - \ - if (is_negated && _min == 0) { \ - error = ERANGE; \ - goto finished; \ - } \ - \ - _dest = _strto(p, &endp, base); \ - if (endp == p || !nvram_is_ftermc(*endp)) { \ - error = ERANGE; \ - goto finished; \ - } \ - \ - if (_dest > _max || _dest < _min) { \ - error = ERANGE; \ - goto finished; \ - } \ - \ - if (limit > nbytes && limit - nbytes >= sizeof(_ctype)) \ - *((_ctype *)((uint8_t *)buf + nbytes)) = _dest; \ - \ - nbytes += sizeof(_ctype); \ -} while(0) - - switch (type) { - case BHND_NVRAM_TYPE_CHAR: - /* Copy out the characters directly */ - for (size_t i = 0; i < field_len; i++) { - if (limit > nbytes) - *((char *)buf + nbytes) = p[i]; - nbytes++; - } - break; - - case BHND_NVRAM_TYPE_UINT8: - NV_READ_INT(uint8_t, UINT8_MAX, 0, intv.u32, strtoul); - break; - - case BHND_NVRAM_TYPE_UINT16: - NV_READ_INT(uint16_t, UINT16_MAX, 0, intv.u32, strtoul); - break; - - case BHND_NVRAM_TYPE_UINT32: - NV_READ_INT(uint32_t, UINT32_MAX, 0, intv.u32, strtoul); - break; - - case BHND_NVRAM_TYPE_INT8: - NV_READ_INT(int8_t, INT8_MAX, INT8_MIN, intv.s32, - strtol); - break; - - case BHND_NVRAM_TYPE_INT16: - NV_READ_INT(int16_t, INT16_MAX, INT16_MIN, intv.s32, - strtol); - break; - - case BHND_NVRAM_TYPE_INT32: - NV_READ_INT(int32_t, INT32_MAX, INT32_MIN, intv.s32, - strtol); - break; - - case BHND_NVRAM_TYPE_CSTR: /* Must be handled above */ - /* fallthrough */ - default: - NVRAM_LOG(sc, "unhandled NVRAM type: %d\n", type); - error = ENXIO; - goto finished; - } - - /* Advance to next field, skip any trailing delimiter */ - p += field_len; - if (nvram_is_fdelim(*p)) - p++; - } - - error = 0; - -finished: - if (cstr != cstr_buf) - free(cstr, M_BHND_NVRAM); - - 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_parser_setvar(struct bhnd_nvram *sc, const char *name, - const void *buf, size_t len, bhnd_nvram_type type) -{ - /* 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_type_width(type) != 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_INT8: - case BHND_NVRAM_TYPE_INT16: - case BHND_NVRAM_TYPE_INT32: - // TODO: primitive type value support - return (EOPNOTSUPP); - - case BHND_NVRAM_TYPE_CHAR: - case BHND_NVRAM_TYPE_CSTR: - return (bhnd_nvram_varmap_add(&sc->pending, name, buf, len)); - } - - return (0); -} - -/** - * Return true if @p ptr + nbytes falls within our backing buffer, false - * otherwise. - */ -static bool -bhnd_nvram_bufptr_valid(struct bhnd_nvram *sc, const void *ptr, size_t nbytes, - bool log_error) -{ - const uint8_t *p = ptr; - - if (p < sc->buf) - goto failed; - - if (nbytes > sc->buf_size) - goto failed; - - if (p - sc->buf > sc->buf_size - nbytes) - goto failed; - - return (true); - -failed: - if (log_error) - NVRAM_LOG(sc, "NVRAM record not readable at %p+%#zx (base=%p, " - "len=%zu)\n", p, nbytes, sc->buf, sc->buf_size); - return (false); -} - -/** - * Parse a 'key=value' env string. - */ -static int -bhnd_nvram_parse_env(struct bhnd_nvram *sc, const char *env, size_t len, - const char **key, size_t *key_len, const char **val, size_t *val_len) -{ - const char *p; - - /* Key */ - if ((p = memchr(env, '=', len)) == NULL) { - NVRAM_LOG(sc, "missing delim in '%.*s'\n", - NVRAM_PRINT_WIDTH(len), env); - return (EINVAL); - } - - *key = env; - *key_len = p - env; - - /* Skip '=' */ - p++; - - /* Vaue */ - *val = p; - *val_len = len - (p - env); - - return (0); -} - -/** - * Fetch a string pointer to @p name's value, if any. - * - * @param sc The NVRAM parser state. - * @param name The NVRAM variable name. - * @param[out] value On success, a pointer to the variable's value - * string. The string may not be NUL terminated. - * @param[out] value_len On success, the length of @p value, not - * including a terminating NUL (if any exists). - * - * @retval 0 success - * @retval ENOENT The requested variable was not found. - * @retval non-zero If reading @p name otherwise fails, a regular unix - * error code will be returned. - */ -static int -bhnd_nvram_find_var(struct bhnd_nvram *sc, const char *name, const char **value, - size_t *value_len) -{ - struct bhnd_nvram_tuple *t; - bhnd_nvram_op_enum_buf enum_fn; - const char *env; - size_t env_len; - size_t name_len; - int error; - - enum_fn = sc->ops->enum_buf; - name_len = strlen(name); - - /* - * Search path: - * - * - uncommitted changes - * - index lookup OR buffer scan - * - registered defaults - */ - - /* Search uncommitted changes */ - t = bhnd_nvram_varmap_find(&sc->pending, name, name_len); - if (t != NULL) { - if (t->value != NULL) { - /* Uncommited value exists, is not a deletion */ - *value = t->value; - *value_len = t->value_len; - return (0); - } else { - /* Value is marked for deletion. */ - error = ENOENT; - goto failed; - } - } - - /* Search backing buffer. We the index if available; otherwise, - * perform a buffer scan */ - if (sc->idx != NULL) { - error = bhnd_nvram_index_lookup(sc, sc->idx, name, &env, - &env_len, value, value_len); - } else { - error = bhnd_nvram_buffer_lookup(sc, name, &env, &env_len, - value, value_len); - } - -failed: - /* If a parse error occured, we don't want to hide the issue by - * returning a default NVRAM value. Otherwise, look for a matching - * default. */ - if (error != ENOENT) - return (error); - - t = bhnd_nvram_varmap_find(&sc->defaults, name, name_len); - if (t != NULL) { - *value = t->value; - *value_len = t->value_len; - return (0); - } - - /* Not found, and no default value available */ - return (ENOENT); -} - -/* - * An strcmp()-compatible lexical comparison implementation that - * handles non-NUL-terminated strings. - */ -static int -bhnd_nvram_keycmp(const char *lhs, size_t lhs_len, const char *rhs, - size_t rhs_len) -{ - int order; - - order = strncmp(lhs, rhs, ulmin(lhs_len, rhs_len)); - if (order == 0) { - if (lhs_len < rhs_len) - order = -1; - else if (lhs_len > rhs_len) - order = 1; - } - - return (order); -} - -/* sort function for bhnd_nvram_idx_entry values */ -static int -bhnd_nvram_sort_idx(void *ctx, const void *lhs, const void *rhs) -{ - struct bhnd_nvram *sc; - const struct bhnd_nvram_idx_entry *l_idx, *r_idx; - const char *l_str, *r_str; - - sc = ctx; - l_idx = lhs; - r_idx = rhs; - - /* Fetch string pointers */ - l_str = (char *)(sc->buf + l_idx->env_offset); - r_str = (char *)(sc->buf + r_idx->env_offset); - - /* Perform comparison */ - return (bhnd_nvram_keycmp(l_str, l_idx->key_len, r_str, - r_idx->key_len)); -} - - -/** - * 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 *sc) -{ - bhnd_nvram_op_enum_buf enum_fn; - const char *key, *val; - const char *env; - const uint8_t *p; - size_t env_len; - size_t idx_bytes; - size_t key_len, val_len; - size_t num_records; - int error; - - enum_fn = sc->ops->enum_buf; - num_records = 0; - - /* Parse and register all device path aliases */ - p = NULL; - while ((error = enum_fn(sc, &env, &env_len, p, &p)) == 0) { - struct bhnd_nvram_devpath *devpath; - char *eptr; - char suffix[NVRAM_KEY_MAX+1]; - size_t suffix_len; - u_long index; - - /* Hit EOF */ - if (env == NULL) - break; - - num_records++; - - /* Skip string comparison if env_len < strlen(devpath) */ - if (env_len < NVRAM_DEVPATH_LEN) - continue; - - /* Check for devpath prefix */ - if (strncmp(env, NVRAM_DEVPATH_STR, NVRAM_DEVPATH_LEN) != 0) - continue; - - /* Split key and value */ - error = bhnd_nvram_parse_env(sc, env, env_len, &key, - &key_len, &val, &val_len); - if (error) - return (error); - - /* NUL terminate the devpath's suffix */ - if (key_len >= sizeof(suffix)) { - NVRAM_LOG(sc, "variable '%.*s' exceeds NVRAM_KEY_MAX, " - "skipping devpath parsing\n", - NVRAM_PRINT_WIDTH(key_len), key); - continue; - } else { - suffix_len = key_len - NVRAM_DEVPATH_LEN; - if (suffix_len == 0) - continue; - - strcpy(suffix, key+NVRAM_DEVPATH_LEN); - suffix[suffix_len] = '\0'; - } - - /* Parse the index value */ - index = strtoul(suffix, &eptr, 10); - if (eptr == suffix || *eptr != '\0') { - NVRAM_LOG(sc, "invalid devpath variable '%.*s'\n", - NVRAM_PRINT_WIDTH(key_len), key); - continue; - } - - /* Register path alias */ - devpath = malloc(sizeof(*devpath), M_BHND_NVRAM, M_NOWAIT); - if (devpath == NULL) - return (ENOMEM); - - devpath->index = index; - devpath->path = strndup(val, val_len, M_BHND_NVRAM); - LIST_INSERT_HEAD(&sc->devpaths, devpath, dp_link); - } - - if (error) - return (error); - - /* Save record count */ - sc->num_buf_vars = num_records; - - /* Skip generating variable index if threshold is not met */ - if (sc->num_buf_vars < NVRAM_IDX_VAR_THRESH) - return (0); - - /* Allocate and populate variable index */ - idx_bytes = sizeof(struct bhnd_nvram_idx) + - (sizeof(struct bhnd_nvram_idx_entry) * sc->num_buf_vars); - sc->idx = malloc(idx_bytes, M_BHND_NVRAM, M_NOWAIT); - if (sc->idx == NULL) { - NVRAM_LOG(sc, "error allocating %zu byte index\n", idx_bytes); - goto bad_index; - } - - sc->idx->num_entries = sc->num_buf_vars; - - if (bootverbose) { - NVRAM_LOG(sc, "allocated %zu byte index for %zu variables " - "in %zu bytes\n", idx_bytes, sc->num_buf_vars, - sc->buf_size); - } - - p = NULL; - for (size_t i = 0; i < sc->idx->num_entries; i++) { - struct bhnd_nvram_idx_entry *idx; - size_t env_offset; - size_t key_len, val_len; - - /* Fetch next record */ - if ((error = enum_fn(sc, &env, &env_len, p, &p))) - return (error); - - /* Early EOF */ - if (env == NULL) { - NVRAM_LOG(sc, "indexing failed, expected %zu records " - "(got %zu)\n", sc->idx->num_entries, i+1); - goto bad_index; - } - - /* Calculate env offset */ - env_offset = (const uint8_t *)env - (const uint8_t *)sc->buf; - if (env_offset > NVRAM_IDX_OFFSET_MAX) { - NVRAM_LOG(sc, "'%.*s' offset %#zx exceeds maximum " - "indexable value\n", NVRAM_PRINT_WIDTH(env_len), - env, env_offset); - goto bad_index; - } - - /* Split key and value */ - error = bhnd_nvram_parse_env(sc, env, env_len, &key, &key_len, - &val, &val_len); - if (error) - return (error); - - if (key_len > NVRAM_IDX_LEN_MAX) { - NVRAM_LOG(sc, "key length %#zx at %#zx exceeds maximum " - "indexable value\n", key_len, env_offset); - goto bad_index; - } - - if (val_len > NVRAM_IDX_LEN_MAX) { - NVRAM_LOG(sc, "value length %#zx for key '%.*s' " - "exceeds maximum indexable value\n", val_len, - NVRAM_PRINT_WIDTH(key_len), key); - goto bad_index; - } - - idx = &sc->idx->entries[i]; - idx->env_offset = env_offset; - idx->key_len = key_len; - idx->val_len = val_len; - } - - /* Sort the index table */ - qsort_r(sc->idx->entries, sc->idx->num_entries, - sizeof(sc->idx->entries[0]), sc, bhnd_nvram_sort_idx); - - return (0); - -bad_index: - /* Fall back on non-indexed access */ - NVRAM_LOG(sc, "reverting to non-indexed variable lookup\n"); - if (sc->idx != NULL) { - free(sc->idx, M_BHND_NVRAM); - sc->idx = NULL; - } - - return (0); -} - - -/** - * Perform an index lookup of @p name. - * - * @param sc The NVRAM parser state. - * @param idx The index to search. - * @param name The variable to search for. - * @param[out] env On success, the pointer to @p name within the - * backing buffer. - * @param[out] env_len On success, the length of @p env. - * @param[out] value On success, the pointer to @p name's value - * within the backing buffer. - * @param[out] value_len On success, the length of @p value. - * - * @retval 0 If @p name was found in the index. - * @retval ENOENT If @p name was not found in the index. - * @retval ENODEV If no index has been generated. - */ -static int -bhnd_nvram_index_lookup(struct bhnd_nvram *sc, struct bhnd_nvram_idx *idx, - const char *name, const char **env, size_t *env_len, const char **value, - size_t *value_len) -{ - struct bhnd_nvram_idx_entry *idxe; - const char *idxe_key; - size_t min, mid, max; - size_t name_len; - int order; - - if (idx->num_entries == 0) - return (ENOENT); - - /* - * Locate the requested variable using a binary search. - */ - min = 0; - mid = 0; - max = idx->num_entries - 1; - name_len = strlen(name); - - while (max >= min) { - /* Select midpoint */ - mid = (min + max) / 2; - idxe = &idx->entries[mid]; - - /* Determine which side of the partition to search */ - idxe_key = (const char *) (sc->buf + idxe->env_offset); - order = bhnd_nvram_keycmp(idxe_key, idxe->key_len, name, - name_len); - - if (order < 0) { - /* Search upper partition */ - min = mid + 1; - } else if (order > 0) { - /* Search lower partition */ - max = mid - 1; - } else if (order == 0) { - /* Match found */ - *env = sc->buf + idxe->env_offset; - *env_len = idxe->key_len + idxe->val_len + 1 /* '=' */; - - *value = *env + idxe->key_len + 1 /* '=' */; - *value_len = idxe->val_len; - - return (0); - } - } - - /* Not found */ - return (ENOENT); -} - - -/** - * Perform a unindexed search for an entry matching @p name in the backing - * NVRAM data buffer. - * - * @param sc The NVRAM parser state. - * @param name The variable to search for. - * @param[out] env On success, the pointer to @p name within the - * backing buffer. - * @param[out] env_len On success, the length of @p env. - * @param[out] value On success, the pointer to @p name's value - * within the backing buffer. - * @param[out] value_len On success, the length of @p value. - * - * @retval 0 If @p name was found in the index. - * @retval ENOENT If @p name was not found in the index. - * @retval ENODEV If no index has been generated. - */ -static int -bhnd_nvram_buffer_lookup(struct bhnd_nvram *sc, const char *name, - const char **env, size_t *env_len, const char **value, size_t *value_len) -{ - bhnd_nvram_op_enum_buf enum_fn; - const uint8_t *p; - size_t name_len; - int error; - - enum_fn = sc->ops->enum_buf; - name_len = strlen(name); - - /* Iterate over all records in the backing buffer */ - p = NULL; - while ((error = enum_fn(sc, env, env_len, p, &p)) == 0) { - /* Hit EOF, not found */ - if (*env == NULL) - return (ENOENT); - - /* Skip string comparison if env_len < strlen('key=') */ - if (*env_len < name_len + 1) - continue; - - /* Skip string comparison if delimiter isn't found at - * expected position */ - if (*(*env + name_len) != '=') - continue; - - /* Check for match */ - if (strncmp(*env, name, name_len) == 0) { - /* Found */ - *value = *env + name_len + 1; - *value_len = *env_len - name_len - 1; - return (0); - }; - } - - return (error); -} - -/* FMT_BCM NVRAM data size calculation */ -static int -bhnd_nvram_bcm_getsize(const void *data, size_t *size) -{ - const struct bhnd_nvram_header *hdr; - - if (*size < sizeof(*hdr)) - return (EINVAL); - - hdr = (const struct bhnd_nvram_header *) data; - *size = le32toh(hdr->size); - return (0); -} - -/* FMT_BCM-specific parser initialization */ -static int -bhnd_nvram_bcm_init(struct bhnd_nvram *sc) -{ - const uint8_t *p; - uint32_t cfg0; - uint8_t crc, valid; - - /* Validate CRC */ - if (sc->buf_size < NVRAM_CRC_SKIP) - return (EINVAL); - - if (sc->buf_size < sizeof(struct bhnd_nvram_header)) - return (EINVAL); - - cfg0 = ((struct bhnd_nvram_header *)sc->buf)->cfg0; - valid = (cfg0 & NVRAM_CFG0_CRC_MASK) >> NVRAM_CFG0_CRC_SHIFT; - - p = sc->buf; - crc = bhnd_nvram_crc8(p + NVRAM_CRC_SKIP, sc->buf_size-NVRAM_CRC_SKIP, - BHND_NVRAM_CRC8_INITIAL); - - if (crc != valid) { - NVRAM_LOG(sc, "warning: NVRAM CRC error (crc=%#hhx, " - "expected=%hhx)\n", crc, valid); - } - - return (0); -} - -/* Populate FMT_BCM-specific default values */ -static int -bhnd_nvram_bcm_init_defaults(struct bhnd_nvram *sc) -{ - struct bhnd_nvram_header *header; - char vbuf[NVRAM_VAL_MAX]; - uint32_t value; - int error; - int nwrite; - - /* Verify that our header is readable */ - header = (struct bhnd_nvram_header *) sc->buf; - if (!bhnd_nvram_bufptr_valid(sc, header, sizeof(*header), true)) - return (EINVAL); - - /* Extract a value value from the NVRAM header, format it, and - * register a new default variable tuple */ -#define NVRAM_BCM_HEADER_DEFAULT(_field, _name) do { \ - value = NVRAM_GET_BITS(le32toh(header->_field), _name); \ - nwrite = snprintf(vbuf, sizeof(vbuf), _name ##_FMT, value); \ - if (nwrite < 0 || nwrite >= sizeof(vbuf)) { \ - NVRAM_LOG(sc, "%s: formatting '%s' failed: %d\n", \ - __FUNCTION__, _name ## _VAR, nwrite); \ - return (ENXIO); \ - } \ - error = bhnd_nvram_varmap_add(&sc->defaults, \ - _name ##_VAR, vbuf, strlen(vbuf)); \ - \ - if (error) \ - return (error); \ -} while(0) - - NVRAM_BCM_HEADER_DEFAULT(cfg0, NVRAM_CFG0_SDRAM_INIT); - NVRAM_BCM_HEADER_DEFAULT(cfg1, NVRAM_CFG1_SDRAM_CFG); - NVRAM_BCM_HEADER_DEFAULT(cfg1, NVRAM_CFG1_SDRAM_REFRESH); - NVRAM_BCM_HEADER_DEFAULT(sdram_ncdl, NVRAM_SDRAM_NCDL); - -#undef NVRAM_BCM_HEADER_DEFAULT - - return (0); -} - - -/* FMT_BCM record parsing */ -static int -bhnd_nvram_bcm_enum_buf(struct bhnd_nvram *sc, const char **env, size_t *len, - const uint8_t *p, uint8_t const **next) -{ - /* First record is found following the NVRAM header */ - if (p == NULL) - p = sc->buf + sizeof(struct bhnd_nvram_header); - - if (!bhnd_nvram_bufptr_valid(sc, p, 1, true)) - return (EINVAL); - - /* EOF */ - if (*p == '\0') { - *env = NULL; - *len = 0; - *next = p; - return (0); - } - - /* Provide pointer to env data */ - *env = p; - *len = strnlen(p, sc->buf_size - (p - sc->buf)); - - /* Advance to next entry and skip terminating NUL */ - p += *len; - if (bhnd_nvram_bufptr_valid(sc, p, 1, false)) { - p++; - } else { - NVRAM_LOG(sc, "warning: missing NVRAM termination record"); - } - - *next = p; - return (0); -} - -/* FMT_TLV NVRAM data size calculation */ -static int -bhnd_nvram_tlv_getsize(const void *data, size_t *size) -{ - const uint8_t *const start = data; - size_t offset; - uint16_t rlen; - - offset = 0; - while (offset < *size) { - uint8_t type; - - /* Fetch type */ - type = *(start+offset); - - /* EOF */ - if (type == NVRAM_TLV_TYPE_END) { - *size = offset + 1; - return (0); - } - - if ((offset++) == *size) - return (EINVAL); - - /* Determine record length */ - if (type & NVRAM_TLV_TF_U8_LEN) { - rlen = *(start+offset); - } else { - rlen = *(start+offset) << 8; - if ((offset++) == *size) - return (EINVAL); - rlen |= *(start+offset); - } - - if ((offset++) >= *size) - return (EINVAL); - - /* Advance to next entry */ - if (rlen > *size || *size - rlen < offset) - return (EINVAL); - - offset += rlen; - } - - /* EOF not found */ - return (EINVAL); -} - -/* FMT_TLV-specific parser initialization */ -static int -bhnd_nvram_tlv_init(struct bhnd_nvram *sc) -{ - return (0); -} - -/* FMT_TLV record parsing */ -static int -bhnd_nvram_tlv_enum_buf(struct bhnd_nvram *sc, const char **env, size_t *len, - const uint8_t *p, uint8_t const **next) -{ - size_t rlen; - uint8_t type; - - if (p == NULL) - p = sc->buf; - - /* Fetch type */ - if (!bhnd_nvram_bufptr_valid(sc, p, 1, true)) - return (EINVAL); - - type = *p; - - /* EOF */ - if (type == NVRAM_TLV_TYPE_END) { - *env = NULL; - *len = 0; - *next = p; - return (0); - } - - /* Determine record length */ - p++; - if (type & NVRAM_TLV_TF_U8_LEN) { - if (!bhnd_nvram_bufptr_valid(sc, p, 1, true)) - return (EINVAL); - - rlen = *p; - p += 1; - } else { - if (!bhnd_nvram_bufptr_valid(sc, p, 2, true)) - return (EINVAL); - rlen = (p[0] << 8) | (p[1]); - p += 2; - } - - /* Verify record readability */ - if (!bhnd_nvram_bufptr_valid(sc, p, rlen, true)) - return (EINVAL); - - /* Error on non-env records */ - if (type != NVRAM_TLV_TYPE_ENV) { - NVRAM_LOG(sc, "unsupported NVRAM TLV tag: %#hhx\n", type); - return (EINVAL); - } - - /* Skip flag field */ - if (rlen < 1) - return (EINVAL); - p++; - rlen--; - - /* Provide pointer to env data */ - *env = p; - *len = strnlen(*env, rlen); - - /* Advance to next entry */ - *next = p + rlen; - - return (0); -} - -/* FMT_BTXT NVRAM data size calculation */ -static int -bhnd_nvram_txt_getsize(const void *data, size_t *size) -{ - *size = (strnlen(data, *size)); - return (0); -} - -/* FMT_BTXT-specific parser initialization */ -static int -bhnd_nvram_txt_init(struct bhnd_nvram *sc) -{ - return (0); -} - -/* Seek past the next line ending (\r, \r\n, or \n) */ -static const uint8_t * -bhnd_nvram_txt_seek_eol(struct bhnd_nvram *sc, const uint8_t *p) -{ - while (p < sc->buf + sc->buf_size) { - switch (*p) { - case '\r': - /* \r\n */ - if (bhnd_nvram_bufptr_valid(sc, p, 1, false)) { - if (*(p+1) == '\n') - p++; - } - - return (p+1); - case '\n': - return (p+1); - default: - p++; - break; - } - } - - return (p); -} - -/* Seek to the next valid key=value entry (or EOF) */ -static const uint8_t * -bhnd_nvram_txt_seek_nextline(struct bhnd_nvram *sc, const uint8_t *p) -{ - /* Skip leading whitespace and comments */ - while (p < sc->buf + sc->buf_size) { - if (isspace(*p)) { - p++; - continue; - } - - if (*p == '#') { - p = bhnd_nvram_txt_seek_eol(sc, p); - continue; - } - - break; - } - - return (p); -} - -/* FMT_BTXT record parsing */ -static int -bhnd_nvram_txt_enum_buf(struct bhnd_nvram *sc, const char **env, size_t *len, - const uint8_t *p, uint8_t const **next) -{ - const uint8_t *startp; - size_t line_len; - - if (p == NULL) - p = sc->buf; - - /* Skip any leading whitespace and comments */ - p = bhnd_nvram_txt_seek_nextline(sc, p); - - /* EOF? */ - if (!bhnd_nvram_bufptr_valid(sc, p, 1, false)) { - *env = NULL; - *len = 0; - *next = p; - return (0); - } - - /* Find record termination (EOL, or '#') */ - startp = p; - while (p < sc->buf + sc->buf_size) { - if (*p == '#' || *p == '\n' || *p == '\r') - break; - - p++; - } - - /* Calculate line length, check for EOF */ - line_len = p - startp; - if (!bhnd_nvram_bufptr_valid(sc, p, 1, false)) { - *env = NULL; - *len = 0; - *next = p; - return (0); - } - - /* Got env data; trim any tailing whitespace */ - *env = startp; - *len = line_len; - - for (size_t i = 0; i < line_len && line_len > 0; i++) { - char c = startp[line_len - i - 1]; - if (!isspace(c)) - break; - - *len -= 1; - } - - /* Advance to next entry */ - p = bhnd_nvram_txt_seek_nextline(sc, p); - - *next = p; - return (0); -} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_parser.h b/sys/dev/bhnd/nvram/bhnd_nvram_parser.h deleted file mode 100644 index 27abd556a31..00000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram_parser.h +++ /dev/null @@ -1,101 +0,0 @@ -/*- - * 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_PARSER_H_ -#define _BHND_NVRAM_BHND_NVRAM_PARSER_H_ - -#include -#include - -#include "bhnd_nvram_common.h" - -union bhnd_nvram_ident; - -struct bhnd_nvram_idx; -struct bhnd_nvram_ops; -struct bhnd_nvram_devpath; - -struct bhnd_nvram; - -LIST_HEAD(bhnd_nvram_devpaths, bhnd_nvram_devpath); - -int bhnd_nvram_parser_identify(const union bhnd_nvram_ident *ident, - bhnd_nvram_format expected); -int bhnd_nvram_parser_init(struct bhnd_nvram *sc, device_t owner, - const void *data, size_t len, bhnd_nvram_format fmt); -void bhnd_nvram_parser_fini(struct bhnd_nvram *sc); - -int bhnd_nvram_parser_getvar(struct bhnd_nvram *sc, const char *name, - void *buf, size_t *len, bhnd_nvram_type type); -int bhnd_nvram_parser_setvar(struct bhnd_nvram *sc, const char *name, - const void *buf, size_t len, bhnd_nvram_type type); - -/** BCM NVRAM header */ -struct bhnd_nvram_header { - uint32_t magic; - uint32_t size; - uint32_t cfg0; /**< crc:8, version:8, sdram_init:16 */ - uint32_t cfg1; /**< sdram_config:16, sdram_refresh:16 */ - uint32_t sdram_ncdl; /**< sdram_ncdl */ -} __packed; - -/** - * NVRAM format identification. - * - * To perform identification of the NVRAM format using bhnd_nvram_identify(), - * read `sizeof(bhnd_nvram_indent)` bytes from the head of the NVRAM data. - */ -union bhnd_nvram_ident { - struct bhnd_nvram_header bcm; - char btxt[4]; - struct bhnd_tlv_ident { - uint8_t tag; - uint8_t size[2]; - uint8_t flags; - } __packed tlv; -}; - -/** bhnd nvram parser instance state */ -struct bhnd_nvram { - device_t dev; /**< parent device, or NULL */ - const struct bhnd_nvram_ops *ops; - uint8_t *buf; /**< nvram data */ - size_t buf_size; - size_t num_buf_vars; /**< number of records in @p buf (0 if not yet calculated) */ - - struct bhnd_nvram_idx *idx; /**< key index */ - - struct bhnd_nvram_devpaths devpaths; /**< device paths */ - struct bhnd_nvram_varmap defaults; /**< default values */ - struct bhnd_nvram_varmap pending; /**< uncommitted writes */ -}; - -#endif /* _BHND_NVRAM_BHND_NVRAM_PARSER_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_parservar.h b/sys/dev/bhnd/nvram/bhnd_nvram_parservar.h deleted file mode 100644 index a02dfe0d21b..00000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram_parservar.h +++ /dev/null @@ -1,86 +0,0 @@ -/*- - * 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_PARSERVAR_H_ -#define _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_ - -#include - -#include "bhnd_nvram_common.h" - -#include "bhnd_nvram_parser.h" - -#define NVRAM_IDX_VAR_THRESH 15 /**< index is generated if minimum variable count is met */ -#define NVRAM_IDX_OFFSET_MAX UINT16_MAX /**< maximum indexable offset */ -#define NVRAM_IDX_LEN_MAX UINT8_MAX /**< maximum indexable key/value length */ - -#define NVRAM_KEY_MAX 64 /**< maximum key length (not incl. NUL) */ -#define NVRAM_VAL_MAX 255 /**< maximum value length (not incl. NUL) */ - -#define NVRAM_DEVPATH_STR "devpath" /**< name prefix of device path aliases */ -#define NVRAM_DEVPATH_LEN (sizeof(NVRAM_DEVPATH_STR) - 1) - -#define NVRAM_SMALL_HASH_SIZE 16 /**< hash table size for pending/default tuples */ - -/** - * NVRAM devpath record. - * - * Aliases index values to full device paths. - */ -struct bhnd_nvram_devpath { - u_long index; /** alias index */ - char *path; /** aliased path */ - - LIST_ENTRY(bhnd_nvram_devpath) dp_link; -}; - -/** - * NVRAM index record. - * - * Provides entry offsets into a backing NVRAM buffer. - */ -struct bhnd_nvram_idx_entry { - uint16_t env_offset; /**< offset to env string */ - uint8_t key_len; /**< key length */ - uint8_t val_len; /**< value length */ -}; - -/** - * NVRAM index. - * - * Provides a compact binary search index into the backing NVRAM buffer. - */ -struct bhnd_nvram_idx { - size_t num_entries; /**< entry count */ - struct bhnd_nvram_idx_entry entries[]; /**< index entries */ -}; - -#endif /* _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_private.h b/sys/dev/bhnd/nvram/bhnd_nvram_private.h new file mode 100644 index 00000000000..e7b33732acc --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_private.h @@ -0,0 +1,402 @@ +/*- + * 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_PRIVATE_H_ +#define _BHND_NVRAM_BHND_NVRAM_PRIVATE_H_ + +/* + * Private BHND NVRAM definitions. + */ + +#include + +#ifdef _KERNEL +#include + +#include +#else +#include +#include +#include +#include +#endif + +#include "bhnd_nvram.h" +#include "bhnd_nvram_value.h" + +/* + * bhnd_nvram_crc8() lookup table. + */ +extern const uint8_t bhnd_nvram_crc8_tab[]; + +/* Forward declarations */ +struct bhnd_nvram_vardefn; + +#ifdef _KERNEL + +MALLOC_DECLARE(M_BHND_NVRAM); + +#define bhnd_nv_isupper(c) isupper(c) +#define bhnd_nv_islower(c) islower(c) +#define bhnd_nv_isalpha(c) isalpha(c) +#define bhnd_nv_isprint(c) isprint(c) +#define bhnd_nv_isspace(c) isspace(c) +#define bhnd_nv_isdigit(c) isdigit(c) +#define bhnd_nv_isxdigit(c) isxdigit(c) +#define bhnd_nv_toupper(c) toupper(c) + +#define bhnd_nv_malloc(size) malloc((size), M_BHND_NVRAM, M_WAITOK) +#define bhnd_nv_calloc(n, size) malloc((n) * (size), M_BHND_NVRAM, \ + M_WAITOK | M_ZERO) +#define bhnd_nv_reallocf(buf, size) reallocf((buf), (size), M_BHND_NVRAM, \ + M_WAITOK) +#define bhnd_nv_free(buf) free((buf), M_BHND_NVRAM) +#define bhnd_nv_strndup(str, len) strndup(str, len, M_BHND_NVRAM) + +#ifdef INVARIANTS +#define BHND_NV_INVARIANTS +#endif + +#define BHND_NV_ASSERT(expr, ...) KASSERT(expr, __VA_ARGS__) + +#define BHND_NV_VERBOSE (bootverbose) +#define BHND_NV_PANIC(...) panic(__VA_ARGS__) +#define BHND_NV_LOG(fmt, ...) \ + printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__) + +#define bhnd_nv_ummax(a, b) ummax((a), (b)) +#define bhnd_nv_ummin(a, b) ummin((a), (b)) + +#else /* !_KERNEL */ + +#include +#include +#include +#include + +/* ASCII-specific ctype variants that work consistently regardless + * of current locale */ +#define bhnd_nv_isupper(c) ((c) >= 'A' && (c) <= 'Z') +#define bhnd_nv_islower(c) ((c) >= 'a' && (c) <= 'z') +#define bhnd_nv_isalpha(c) (bhnd_nv_isupper(c) || bhnd_nv_islower(c)) +#define bhnd_nv_isprint(c) ((c) >= ' ' && (c) <= '~') +#define bhnd_nv_isspace(c) ((c) == ' ' || ((c) >= '\t' && (c) <= '\r')) +#define bhnd_nv_isdigit(c) isdigit(c) +#define bhnd_nv_isxdigit(c) isxdigit(c) +#define bhnd_nv_toupper(c) ((c) - \ + (('a' - 'A') * ((c) >= 'a' && (c) <= 'z'))) + +#define bhnd_nv_malloc(size) malloc((size)) +#define bhnd_nv_calloc(n, size) calloc((n), (size)) +#define bhnd_nv_reallocf(buf, size) reallocf((buf), (size)) +#define bhnd_nv_free(buf) free((buf)) +#define bhnd_nv_strndup(str, len) strndup(str, len) + +#ifndef NDEBUG +#define BHND_NV_INVARIANTS +#endif + +#define BHND_NV_ASSERT(expr, ...) assert(expr) + +#define BHND_NV_VERBOSE (0) +#define BHND_NV_PANIC(fmt, ...) do { \ + fprintf(stderr, "panic: " fmt "\n", ##__VA_ARGS__); \ + abort(); \ +} while(0) +#define BHND_NV_LOG(fmt, ...) \ + fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) + +static inline uintmax_t +bhnd_nv_ummax(uintmax_t a, uintmax_t b) +{ + return (a > b ? a : b); +} + +static inline uintmax_t +bhnd_nv_ummin(uintmax_t a, uintmax_t b) +{ + + return (a < b ? a : b); +} + +#endif /* _KERNEL */ + +#ifdef BHND_NV_VERBOSE +#define BHND_NV_DEBUG(...) BHND_NV_LOG(__VA_ARGS__) +#else /* !BHND_NV_VERBOSE */ +#define BHND_NV_DEBUG(...) +#endif /* BHND_NV_VERBOSE */ + +/* Limit a size_t value to a suitable range for use as a printf string field + * width */ +#define BHND_NV_PRINT_WIDTH(_len) \ + ((_len) > (INT_MAX) ? (INT_MAX) : (int)(_len)) + +int bhnd_nvram_value_coerce(const void *inp, + size_t ilen, bhnd_nvram_type itype, + void *outp, size_t *olen, + bhnd_nvram_type otype); + +int bhnd_nvram_value_nelem(bhnd_nvram_type type, + const void *data, size_t len, + size_t *nelem); +size_t bhnd_nvram_value_size(bhnd_nvram_type type, + const void *data, size_t nbytes, + size_t nelem); + +int bhnd_nvram_value_printf(const char *fmt, + const void *inp, size_t ilen, + bhnd_nvram_type itype, char *outp, + size_t *olen, ...); +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); + +const struct bhnd_nvram_vardefn *bhnd_nvram_find_vardefn(const char *varname); +const struct bhnd_nvram_vardefn *bhnd_nvram_get_vardefn(size_t id); +size_t bhnd_nvram_get_vardefn_id( + const struct bhnd_nvram_vardefn *defn); + +int bhnd_nvram_parse_int(const char *s, + size_t maxlen, u_int base, size_t *nbytes, + void *outp, size_t *olen, + bhnd_nvram_type otype); + +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); + +size_t bhnd_nvram_parse_field(const char **inp, + size_t ilen, char delim); +size_t bhnd_nvram_trim_field(const char **inp, + size_t ilen, char delim); + +bool bhnd_nvram_validate_name(const char *name, + size_t name_len); + +/** + * Calculate CRC-8 over @p buf using the Broadcom SPROM/NVRAM CRC-8 + * polynomial. + * + * @param buf input buffer + * @param size buffer size + * @param crc last computed crc, or BHND_NVRAM_CRC8_INITIAL + */ +static inline uint8_t +bhnd_nvram_crc8(const void *buf, size_t size, uint8_t crc) +{ + const uint8_t *p = (const uint8_t *)buf; + while (size--) + crc = bhnd_nvram_crc8_tab[(crc ^ *p++)]; + + return (crc); +} + +#define BHND_NVRAM_CRC8_INITIAL 0xFF /**< Initial bhnd_nvram_crc8 value */ +#define BHND_NVRAM_CRC8_VALID 0x9F /**< Valid CRC-8 checksum */ + +/** NVRAM variable flags */ +enum { + BHND_NVRAM_VF_MFGINT = 1<<0, /**< mfg-internal variable; should not + be externally visible */ + BHND_NVRAM_VF_IGNALL1 = 1<<1 /**< hide variable if its value has all + bits set. */ +}; + +/** + * SPROM layout flags + */ +enum { + /** + * SPROM layout does not have magic identification value. + * + * This applies to SPROM revisions 1-3, where the actual + * layout must be determined by looking for a matching sromrev + * at the expected offset, and then verifying the CRC to ensure + * that the match was not a false positive. + */ + SPROM_LAYOUT_MAGIC_NONE = (1<<0), +}; + +/** NVRAM variable definition */ +struct bhnd_nvram_vardefn { + const char *name; /**< variable name */ + const char *desc; /**< human readable description, + or NULL */ + const char *help; /**< human readable help text, + or NULL */ + bhnd_nvram_type type; /**< variable type */ + uint8_t nelem; /**< element count, or 1 if not + an array-typed variable */ + const bhnd_nvram_val_fmt_t *fmt; /**< value format, or NULL */ + uint32_t flags; /**< flags (BHND_NVRAM_VF_*) */ +}; + +/* + * NVRAM variable definitions generated from nvram_map. + */ +extern const struct bhnd_nvram_vardefn bhnd_nvram_vardefns[]; +extern const size_t bhnd_nvram_num_vardefns; + +/** + * SPROM layout descriptor. + */ +struct bhnd_sprom_layout { + size_t size; /**< SPROM image size, in bytes */ + uint8_t rev; /**< SPROM revision */ + uint8_t flags; /**< layout flags (SPROM_LAYOUT_*) */ + size_t srev_offset; /**< offset to SROM revision */ + size_t magic_offset; /**< offset to magic value */ + uint16_t magic_value; /**< expected magic value */ + const uint8_t *bindings; /**< SPROM binding opcode table */ + size_t bindings_size; /**< SPROM binding opcode table size */ + uint16_t num_vars; /**< total number of variables defined + for this layout by the binding + table */ +}; + +/* + * SPROM layout descriptions generated from nvram_map. + */ +extern const struct bhnd_sprom_layout bhnd_sprom_layouts[]; +extern const size_t bhnd_sprom_num_layouts; + +/* + * SPROM binding opcodes. + * + * Most opcodes are provided with two variants: + * + * - Standard: The opcode's data directly follows the opcode. The data type + * (SPROM_OPCODE_DATA_*) is encoded in the opcode immediate (IMM). + * - Immediate: The opcode's data is encoded directly in the opcode immediate + * (IMM). + */ +#define SPROM_OPC_MASK 0xF0 /**< operation mask */ +#define SPROM_IMM_MASK 0x0F /**< immediate value mask */ +#define SPROM_IMM_MAX SPROM_IMM_MASK +#define SPROM_OP_DATA_U8 0x00 /**< data is u8 */ +#define SPROM_OP_DATA_U8_SCALED 0x01 /**< data is u8; multiply by + type width */ +#define SPROM_OP_DATA_U16 0x02 /**< data is u16-le */ +#define SPROM_OP_DATA_U32 0x03 /**< data is u32-le */ +#define SPROM_OP_DATA_I8 0x04 /**< data is i8 */ +#define SPROM_OPCODE_EXT 0x00 /**< extended opcodes defined + in IMM */ +#define SPROM_OPCODE_EOF 0x00 /**< marks end of opcode + stream */ +#define SPROM_OPCODE_NELEM 0x01 /**< variable array element + count follows as U8 */ +#define SPROM_OPCODE_VAR_END 0x02 /**< marks end of variable + definition */ +#define SPROM_OPCODE_TYPE 0x03 /**< input type follows as U8 + (see BHND_NVRAM_TYPE_*) */ +#define SPROM_OPCODE_VAR_IMM 0x10 /**< variable ID (imm) */ +#define SPROM_OPCODE_VAR_REL_IMM 0x20 /**< relative variable ID + (last ID + imm) */ +#define SPROM_OPCODE_VAR 0x30 /**< variable ID */ +#define SPROM_OPCODE_REV_IMM 0x40 /**< revision range (imm) */ +#define SPROM_OPCODE_REV_RANGE 0x50 /**< revision range (8-bit range)*/ +#define SPROM_OP_REV_RANGE_MAX 0x0F /**< maximum representable SROM + revision */ +#define SPROM_OP_REV_START_MASK 0xF0 +#define SPROM_OP_REV_START_SHIFT 4 +#define SPROM_OP_REV_END_MASK 0x0F +#define SPROM_OP_REV_END_SHIFT 0 +#define SPROM_OPCODE_MASK_IMM 0x60 /**< value mask (imm) */ +#define SPROM_OPCODE_MASK 0x70 /**< value mask */ +#define SPROM_OPCODE_SHIFT_IMM 0x80 /**< value shift (unsigned + imm, multipled by 2) */ +#define SPROM_OPCODE_SHIFT 0x90 /**< value shift */ +#define SPROM_OPCODE_OFFSET_REL_IMM 0xA0 /**< relative input offset + (last offset + + (imm * type width)) */ +#define SPROM_OPCODE_OFFSET 0xB0 /**< input offset */ +#define SPROM_OPCODE_TYPE_IMM 0xC0 /**< input type (imm, + see BHND_NVRAM_TYPE_*) */ +#define SPROM_OPCODE_DO_BIND 0xD0 /**< bind current value, + advance input/output + offsets as per IMM */ +#define SPROM_OP_BIND_SKIP_IN_MASK 0x03 /**< the number of input + elements to advance after + the bind */ +#define SPROM_OP_BIND_SKIP_IN_SHIFT 0 +#define SPROM_OP_BIND_SKIP_IN_SIGN (1<<2) /**< SKIP_IN sign bit */ +#define SPROM_OP_BIND_SKIP_OUT_MASK 0x08 /**< the number of output + elements to advance after + the bind */ +#define SPROM_OP_BIND_SKIP_OUT_SHIFT 3 +#define SPROM_OPCODE_DO_BINDN_IMM 0xE0 /**< bind IMM times, advancing + input/output offsets by one + element each time */ +#define SPROM_OPCODE_DO_BINDN 0xF0 /**< bind N times, advancing + input/output offsets as per + SPROM_OP_BIND_SKIP_IN/SPROM_OP_BIND_SKIP_OUT + IMM values. The U8 element + count follows. */ + +/** Evaluates to true if opcode is an extended opcode */ +#define SPROM_OPCODE_IS_EXT(_opcode) \ + (((_opcode) & SPROM_OPC_MASK) == SPROM_OPCODE_EXT) + +/** Return the opcode constant for a simple or extended opcode */ +#define SPROM_OPCODE_OP(_opcode) \ + (SPROM_OPCODE_IS_EXT(_opcode) ? (_opcode) : ((_opcode) & SPROM_OPC_MASK)) + +/** Return the opcode immediate for a simple opcode, or zero if this is + * an extended opcode */ +#define SPROM_OPCODE_IMM(_opcode) \ + (SPROM_OPCODE_IS_EXT(_opcode) ? 0 : ((_opcode) & SPROM_IMM_MASK)) + +/** Evaluates to true if the given opcode produces an implicit + * SPROM_OPCODE_VAR_END instruction for any open variable */ +#define SPROM_OP_IS_IMPLICIT_VAR_END(_opcode) \ + (((_opcode) == SPROM_OPCODE_VAR_IMM) || \ + ((_opcode) == SPROM_OPCODE_VAR_REL_IMM) || \ + ((_opcode) == SPROM_OPCODE_VAR) || \ + ((_opcode) == SPROM_OPCODE_REV_IMM) || \ + ((_opcode) == SPROM_OPCODE_REV_RANGE)) + +/** Evaluates to true if the given opcode is either an explicit + * SPROM_OPCODE_VAR_END instruction, or is an opcode that produces an + * implicit terminatation of any open variable */ +#define SPROM_OP_IS_VAR_END(_opcode) \ + (((_opcode) == SPROM_OPCODE_VAR_END) || \ + SPROM_OP_IS_IMPLICIT_VAR_END(_opcode)) + +/** maximum representable immediate value */ +#define SPROM_OP_IMM_MAX SPROM_IMM_MASK + +/** maximum representable SROM revision */ +#define SPROM_OP_REV_MAX MAX(SPROM_OP_REV_RANGE_MAX, SPROM_IMM_MAX) + +#endif /* _BHND_NVRAM_BHND_NVRAM_PRIVATE_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_store.c b/sys/dev/bhnd/nvram/bhnd_nvram_store.c new file mode 100644 index 00000000000..1b4770aae16 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_store.c @@ -0,0 +1,572 @@ +/*- + * 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_t *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(type, buf, len, 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_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: + // 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); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvramvar.h b/sys/dev/bhnd/nvram/bhnd_nvram_store.h similarity index 60% rename from sys/dev/bhnd/nvram/bhnd_nvramvar.h rename to sys/dev/bhnd/nvram/bhnd_nvram_store.h index 890bd2e3174..28274e20ca6 100644 --- a/sys/dev/bhnd/nvram/bhnd_nvramvar.h +++ b/sys/dev/bhnd/nvram/bhnd_nvram_store.h @@ -29,40 +29,40 @@ * $FreeBSD$ */ -#ifndef _BHND_NVRAM_BHND_NVRAMVAR_H_ -#define _BHND_NVRAM_BHND_NVRAMVAR_H_ +#ifndef _BHND_NVRAM_BHND_NVRAM_STORE_H_ +#define _BHND_NVRAM_BHND_NVRAM_STORE_H_ +#ifdef _KERNEL #include #include +#include +#else /* !_KERNEL */ +#include -#include "bhnd_nvram_parser.h" +#include -DECLARE_CLASS(bhnd_nvram_driver); +#include +#include +#endif -int bhnd_nvram_probe(device_t dev); -int bhnd_nvram_attach(device_t dev, void *data, size_t size, - bhnd_nvram_format fmt); -int bhnd_nvram_resume(device_t dev); -int bhnd_nvram_suspend(device_t dev); -int bhnd_nvram_detach(device_t dev); +#include -/** - * bhnd_nvram driver instance state. Must be first member of all subclass - * softc structures. - */ -struct bhnd_nvram_softc { - device_t dev; - struct mtx mtx; /**< nvram mutex */ - struct bhnd_nvram nvram; /**< nvram shadow */ -}; +#include "bhnd_nvram_data.h" +#include "bhnd_nvram_io.h" +struct bhnd_nvram_store; -#define BHND_NVRAM_LOCK_INIT(sc) \ - mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ - "bhnd_nvram lock", MTX_DEF) -#define BHND_NVRAM_LOCK(sc) mtx_lock(&(sc)->mtx) -#define BHND_NVRAM_UNLOCK(sc) mtx_unlock(&(sc)->mtx) -#define BHND_NVRAM_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what) -#define BHND_NVRAM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx) +int bhnd_nvram_store_new(struct bhnd_nvram_store **store, + struct bhnd_nvram_data *data); -#endif /* _BHND_NVRAM_BHND_NVRAMVAR_H_ */ +int bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store, + struct bhnd_nvram_io *io, bhnd_nvram_data_class_t *cls); + +void bhnd_nvram_store_free(struct bhnd_nvram_store *store); + +int bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name, + void *buf, size_t *len, bhnd_nvram_type type); +int bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name, + const void *buf, size_t len, bhnd_nvram_type type); + +#endif /* _BHND_NVRAM_BHND_NVRAM_STORE_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h b/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h new file mode 100644 index 00000000000..9dcccfe4dff --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h @@ -0,0 +1,111 @@ +/*- + * 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_STOREVAR_H_ +#define _BHND_NVRAM_BHND_NVRAM_STOREVAR_H_ + +#include + +#ifndef _KERNEL +#include +#endif + +#include "bhnd_nvram_store.h" + +/** Index is only generated if minimum variable count is met */ +#define NVRAM_IDX_VAR_THRESH 15 + +#define BHND_NVSTORE_PATH_ALIAS_NONE ULONG_MAX + +LIST_HEAD(bhnd_nvstore_paths, bhnd_nvstore_path); + +/** + * NVRAM store path. + */ +struct bhnd_nvstore_path { + char *path; /** relative path */ + u_long index; /** aliased path index, or + BHND_NVSTORE_PATH_IDX_INVALID */ + + LIST_ENTRY(bhnd_nvstore_path) dp_link; +}; + +/** + * NVRAM store index. + * + * Provides effecient name-based lookup by maintaining an array of cached + * cookiep values, sorted lexicographically by variable name. + */ +struct bhnd_nvstore_index { + size_t num_cookiep; /**< cookiep count */ + void *cookiep[]; /**< cookiep values */ +}; + + +/** bhnd nvram store instance state */ +struct bhnd_nvram_store { +#ifdef _KERNEL + struct mtx mtx; +#else + pthread_mutex_t mtx; +#endif + struct bhnd_nvram_data *nv; /**< backing data */ + struct bhnd_nvstore_index *idx; /**< index, or NULL */ + struct bhnd_nvstore_paths paths; /**< paths */ + nvlist_t *pending; /**< uncommitted writes */ +}; + +#ifdef _KERNEL + +#define BHND_NVSTORE_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, "BHND NVRAM store lock", NULL, MTX_DEF) +#define BHND_NVSTORE_LOCK(sc) mtx_lock(&(sc)->mtx) +#define BHND_NVSTORE_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define BHND_NVSTORE_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what) +#define BHND_NVSTORE_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx) + +#else /* !_KERNEL */ + +#define BHND_NVSTORE_LOCK_INIT(sc) do { \ + int error = pthread_mutex_init(&(sc)->mtx, NULL); \ + if (error) \ + BHND_NV_PANIC("pthread_mutex_init() failed: %d", \ + error); \ +} while(0) + +#define BHND_NVSTORE_LOCK(sc) pthread_mutex_lock(&(sc)->mtx) +#define BHND_NVSTORE_UNLOCK(sc) pthread_mutex_unlock(&(sc)->mtx) +#define BHND_NVSTORE_LOCK_DESTROY(sc) pthread_mutex_destroy(&(sc)->mtx) +#define BHND_NVSTORE_LOCK_ASSERT(sc, what) + +#endif /* _KERNEL */ + +#endif /* _BHND_NVRAM_BHND_NVRAM_STOREVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_subr.c b/sys/dev/bhnd/nvram/bhnd_nvram_subr.c new file mode 100644 index 00000000000..7fcb4828103 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_subr.c @@ -0,0 +1,1271 @@ +/*- + * 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 + +/** signed/unsigned 32-bit integer value storage */ +union bhnd_nvram_int_storage { + uint32_t u32; + int32_t s32; +}; + +/* + * 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_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[]"); + } + + /* 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_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: + 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_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: + 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: + 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: + 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: + 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); + } + + /* Quiesce gcc4.2 */ + BHND_NV_PANIC("bhnd nvram type %u unknown", type); +} + +/** + * Calculate the number of elements represented by a value of @p len bytes + * with @p type. + * + * @param type The value type. + * @param data The actual data to be queried, or NULL if unknown. + * @param len The length in bytes of @p data, or if @p data is NULL, + * the expected length in bytes. + * @param[out] nelem On success, the number of elements. If @p type is not + * a fixed width type (e.g. BHND_NVRAM_TYPE_STRING_ARRAY), + * and @p data is NULL, an @p nelem value of 0 will be + * returned. + * + * @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 len is not correctly aligned for elements of + * @p type. + */ +int +bhnd_nvram_value_nelem(bhnd_nvram_type type, const void *data, size_t len, + size_t *nelem) +{ + bhnd_nvram_type base_type; + size_t base_size; + + /* Length must be aligned to the element size */ + base_type = bhnd_nvram_base_type(type); + base_size = bhnd_nvram_value_size(base_type, NULL, 0, 1); + if (base_size != 0 && len % base_size != 0) + return (EFAULT); + + switch (type) { + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_STRING_ARRAY: { + const char *p; + size_t nleft; + + /* Cannot determine the element count without parsing + * the actual data */ + if (data == NULL) { + *nelem = 0; + return (0); + } + + /* Iterate over the NUL-terminated strings to calculate + * total element count */ + p = data; + nleft = len; + *nelem = 0; + while (nleft > 0) { + size_t slen; + + /* Increment element count */ + (*nelem)++; + + /* If not a string array, data must not contain more + * than one entry. */ + if (!bhnd_nvram_is_array_type(type) && *nelem > 1) + return (EFTYPE); + + /* 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_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: + /* Length must be equal to the size of exactly one + * element (arrays can represent zero elements -- non-array + * types cannot) */ + if (len != base_size) + return (EFTYPE); + *nelem = 1; + 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: + BHND_NV_ASSERT(base_size != 0, ("invalid base size")); + *nelem = len / base_size; + return (0); + } + + /* Quiesce gcc4.2 */ + BHND_NV_PANIC("bhnd nvram type %u unknown", type); +} + +/** + * Return the size, in bytes, of a value of @p type with @p nelem elements. + * + * @param type The value type. + * @param data 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 nbytes The size of @p data, in bytes, or 0 if @p data is NULL. + * @param nelem The number of elements. If @p type is not an array type, + * this value must be 1. + * + * @retval 0 If @p type has a variable width, and @p data is NULL. + * @retval 0 If a @p nelem value greater than 1 is provided for a + * non-array @p type. + * @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 non-zero The size, in bytes, of @p type with @p nelem elements. + */ +size_t +bhnd_nvram_value_size(bhnd_nvram_type type, const void *data, size_t nbytes, + 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(type) && nelem != 1) + return (0); + + switch (type) { + 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: { + bhnd_nvram_type base_type; + size_t base_size; + + base_type = bhnd_nvram_base_type(type); + base_size = bhnd_nvram_value_size(base_type, NULL, 0, 1); + + /* Would nelem * base_size overflow? */ + if (SIZE_MAX / nelem < base_size) { + BHND_NV_LOG("cannot represent size %s * %zu\n", + bhnd_nvram_type_name(base_type), nelem); + return (0); + } + + return (nelem * base_size); + } + + case BHND_NVRAM_TYPE_STRING_ARRAY: { + const char *p; + size_t total_size; + + if (data == NULL) + return (0); + + /* Iterate over the NUL-terminated strings to calculate + * total byte length */ + p = data; + total_size = 0; + for (size_t i = 0; i < nelem; i++) { + size_t elem_size; + + elem_size = strnlen(p, nbytes - total_size); + p += elem_size; + + /* Check for (and skip) terminating NUL */ + if (total_size < nbytes && *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 (data == NULL) + return (0); + + /* Find length */ + size = strnlen(data, nbytes); + + /* Is there a terminating NUL, or did we just hit the + * end of the string input */ + if (size < nbytes) + size++; + + return (size); + } + 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", type); +} + +/** + * Iterate over all strings in the @p inp string array. + * + * @param inp The string array to be iterated. This must be a buffer + * of one or more NUL-terminated strings -- + * @see BHND_NVRAM_TYPE_STRING_ARRAY. + * @param ilen The size, in bytes, of @p inp, including any + * terminating NUL character(s). + * @param prev The value previously returned by + * bhnd_nvram_string_array_next(), or NULL to begin + * iteration. + * + * @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 nremain, plen; + + if (ilen == 0) + return (NULL); + + if (prev == NULL) + return (inp); + + /* Advance to next value */ + BHND_NV_ASSERT(prev >= inp, ("invalid prev pointer")); + BHND_NV_ASSERT(prev < (inp+ilen), ("invalid prev pointer")); + + nremain = ilen - (size_t)(prev - inp); + plen = strnlen(prev, nremain); + nremain -= plen; + + /* Only a trailing NUL remains? */ + if (nremain <= 1) + return (NULL); + + return (prev + plen + 1); +} + +/** + * 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_t 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); +} + +/* 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); +} + +/** + * 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_t 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); +} + +/** + * 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_value_size(otype, NULL, 0, 1); + 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); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_value.c b/sys/dev/bhnd/nvram/bhnd_nvram_value.c new file mode 100644 index 00000000000..8454fb6a1a2 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_value.c @@ -0,0 +1,1313 @@ +/*- + * 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 + +#else /* !_KERNEL */ + +#include +#include +#include +#include + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_valuevar.h" + + +static void *bhnd_nvram_val_alloc_bytes(bhnd_nvram_val_t *value, + size_t ilen, bhnd_nvram_type itype, + uint32_t flags); +static int bhnd_nvram_val_set(bhnd_nvram_val_t *value, const void *inp, + size_t ilen, bhnd_nvram_type itype, + uint32_t flags); +static int bhnd_nvram_val_set_inline(bhnd_nvram_val_t *value, + const void *inp, size_t ilen, bhnd_nvram_type itype); + +#define BHND_NVRAM_VAL_INITIALIZER(_fmt, _storage) \ + (bhnd_nvram_val_t) { \ + .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")) + +/* Common initialization support for bhnd_nvram_val_init() and + * bhnd_nvram_val_new() */ +static int +bhnd_nvram_val_init_common(bhnd_nvram_val_t *value, bhnd_nvram_val_storage_t + val_storage, const bhnd_nvram_val_fmt_t *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; + + /* Determine expected data type, and allow the format to delegate to + * a new format instance */ + if (fmt != NULL && fmt->op_filter != NULL) { + const bhnd_nvram_val_fmt_t *nfmt = fmt; + + /* Use the filter function to determine whether direct + * initialization from is itype permitted */ + error = fmt->op_filter(&nfmt, inp, ilen, itype); + if (error) + return (error); + + /* Retry initialization with new format? */ + if (nfmt != fmt) { + return (bhnd_nvram_val_init_common(value, val_storage, + nfmt, inp, ilen, itype, flags)); + } + + /* Value can be initialized with provided input type */ + otype = itype; + + } else if (fmt != NULL) { + /* Value must be initialized with the format's native + * type */ + otype = fmt->native_type; + + } else { + /* No format specified; we can initialize directly from the + * input data, and we'll handle all format operations + * internally. */ + 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_t *value, const bhnd_nvram_val_fmt_t *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_t **value, const bhnd_nvram_val_fmt_t *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); +} + +/** + * 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_t if @p value was successfully copied or retained. + * @retval NULL if allocation failed. + */ +bhnd_nvram_val_t * +bhnd_nvram_val_copy(bhnd_nvram_val_t *value) +{ + bhnd_nvram_val_t *result; + const void *bytes; + bhnd_nvram_type type; + size_t len; + uint32_t flags; + int error; + + /* If dynamically allocated, simply bump the reference count */ + if (value->val_storage == BHND_NVRAM_VAL_STORAGE_DYNAMIC) { + refcount_acquire(&value->refs); + return (value); + } + + /* Otherwise, we need to perform an actual copy */ + BHND_NV_ASSERT(value->refs == 1, ("non-allocated value has " + "active refcount (%u)", value->refs)); + + /* 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_t *value) +{ + BHND_NV_ASSERT(value->refs >= 1, ("value over-released")); + + /* Drop reference */ + if (!refcount_release(&value->refs)) + return; + + /* Free allocated external representation data */ + if (value->data_storage == BHND_NVRAM_VAL_DATA_EXT_ALLOC) + bhnd_nv_free(__DECONST(void *, value->data.ptr)); + + /* Free instance if dynamically allocated */ + if (value->val_storage == BHND_NVRAM_VAL_STORAGE_DYNAMIC) + bhnd_nv_free(value); +} + +/** + * 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(void *outp, size_t *olen, bhnd_nvram_type otype, + const void *inp, size_t ilen, bhnd_nvram_type itype) +{ + 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 field data */ + switch (otype) { + 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_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(void *outp, size_t *olen, bhnd_nvram_type otype, + const void *inp, size_t ilen, bhnd_nvram_type itype) +{ + 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_CHAR: + case BHND_NVRAM_TYPE_CHAR_ARRAY: + 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_t *value, void *outp, size_t *olen, + bhnd_nvram_type otype) +{ + /* Prefer format implementation */ + if (value->fmt != NULL && 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_t *value, const void *inp, + size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) +{ + /* Prefer format implementation */ + if (value->fmt != NULL && 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_t *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] len If prev is non-NULL, len 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_t *value, const void *prev, size_t *len) +{ + /* Prefer the format implementation */ + if (value->fmt != NULL && value->fmt->op_next != NULL) + return (value->fmt->op_next(value, prev, len)); + + return (bhnd_nvram_val_generic_next(value, prev, len)); +} + +/** + * Return value's element data type. + * + * @param value The value to be queried. + */ +bhnd_nvram_type +bhnd_nvram_val_elem_type(bhnd_nvram_val_t *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_t *value) +{ + const void *bytes; + bhnd_nvram_type type; + size_t nelem, len; + int error; + + /* Prefer format implementation */ + if (value->fmt != NULL && 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 != NULL && 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(type, bytes, len, &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_t *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); + + /* + * Normally, a rank polymorphic type like a character array would not + * be representable as a rank 1 type. + * + * As a special-cased exception, we can support conversion directly + * from CHAR_ARRAY to STRING by treating the character array as a + * non-NUL-terminated string. + * + * This conversion is isomorphic; we also support conversion directly + * from a STRING to a CHAR_ARRAY by the same mechanism. + */ + inp = bhnd_nvram_val_bytes(value, &ilen, &itype); + if ((itype == BHND_NVRAM_TYPE_CHAR_ARRAY && + otype == BHND_NVRAM_TYPE_STRING) || + (itype == BHND_NVRAM_TYPE_STRING && + otype == BHND_NVRAM_TYPE_CHAR_ARRAY)) + { + return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen, + otype)); + } + + /* + * 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_t *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_STRING: + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_CHAR_ARRAY: + return (bhnd_nvram_val_encode_string(outp, olen, otype, inp, + ilen, itype)); + + 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(outp, olen, otype, inp, ilen, + itype)); + + 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_t *value, const void *prev, + size_t *len) +{ + const uint8_t *inp; + const uint8_t *next; + bhnd_nvram_type itype; + size_t ilen; + size_t offset; + + /* Otherwise, default to iterating over the backing representation + * according to its native representation */ + inp = bhnd_nvram_val_bytes(value, &ilen, &itype); + + /* First element */ + if (prev == NULL) { + /* Zero-length array? */ + if (ilen == 0) + return (NULL); + + *len = bhnd_nvram_value_size(itype, inp, ilen, 1); + return (inp); + } + + /* Advance to next element */ + BHND_NV_ASSERT(prev >= (const void *)inp, ("invalid cookiep")); + next = (const uint8_t *)prev + *len; + offset = (size_t)(next - inp); + + if (offset >= ilen) { + /* Hit end of the array */ + return (NULL); + } + + /* Determine element size */ + *len = bhnd_nvram_value_size(itype, next, ilen - offset, 1); + if (ilen - offset < *len) + BHND_NV_PANIC("short element -- misaligned representation"); + + return (next); +} + +/** + * Initialize the representation of @p value with @p ptr. + * + * If @p value is an externally allocated instance and the representation + * cannot be represented inline, the given data will not be copied, and @p ptr + * must remain valid for the lifetime of @p value. + * + * Otherwise, @p value will be initialized with a copy of the @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_t *value, const void *inp, size_t ilen, + bhnd_nvram_type itype, uint32_t flags) +{ + void *bytes; + + BHND_NVRAM_VAL_ASSERT_EMPTY(value); + + /* Reference the external data */ + if ((flags & BHND_NVRAM_VAL_BORROW_DATA) || + (flags & BHND_NVRAM_VAL_STATIC_DATA)) + { + if (flags & BHND_NVRAM_VAL_BORROW_DATA) + value->data_storage = BHND_NVRAM_VAL_DATA_EXT_WEAK; + else + value->data_storage = BHND_NVRAM_VAL_DATA_EXT_STATIC; + + 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_t *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; \ +} 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_CHAR: + NV_STORE_INLINE(uint8_t, ch); + 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_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_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_t *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); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_value.h b/sys/dev/bhnd/nvram/bhnd_nvram_value.h new file mode 100644 index 00000000000..65f5f2e6161 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_value.h @@ -0,0 +1,224 @@ +/*- + * 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_t; +typedef struct bhnd_nvram_val bhnd_nvram_val_t; + +int bhnd_nvram_val_init(bhnd_nvram_val_t *value, + const bhnd_nvram_val_fmt_t *fmt, + const void *inp, size_t ilen, + bhnd_nvram_type itype, uint32_t flags); + +int bhnd_nvram_val_new(bhnd_nvram_val_t **value, + const bhnd_nvram_val_fmt_t *fmt, + const void *inp, size_t ilen, + bhnd_nvram_type itype, uint32_t flags); + +bhnd_nvram_val_t *bhnd_nvram_val_copy(bhnd_nvram_val_t *value); + +void bhnd_nvram_val_release( + bhnd_nvram_val_t *value); + +int bhnd_nvram_val_encode(bhnd_nvram_val_t *value, + void *outp, size_t *olen, + bhnd_nvram_type otype); + +int bhnd_nvram_val_encode_elem( + bhnd_nvram_val_t *value, const void *inp, + size_t ilen, void *outp, size_t *olen, + bhnd_nvram_type otype); + +int bhnd_nvram_val_printf(bhnd_nvram_val_t *value, + const char *fmt, char *outp, size_t *olen, + ...); +int bhnd_nvram_val_vprintf(bhnd_nvram_val_t *value, + const char *fmt, char *outp, size_t *olen, + va_list ap); + + +const void *bhnd_nvram_val_bytes(bhnd_nvram_val_t *value, + size_t *len, bhnd_nvram_type *itype); + +bhnd_nvram_type bhnd_nvram_val_elem_type( + bhnd_nvram_val_t *value); + +const void *bhnd_nvram_val_next(bhnd_nvram_val_t *value, + const void *prev, size_t *len); + +size_t bhnd_nvram_val_nelem(bhnd_nvram_val_t *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 or static 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, +} bhnd_nvram_val_storage_t; + +/** + * @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_t; + +/** + * NVRAM value + */ +struct bhnd_nvram_val { + volatile u_int refs; /**< reference count */ + bhnd_nvram_val_storage_t val_storage; /**< value structure storage */ + const bhnd_nvram_val_fmt_t *fmt; /**< value format, or NULL for default behavior */ + bhnd_nvram_val_data_storage_t 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 */ + const void *ptr; /**< external data */ + } data; +}; + +/** Declare a bhnd_nvram_val_fmt with name @p _n */ +#define BHND_NVRAM_VAL_TYPE_DECL(_n) \ + extern const bhnd_nvram_val_fmt_t bhnd_nvram_val_ ## _n ## _fmt; + +BHND_NVRAM_VAL_TYPE_DECL(bcm_decimal); +BHND_NVRAM_VAL_TYPE_DECL(bcm_hex); +BHND_NVRAM_VAL_TYPE_DECL(bcm_leddc); +BHND_NVRAM_VAL_TYPE_DECL(bcm_macaddr); +BHND_NVRAM_VAL_TYPE_DECL(bcm_string); + +#endif /* _BHND_NVRAM_BHND_NVRAM_VALUE_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c b/sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c new file mode 100644 index 00000000000..e5c7b85953f --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c @@ -0,0 +1,1032 @@ +/*- + * 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_t **fmt, const void *inp, + size_t ilen, bhnd_nvram_type itype); +static int bhnd_nvram_val_bcm_macaddr_encode( + bhnd_nvram_val_t *value, void *outp, size_t *olen, + bhnd_nvram_type otype); + +static int bhnd_nvram_val_bcm_macaddr_string_filter( + const bhnd_nvram_val_fmt_t **fmt, const void *inp, + size_t ilen, bhnd_nvram_type itype); +static int bhnd_nvram_val_bcm_macaddr_string_encode_elem( + bhnd_nvram_val_t *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_t *value, const void *prev, + size_t *len); + + +static int bhnd_nvram_val_bcm_int_filter( + const bhnd_nvram_val_fmt_t **fmt, const void *inp, + size_t ilen, bhnd_nvram_type itype); +static int bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val_t *value, + void *outp, size_t *olen, bhnd_nvram_type otype); + +static int bhnd_nvram_val_bcm_decimal_encode_elem( + bhnd_nvram_val_t *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_t *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_t **fmt, const void *inp, + size_t ilen, bhnd_nvram_type itype); +static int bhnd_nvram_val_bcm_leddc_encode_elem( + bhnd_nvram_val_t *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_t *value, + void *outp, size_t *olen, bhnd_nvram_type otype); + +static int bhnd_nvram_val_bcmstr_csv_filter( + const bhnd_nvram_val_fmt_t **fmt, const void *inp, + size_t ilen, bhnd_nvram_type itype); +static const void *bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val_t *value, + const void *prev, size_t *len); + +/** + * Broadcom NVRAM MAC address format. + */ +const bhnd_nvram_val_fmt_t 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_t 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_t 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_t 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_t 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_t 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_t 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, +}; + +/** + * Common hex/decimal integer filter implementation. + */ +static int +bhnd_nvram_val_bcm_int_filter(const bhnd_nvram_val_fmt_t **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_t *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_t *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_value_size(itype, NULL, 0, 1) * 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_t *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_t **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_t *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_t *value, void *outp, + size_t *olen, bhnd_nvram_type otype) +{ + bhnd_nvram_val_t array; + const bhnd_nvram_val_fmt_t *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_t **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_t *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: + next = bhnd_nvram_string_array_next(inp, ilen, prev); + if (next != NULL) { + *len = strlen(next); + + /* Account for trailing NUL */ + if (*len + (size_t)(next - inp) < ilen) + (*len)++; + } + + return (next); + default: + BHND_NV_PANIC("unsupported type: %d", itype); + } +} + +/** + * MAC address filter. + */ +static int +bhnd_nvram_val_bcm_macaddr_filter(const bhnd_nvram_val_fmt_t **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_t *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_t **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_t *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_t *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); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c b/sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c new file mode 100644 index 00000000000..ad57fd60905 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c @@ -0,0 +1,883 @@ +/*- + * 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" + +#ifdef _KERNEL +#define bhnd_nv_hex2ascii(hex) hex2ascii(hex) +#else /* !_KERNEL */ +static char const bhnd_nv_hex2ascii[] = "0123456789abcdefghijklmnopqrstuvwxyz"; +#define bhnd_nv_hex2ascii(hex) (bhnd_nv_hex2ascii[hex]) +#endif /* _KERNEL */ + +/** + * Maximum size, in bytes, of a string-encoded NVRAM integer value, not + * including any prefix (0x, 0, etc). + * + * We assume the largest possible encoding is the base-2 representation + * of a 64-bit integer. + */ +#define NV_NUMSTR_MAX ((sizeof(uint64_t) * CHAR_BIT) + 1) + +/** + * Format a string representation of @p value using @p fmt, with, writing the + * result to @p outp. + * + * @param value The value to be formatted. + * @param fmt The format string. + * @param[out] outp On success, the string 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 number of bytes required for the + * requested string encoding (including a trailing + * NUL). + * + * Refer to bhnd_nvram_val_vprintf() for full format string documentation. + * + * @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 value to a single 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_val_printf(bhnd_nvram_val_t *value, const char *fmt, char *outp, + size_t *olen, ...) +{ + va_list ap; + int error; + + va_start(ap, olen); + error = bhnd_nvram_val_vprintf(value, fmt, outp, olen, ap); + va_end(ap); + + return (error); +} + + +/** + * Format a string representation of the elements of @p value using @p fmt, + * writing the result to @p outp. + * + * @param value The value to be formatted. + * @param fmt The format string. + * @param[out] outp On success, the string 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 number of bytes required for the + * requested string encoding (including a trailing + * NUL). + * @param ap Argument list. + * + * @par Format Strings + * + * Value format strings are similar, but not identical to, those used + * by printf(3). + * + * Format specifier format: + * %[repeat][flags][width][.precision][length modifier][specifier] + * + * The format specifier is interpreted as an encoding directive for an + * individual value element; each format specifier will fetch the next element + * from the value, encode the element as the appropriate type based on the + * length modifiers and specifier, and then format the result as a string. + * + * For example, given a string value of '0x000F', and a format specifier of + * '%#hhx', the value will be asked to encode its first element as + * BHND_NVRAM_TYPE_UINT8. String formatting will then be applied to the 8-bit + * unsigned integer representation, producing a string value of "0xF". + * + * Repeat: + * - [digits] Repeatedly apply the format specifier to the input + * value's elements up to `digits` times. The delimiter + * must be passed as a string in the next variadic + * argument. + * - [] Repeatedly apply the format specifier to the input + * value's elements until all elements have been. The + * processed. The delimiter must be passed as a string in + * the next variadic argument. + * - [*] Repeatedly apply the format specifier to the input + * value's elements. The repeat count is read from the + * next variadic argument as a size_t value + * + * Flags: + * - '#' use alternative form (e.g. 0x/0X prefixing of hex + * strings). + * - '0' zero padding + * - '-' left adjust padding + * - '+' include a sign character + * - ' ' include a space in place of a sign character for + * positive numbers. + * + * Width/Precision: + * - digits minimum field width. + * - * read the minimum field width from the next variadic + * argument as a ssize_t value. A negative value enables + * left adjustment. + * - .digits field precision. + * - .* read the field precision from the next variadic argument + * as a ssize_t value. A negative value enables left + * adjustment. + * + * Length Modifiers: + * - 'hh', 'I8' Convert the value to an 8-bit signed or unsigned + * integer. + * - 'h', 'I16' Convert the value to an 16-bit signed or unsigned + * integer. + * - 'l', 'I32' Convert the value to an 32-bit signed or unsigned + * integer. + * - 'll', 'j', 'I64' Convert the value to an 64-bit signed or unsigned + * integer. + * + * Data Specifiers: + * - 'd', 'i' Convert and format as a signed decimal integer. + * - 'u' Convert and format as an unsigned decimal integer. + * - 'o' Convert and format as an unsigned octal integer. + * - 'x' Convert and format as an unsigned hexadecimal integer, + * using lowercase hex digits. + * - 'X' Convert and format as an unsigned hexadecimal integer, + * using uppercase hex digits. + * - 's' Convert and format as a string. + * - '%' Print a literal '%' character. + * + * @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 value to a single 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_val_vprintf(bhnd_nvram_val_t *value, const char *fmt, char *outp, + size_t *olen, va_list ap) +{ + const void *elem; + size_t elen; + size_t limit, nbytes; + int error; + + elem = NULL; + + /* Determine output byte limit */ + nbytes = 0; + if (outp != NULL) + limit = *olen; + else + limit = 0; + +#define WRITE_CHAR(_c) do { \ + if (limit > nbytes) \ + *(outp + nbytes) = _c; \ + \ + if (nbytes == SIZE_MAX) \ + return (EFTYPE); \ + nbytes++; \ +} while (0) + + /* Encode string value as per the format string */ + for (const char *p = fmt; *p != '\0'; p++) { + const char *delim; + size_t precision, width, delim_len; + u_long repeat, bits; + bool alt_form, ladjust, have_precision; + char padc, signc, lenc; + + padc = ' '; + signc = '\0'; + lenc = '\0'; + delim = ""; + delim_len = 0; + + ladjust = false; + alt_form = false; + + have_precision = false; + precision = 1; + bits = 32; + width = 0; + repeat = 1; + + /* Copy all input to output until we hit a format specifier */ + if (*p != '%') { + WRITE_CHAR(*p); + continue; + } + + /* Hit '%' -- is this followed by an escaped '%' literal? */ + p++; + if (*p == '%') { + WRITE_CHAR('%'); + p++; + continue; + } + + /* Parse repeat specifier */ + if (*p == '[') { + p++; + + /* Determine repeat count */ + if (*p == ']') { + /* Repeat consumes all input */ + repeat = bhnd_nvram_val_nelem(value); + } else if (*p == '*') { + /* Repeat is supplied as an argument */ + repeat = va_arg(ap, size_t); + p++; + } else { + char *endp; + + /* Repeat specified as argument */ + repeat = strtoul(p, &endp, 10); + if (p == endp) { + BHND_NV_LOG("error parsing repeat " + "count at '%s'", p); + return (EINVAL); + } + + /* Advance past repeat count */ + p = endp; + } + + /* Advance past terminating ']' */ + if (*p != ']') { + BHND_NV_LOG("error parsing repeat count at " + "'%s'", p); + return (EINVAL); + } + p++; + + delim = va_arg(ap, const char *); + delim_len = strlen(delim); + } + + /* Parse flags */ + while (*p != '\0') { + const char *np; + bool stop; + + stop = false; + np = p+1; + + switch (*p) { + case '#': + alt_form = true; + break; + case '0': + padc = '0'; + break; + case '-': + ladjust = true; + break; + case ' ': + /* Must not override '+' */ + if (signc != '+') + signc = ' '; + break; + case '+': + signc = '+'; + break; + default: + /* Non-flag character */ + stop = true; + break; + } + + if (stop) + break; + else + p = np; + } + + /* Parse minimum width */ + if (*p == '*') { + ssize_t arg; + + /* Width is supplied as an argument */ + arg = va_arg(ap, int); + + /* Negative width argument is interpreted as + * '-' flag followed by positive width */ + if (arg < 0) { + ladjust = true; + arg = -arg; + } + + width = arg; + p++; + } else if (bhnd_nv_isdigit(*p)) { + uint32_t v; + size_t len, parsed; + + /* Parse width value */ + len = sizeof(v); + error = bhnd_nvram_parse_int(p, strlen(p), 10, &parsed, + &v, &len, BHND_NVRAM_TYPE_UINT32); + if (error) { + BHND_NV_LOG("error parsing width %s: %d\n", p, + error); + return (EINVAL); + } + + /* Save width and advance input */ + width = v; + p += parsed; + } + + /* Parse precision */ + if (*p == '.') { + uint32_t v; + size_t len, parsed; + + p++; + have_precision = true; + + if (*p == '*') { + ssize_t arg; + + /* Precision is specified as an argument */ + arg = va_arg(ap, int); + + /* Negative precision argument is interpreted + * as '-' flag followed by positive + * precision */ + if (arg < 0) { + ladjust = true; + arg = -arg; + } + + precision = arg; + } else if (!bhnd_nv_isdigit(*p)) { + /* Implicit precision of 0 */ + precision = 0; + } else { + /* Parse precision value */ + len = sizeof(v); + error = bhnd_nvram_parse_int(p, strlen(p), 10, + &parsed, &v, &len, + BHND_NVRAM_TYPE_UINT32); + if (error) { + BHND_NV_LOG("error parsing width %s: " + "%d\n", p, error); + return (EINVAL); + } + + /* Save precision and advance input */ + precision = v; + p += parsed; + } + } + + /* Parse length modifiers */ + while (*p != '\0') { + const char *np; + bool stop; + + stop = false; + np = p+1; + + switch (*p) { + case 'h': + if (lenc == '\0') { + /* Set initial length value */ + lenc = *p; + bits = 16; + } else if (lenc == *p && bits == 16) { + /* Modify previous length value */ + bits = 8; + } else { + BHND_NV_LOG("invalid length modifier " + "%c\n", *p); + return (EINVAL); + } + break; + + case 'l': + if (lenc == '\0') { + /* Set initial length value */ + lenc = *p; + bits = 32; + } else if (lenc == *p && bits == 32) { + /* Modify previous length value */ + bits = 64; + } else { + BHND_NV_LOG("invalid length modifier " + "%c\n", *p); + return (EINVAL); + } + break; + + case 'j': + /* Conflicts with all other length + * specifications, and may only occur once */ + if (lenc != '\0') { + BHND_NV_LOG("invalid length modifier " + "%c\n", *p); + return (EINVAL); + } + + lenc = *p; + bits = 64; + break; + + case 'I': { + char *endp; + + /* Conflicts with all other length + * specifications, and may only occur once */ + if (lenc != '\0') { + BHND_NV_LOG("invalid length modifier " + "%c\n", *p); + return (EINVAL); + } + + lenc = *p; + + /* Parse the length specifier value */ + p++; + bits = strtoul(p, &endp, 10); + if (p == endp) { + BHND_NV_LOG("invalid size specifier: " + "%s\n", p); + return (EINVAL); + } + + /* Advance input past the parsed integer */ + np = endp; + break; + } + default: + /* Non-length modifier character */ + stop = true; + break; + } + + if (stop) + break; + else + p = np; + } + + /* Parse conversion specifier and format the value(s) */ + for (u_long n = 0; n < repeat; n++) { + bhnd_nvram_type arg_type; + size_t arg_size; + size_t i; + u_long base; + bool is_signed, is_upper; + + is_signed = false; + is_upper = false; + base = 0; + + /* Fetch next element */ + elem = bhnd_nvram_val_next(value, elem, &elen); + if (elem == NULL) { + BHND_NV_LOG("format string references more " + "than %zu available value elements\n", + bhnd_nvram_val_nelem(value)); + return (EINVAL); + } + + /* + * If this is not the first value, append the delimiter. + */ + if (n > 0) { + size_t nremain = 0; + if (limit > nbytes) + nremain = limit - nbytes; + + if (nremain >= delim_len) + memcpy(outp + nbytes, delim, delim_len); + + /* Add delimiter length to the total byte count */ + if (SIZE_MAX - nbytes < delim_len) + return (EFTYPE); /* overflows size_t */ + + nbytes += delim_len; + } + + /* Parse integer conversion specifiers */ + switch (*p) { + case 'd': + case 'i': + base = 10; + is_signed = true; + break; + + case 'u': + base = 10; + break; + + case 'o': + base = 8; + break; + + case 'x': + base = 16; + break; + + case 'X': + base = 16; + is_upper = true; + break; + } + + /* Format argument */ + switch (*p) { +#define NV_ENCODE_INT(_width) do { \ + arg_type = (is_signed) ? BHND_NVRAM_TYPE_INT ## _width : \ + BHND_NVRAM_TYPE_UINT ## _width; \ + arg_size = sizeof(v.u ## _width); \ + error = bhnd_nvram_val_encode_elem(value, elem, elen, \ + &v.u ## _width, &arg_size, arg_type); \ + if (error) { \ + BHND_NV_LOG("error encoding argument as %s: %d\n", \ + bhnd_nvram_type_name(arg_type), error); \ + return (error); \ + } \ + \ + if (is_signed) { \ + if (v.i ## _width < 0) { \ + add_neg = true; \ + numval = (int64_t)-(v.i ## _width); \ + } else { \ + numval = (int64_t) (v.i ## _width); \ + } \ + } else { \ + numval = v.u ## _width; \ + } \ +} while(0) + case 'd': + case 'i': + case 'u': + case 'o': + case 'x': + case 'X': { + char numbuf[NV_NUMSTR_MAX]; + char *sptr; + uint64_t numval; + size_t slen; + bool add_neg; + union { + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + } v; + + add_neg = false; + + /* If precision is specified, it overrides + * (and behaves identically) to a zero-prefixed + * minimum width */ + if (have_precision) { + padc = '0'; + width = precision; + ladjust = false; + } + + /* If zero-padding is used, value must be right + * adjusted */ + if (padc == '0') + ladjust = false; + + /* Request encode to the appropriate integer + * type, and then promote to common 64-bit + * representation */ + switch (bits) { + case 8: + NV_ENCODE_INT(8); + break; + case 16: + NV_ENCODE_INT(16); + break; + case 32: + NV_ENCODE_INT(32); + break; + case 64: + NV_ENCODE_INT(64); + break; + default: + BHND_NV_LOG("invalid length specifier: " + "%lu\n", bits); + return (EINVAL); + } +#undef NV_ENCODE_INT + + /* If a precision of 0 is specified and the + * value is also zero, no characters should + * be produced */ + if (have_precision && precision == 0 && + numval == 0) + { + break; + } + + /* Emit string representation to local buffer */ + BHND_NV_ASSERT(base <= 16, ("invalid base")); + sptr = numbuf + nitems(numbuf) - 1; + for (slen = 0; slen < sizeof(numbuf); slen++) { + char c; + uint64_t n; + + n = numval % base; + c = bhnd_nv_hex2ascii(n); + if (is_upper) + c = bhnd_nv_toupper(c); + + sptr--; + *sptr = c; + + numval /= (uint64_t)base; + if (numval == 0) { + slen++; + break; + } + } + + arg_size = slen; + + /* Reserve space for 0/0x prefix? */ + if (alt_form) { + if (numval == 0) { + /* If 0, no prefix */ + alt_form = false; + } else if (base == 8) { + arg_size += 1; /* 0 */ + } else if (base == 16) { + arg_size += 2; /* 0x/0X */ + } + } + + /* Reserve space for ' ', '+', or '-' prefix? */ + if (add_neg || signc != '\0') { + if (add_neg) + signc = '-'; + + arg_size++; + } + + /* Right adjust (if using spaces) */ + if (!ladjust && padc != '0') { + for (i = arg_size; i < width; i++) + WRITE_CHAR(padc); + } + + if (signc != '\0') + WRITE_CHAR(signc); + + if (alt_form) { + if (base == 8) { + WRITE_CHAR('0'); + } else if (base == 16) { + WRITE_CHAR('0'); + if (is_upper) + WRITE_CHAR('X'); + else + WRITE_CHAR('x'); + } + } + + /* Right adjust (if using zeros) */ + if (!ladjust && padc == '0') { + for (i = slen; i < width; i++) + WRITE_CHAR(padc); + } + + /* Write the string to our output buffer */ + if (limit > nbytes && limit - nbytes >= slen) + memcpy(outp + nbytes, sptr, slen); + + /* Update the total byte count */ + if (SIZE_MAX - nbytes < arg_size) + return (EFTYPE); /* overflows size_t */ + + nbytes += arg_size; + + /* Left adjust */ + for (i = arg_size; ladjust && i < width; i++) + WRITE_CHAR(padc); + + break; + } + + case 's': { + char *s; + size_t slen; + + /* Query the total length of the element when + * converted to a string */ + arg_type = BHND_NVRAM_TYPE_STRING; + error = bhnd_nvram_val_encode_elem(value, elem, + elen, NULL, &arg_size, arg_type); + if (error) { + BHND_NV_LOG("error encoding argument " + "as %s: %d\n", + bhnd_nvram_type_name(arg_type), + error); + return (error); + } + + /* Do not include trailing NUL in the string + * length */ + if (arg_size > 0) + arg_size--; + + /* Right adjust */ + for (i = arg_size; !ladjust && i < width; i++) + WRITE_CHAR(padc); + + /* Determine output positition and remaining + * buffer space */ + if (limit > nbytes) { + s = outp + nbytes; + slen = limit - nbytes; + } else { + s = NULL; + slen = 0; + } + + /* Encode the string to our output buffer */ + error = bhnd_nvram_val_encode_elem(value, elem, + elen, s, &slen, arg_type); + if (error && error != ENOMEM) { + BHND_NV_LOG("error encoding argument " + "as %s: %d\n", + bhnd_nvram_type_name(arg_type), + error); + return (error); + } + + /* Update the total byte count */ + if (SIZE_MAX - nbytes < arg_size) + return (EFTYPE); /* overflows size_t */ + + nbytes += arg_size; + + /* Left adjust */ + for (i = arg_size; ladjust && i < width; i++) + WRITE_CHAR(padc); + + break; + } + + case 'c': { + char c; + + arg_type = BHND_NVRAM_TYPE_CHAR; + arg_size = bhnd_nvram_value_size(arg_type, NULL, + 0, 1); + + /* Encode as single character */ + error = bhnd_nvram_val_encode_elem(value, elem, + elen, &c, &arg_size, arg_type); + if (error) { + BHND_NV_LOG("error encoding argument " + "as %s: %d\n", + bhnd_nvram_type_name(arg_type), + error); + return (error); + } + + BHND_NV_ASSERT(arg_size == sizeof(c), + ("invalid encoded size")); + + /* Right adjust */ + for (i = arg_size; !ladjust && i < width; i++) + WRITE_CHAR(padc); + + WRITE_CHAR(padc); + + /* Left adjust */ + for (i = arg_size; ladjust && i < width; i++) + WRITE_CHAR(padc); + + break; + } + } + } + } + + /* Append terminating NUL */ + if (limit > nbytes) + *(outp + nbytes) = '\0'; + + if (nbytes < SIZE_MAX) + nbytes++; + else + return (EFTYPE); + + /* Report required space */ + *olen = nbytes; + if (limit < nbytes) { + if (outp != NULL) + return (ENOMEM); + } + + return (0); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_valuevar.h b/sys/dev/bhnd/nvram/bhnd_nvram_valuevar.h new file mode 100644 index 00000000000..b0e098bc714 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_valuevar.h @@ -0,0 +1,100 @@ +/*- + * 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_VALUEVAR_H_ +#define _BHND_NVRAM_BHND_NVRAM_VALUEVAR_H_ + +#include "bhnd_nvram_value.h" + +int bhnd_nvram_val_generic_encode(bhnd_nvram_val_t *value, + void *outp, size_t *olen, bhnd_nvram_type otype); +int bhnd_nvram_val_generic_encode_elem(bhnd_nvram_val_t *value, + const void *inp, size_t ilen, void *outp, size_t *olen, + bhnd_nvram_type otype); +const void *bhnd_nvram_val_generic_next(bhnd_nvram_val_t *value, + const void *prev, size_t *len); +/** + * Filter input data prior to initialization. + * + * This may be used to permit direct initialization from data types other than + * the default native_type defined by @p fmt. + * + * @param[in,out] fmt Indirect pointer to the NVRAM value format. If + * modified by the caller, initialization will be + * restarted and performed using the provided + * format instance. + * @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. + */ +typedef int (bhnd_nvram_val_op_filter)(const bhnd_nvram_val_fmt_t **fmt, + const void *inp, size_t ilen, bhnd_nvram_type itype); + +/** @see bhnd_nvram_val_encode() */ +typedef int (bhnd_nvram_val_op_encode)(bhnd_nvram_val_t *value, void *outp, + size_t *olen, bhnd_nvram_type otype); + +/** @see bhnd_nvram_val_encode_elem() */ +typedef int (bhnd_nvram_val_op_encode_elem)(bhnd_nvram_val_t *value, + const void *inp, size_t ilen, void *outp, size_t *olen, + bhnd_nvram_type otype); + +/** @see bhnd_nvram_val_next() */ +typedef const void *(bhnd_nvram_val_op_next)(bhnd_nvram_val_t *value, + const void *prev, size_t *len); + +/** @see bhnd_nvram_val_nelem() */ +typedef size_t (bhnd_nvram_val_op_nelem)(bhnd_nvram_val_t *value); + +/** + * NVRAM value format. + * + * Provides a set of callbacks to support defining custom parsing + * and encoding/conversion behavior when representing values as + * instances of bhnd_nvram_val. + */ +struct bhnd_nvram_val_fmt { + const char *name; /**< type name */ + bhnd_nvram_type native_type; /**< native value representation */ + + bhnd_nvram_val_op_filter *op_filter; + bhnd_nvram_val_op_encode *op_encode; + bhnd_nvram_val_op_encode_elem *op_encode_elem; + bhnd_nvram_val_op_nelem *op_nelem; + bhnd_nvram_val_op_next *op_next; +}; + +#endif /* _BHND_NVRAM_BHND_NVRAM_VALUEVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_sprom.c b/sys/dev/bhnd/nvram/bhnd_sprom.c index f51e21f590a..d830af9785d 100644 --- a/sys/dev/bhnd/nvram/bhnd_sprom.c +++ b/sys/dev/bhnd/nvram/bhnd_sprom.c @@ -52,15 +52,9 @@ __FBSDID("$FreeBSD$"); #include "bhnd_nvram_if.h" -#include "bhnd_spromvar.h" +#include "bhnd_nvram_io.h" -#define SPROM_LOCK_INIT(sc) \ - mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ - "BHND SPROM lock", MTX_DEF) -#define SPROM_LOCK(sc) mtx_lock(&(sc)->mtx) -#define SPROM_UNLOCK(sc) mtx_unlock(&(sc)->mtx) -#define SPROM_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what) -#define SPROM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx) +#include "bhnd_spromvar.h" /** * Default bhnd sprom driver implementation of DEVICE_PROBE(). @@ -68,9 +62,6 @@ __FBSDID("$FreeBSD$"); int bhnd_sprom_probe(device_t dev) { - /* Quiet by default */ - if (!bootverbose) - device_quiet(dev); device_set_desc(dev, "SPROM/OTP"); /* Refuse wildcard attachments */ @@ -100,32 +91,62 @@ int bhnd_sprom_attach(device_t dev, bus_size_t offset) { struct bhnd_sprom_softc *sc; - int error; - + struct bhnd_nvram_io *io; + struct bhnd_resource *r; + bus_size_t r_size, sprom_size; + int rid; + int error; + sc = device_get_softc(dev); sc->dev = dev; + io = NULL; + /* Allocate SPROM resource */ - sc->sprom_rid = 0; - sc->sprom_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, - &sc->sprom_rid, RF_ACTIVE); - if (sc->sprom_res == NULL) { + rid = 0; + r = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (r == NULL) { device_printf(dev, "failed to allocate resources\n"); return (ENXIO); } - /* Initialize SPROM shadow */ - if ((error = bhnd_sprom_init(&sc->shadow, sc->sprom_res, offset))) + /* Determine SPROM size */ + r_size = rman_get_size(r->res); + if (r_size <= offset || (r_size - offset) > BUS_SPACE_MAXSIZE) { + device_printf(dev, "invalid sprom offset\n"); + error = ENXIO; + goto failed; + } + + sprom_size = r_size - offset; + + /* Allocate an I/O context for the SPROM parser. All SPROM reads + * must be 16-bit aligned */ + io = bhnd_nvram_iores_new(r, offset, sprom_size, sizeof(uint16_t)); + if (io == NULL) { + error = ENXIO; + goto failed; + } + + /* Initialize NVRAM data store */ + error = bhnd_nvram_store_parse_new(&sc->store, io, + &bhnd_nvram_sprom_class); + if (error) goto failed; - /* Initialize mutex */ - SPROM_LOCK_INIT(sc); + /* Clean up our temporary I/O context and its backing resource */ + bhnd_nvram_io_free(io); + bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r); return (0); - + failed: - bhnd_release_resource(dev, SYS_RES_MEMORY, sc->sprom_rid, - sc->sprom_res); + /* Clean up I/O context before releasing its backing resource */ + if (io != NULL) + bhnd_nvram_io_free(io); + + bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r); + return (error); } @@ -157,10 +178,7 @@ bhnd_sprom_detach(device_t dev) sc = device_get_softc(dev); - bhnd_release_resource(dev, SYS_RES_MEMORY, sc->sprom_rid, - sc->sprom_res); - bhnd_sprom_fini(&sc->shadow); - SPROM_LOCK_DESTROY(sc); + bhnd_nvram_store_free(sc->store); return (0); } @@ -172,16 +190,9 @@ static int bhnd_sprom_getvar_method(device_t dev, const char *name, void *buf, size_t *len, bhnd_nvram_type type) { - struct bhnd_sprom_softc *sc; - int error; + struct bhnd_sprom_softc *sc = device_get_softc(dev); - sc = device_get_softc(dev); - - SPROM_LOCK(sc); - error = bhnd_sprom_getvar(&sc->shadow, name, buf, len, type); - SPROM_UNLOCK(sc); - - return (error); + return (bhnd_nvram_store_getvar(sc->store, name, buf, len, type)); } /** @@ -191,16 +202,9 @@ static int bhnd_sprom_setvar_method(device_t dev, const char *name, const void *buf, size_t len, bhnd_nvram_type type) { - struct bhnd_sprom_softc *sc; - int error; + struct bhnd_sprom_softc *sc = device_get_softc(dev); - sc = device_get_softc(dev); - - SPROM_LOCK(sc); - error = bhnd_sprom_setvar(&sc->shadow, name, buf, len, type); - SPROM_UNLOCK(sc); - - return (error); + return (bhnd_nvram_store_setvar(sc->store, name, buf, len, type)); } static device_method_t bhnd_sprom_methods[] = { @@ -218,5 +222,5 @@ static device_method_t bhnd_sprom_methods[] = { DEVMETHOD_END }; -DEFINE_CLASS_0(bhnd_nvram, bhnd_sprom_driver, bhnd_sprom_methods, sizeof(struct bhnd_sprom_softc)); +DEFINE_CLASS_0(bhnd_nvram_store, bhnd_sprom_driver, bhnd_sprom_methods, sizeof(struct bhnd_sprom_softc)); MODULE_VERSION(bhnd_sprom, 1); diff --git a/sys/dev/bhnd/nvram/bhnd_sprom_parser.c b/sys/dev/bhnd/nvram/bhnd_sprom_parser.c deleted file mode 100644 index bf6089a89cd..00000000000 --- a/sys/dev/bhnd/nvram/bhnd_sprom_parser.c +++ /dev/null @@ -1,756 +0,0 @@ -/*- - * 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 -#include -#include -#include -#include - -#include -#include - -#include "bhnd_nvram_common.h" - -#include "bhnd_sprom_parservar.h" - -/* - * BHND SPROM Parser - * - * Provides identification, decoding, and encoding of BHND SPROM data. - */ - -static int sprom_direct_read(struct bhnd_sprom *sc, size_t offset, - void *buf, size_t nbytes, uint8_t *crc); -static int sprom_extend_shadow(struct bhnd_sprom *sc, - size_t image_size, uint8_t *crc); -static int sprom_populate_shadow(struct bhnd_sprom *sc); - -static int sprom_get_var_defn(struct bhnd_sprom *sc, - const char *name, - const struct bhnd_nvram_vardefn **var, - const struct bhnd_sprom_vardefn **sprom, - size_t *size, size_t *nelem, - bhnd_nvram_type req_type); - -static char sprom_get_delim_char(struct bhnd_sprom *sc, - bhnd_nvram_sfmt sfmt); - -/* SPROM revision is always located at the second-to-last byte */ -#define SPROM_REV(_sc) SPROM_READ_1((_sc), (_sc)->sp_size - 2) - -/* SPROM CRC is always located at the last byte */ -#define SPROM_CRC_OFF(_sc) SPROM_CRC_LEN(_sc) - -/* SPROM CRC covers all but the final CRC byte */ -#define SPROM_CRC_LEN(_sc) ((_sc)->sp_size - 1) - -/* SPROM shadow I/O (with byte-order translation) */ -#define SPROM_READ_1(_sc, _off) SPROM_READ_ENC_1(_sc, _off) -#define SPROM_READ_2(_sc, _off) le16toh(SPROM_READ_ENC_2(_sc, _off)) -#define SPROM_READ_4(_sc, _off) le32toh(SPROM_READ_ENC_4(_sc, _off)) - -#define SPROM_WRITE_1(_sc, _off, _v) SPROM_WRITE_ENC_1(_sc, _off, (_v)) -#define SPROM_WRITE_2(_sc, _off, _v) SPROM_WRITE_ENC_2(_sc, _off, \ - htole16(_v)) -#define SPROM_WRITE_4(_sc, _off, _v) SPROM_WRITE_ENC_4(_sc, _off, \ - htole32(_v)) - -/* SPROM shadow I/O (without byte-order translation) */ -#define SPROM_READ_ENC_1(_sc, _off) (*(uint8_t *)((_sc)->sp_shadow + _off)) -#define SPROM_READ_ENC_2(_sc, _off) (*(uint16_t *)((_sc)->sp_shadow + _off)) -#define SPROM_READ_ENC_4(_sc, _off) (*(uint32_t *)((_sc)->sp_shadow + _off)) - -#define SPROM_WRITE_ENC_1(_sc, _off, _v) \ - *((uint8_t *)((_sc)->sp_shadow + _off)) = (_v) -#define SPROM_WRITE_ENC_2(_sc, _off, _v) \ - *((uint16_t *)((_sc)->sp_shadow + _off)) = (_v) -#define SPROM_WRITE_ENC_4(_sc, _off, _v) \ - *((uint32_t *)((_sc)->sp_shadow + _off)) = (_v) - -/* Call @p _next macro with the C type, widened (signed or unsigned) 32-bit C - * type, width, and min/max values associated with @p _dtype */ -#define SPROM_SWITCH_TYPE(_dtype, _next, ...) \ -do { \ - switch (_dtype) { \ - case BHND_NVRAM_TYPE_UINT8: \ - _next (uint8_t, uint32_t, 1, 0, \ - UINT8_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_UINT16: \ - _next (uint16_t, uint32_t, 2, 0, \ - UINT16_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_UINT32: \ - _next (uint32_t, uint32_t, 4, 0, \ - UINT32_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_INT8: \ - _next (int8_t, int32_t, 1, \ - INT8_MIN, INT8_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_INT16: \ - _next (int16_t, int32_t, 2, \ - INT16_MIN, INT16_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_INT32: \ - _next (int32_t, int32_t, 4, \ - INT32_MIN, INT32_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_CHAR: \ - _next (char, int32_t, 1, \ - CHAR_MIN, CHAR_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_CSTR: \ - panic("%s: BHND_NVRAM_TYPE_CSTR unhandled", \ - __FUNCTION__); \ - break; \ - } \ -} while (0) - - -/* Verify the range of _val of (_stype) within _type */ -#define SPROM_VERIFY_RANGE(_type, _widen, _width, _min, _max, _val, \ - _stype) \ -do { \ - if (BHND_NVRAM_SIGNED_TYPE(_stype)) { \ - int32_t sval = (int32_t) (_val); \ - if (sval > (_max) || sval < (_min)) \ - return (ERANGE); \ - } else { \ - if ((_val) > (_max)) \ - return (ERANGE); \ - } \ -} while(0) - -/* - * Table of supported SPROM image formats, sorted by image size, ascending. - */ -#define SPROM_FMT(_sz, _revmin, _revmax, _sig) \ - { SPROM_SZ_ ## _sz, _revmin, _revmax, \ - SPROM_SIG_ ## _sig ## _OFF, \ - SPROM_SIG_ ## _sig } - -static const struct sprom_fmt { - size_t size; - uint8_t rev_min; - uint8_t rev_max; - size_t sig_offset; - uint16_t sig_req; -} sprom_fmts[] = { - SPROM_FMT(R1_3, 1, 3, NONE), - SPROM_FMT(R4_8_9, 4, 4, R4), - SPROM_FMT(R4_8_9, 8, 9, R8_9), - SPROM_FMT(R10, 10, 10, R10), - SPROM_FMT(R11, 11, 11, R11) -}; - -/** - * Identify the SPROM format at @p offset within @p r, verify the CRC, - * and allocate a local shadow copy of the SPROM data. - * - * After successful initialization, @p r will not be accessed; any pin - * configuration required for SPROM access may be reset. - * - * @param[out] sprom On success, will be initialized with shadow of the SPROM - * data. - * @param r An active resource mapping the SPROM data. - * @param offset Offset of the SPROM data within @p resource. - */ -int -bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r, - bus_size_t offset) -{ - bus_size_t res_size; - int error; - - sprom->dev = rman_get_device(r->res); - sprom->sp_res = r; - sprom->sp_res_off = offset; - - /* Determine maximum possible SPROM image size */ - res_size = rman_get_size(r->res); - if (offset >= res_size) - return (EINVAL); - - sprom->sp_size_max = MIN(res_size - offset, SPROM_SZ_MAX); - - /* Allocate and populate SPROM shadow */ - sprom->sp_size = 0; - sprom->sp_capacity = sprom->sp_size_max; - sprom->sp_shadow = malloc(sprom->sp_capacity, M_BHND_NVRAM, M_NOWAIT); - if (sprom->sp_shadow == NULL) - return (ENOMEM); - - /* Read and identify SPROM image */ - if ((error = sprom_populate_shadow(sprom))) - return (error); - - return (0); -} - -/** - * Release all resources held by @p sprom. - * - * @param sprom A SPROM instance previously initialized via bhnd_sprom_init(). - */ -void -bhnd_sprom_fini(struct bhnd_sprom *sprom) -{ - free(sprom->sp_shadow, M_BHND_NVRAM); -} - -/* Perform a read using a SPROM offset descriptor, safely widening the result - * to its 32-bit representation before assigning it to @p _dest. */ -#define SPROM_GETVAR_READ(_type, _widen, _width, _min, _max, _sc, _off, \ - _dest) \ -do { \ - _type _v = (_type)SPROM_READ_ ## _width(_sc, _off->offset); \ - if (_off->shift > 0) { \ - _v >>= _off->shift; \ - } else if (off->shift < 0) { \ - _v <<= -_off->shift; \ - } \ - \ - if (_off->cont) \ - _dest |= ((uint32_t) (_widen) _v) & _off->mask; \ - else \ - _dest = ((uint32_t) (_widen) _v) & _off->mask; \ -} while(0) - -/* Emit a value read using a SPROM offset descriptor, narrowing the - * result output representation. */ -#define SPROM_GETVAR_WRITE(_type, _widen, _width, _min, _max, _off, \ - _src, _buf) \ -do { \ - _type _v = (_type) (_widen) _src; \ - *((_type *)_buf) = _v; \ -} while(0) - -/* String format a value read using a SPROM offset descriptor */ -#define SPROM_GETVAR_SNPRINTF(_type, _widen, _width, _min, _max, _src, \ - _buf, _remain, _fmt, _nwrite) \ -do { \ - _nwrite = snprintf(_buf, _remain, _fmt, (_type) (_widen) _src); \ -} while(0) - -/** - * Read a SPROM variable, performing conversion to host byte order. - * - * @param sc The SPROM parser state. - * @param name The SPROM 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_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf, - size_t *len, bhnd_nvram_type type) -{ - const struct bhnd_nvram_vardefn *nv; - const struct bhnd_sprom_vardefn *sv; - void *outp; - size_t all1_offs; - size_t req_size, nelem; - size_t str_remain; - char str_delim; - uint32_t val; - int error; - - error = sprom_get_var_defn(sc, name, &nv, &sv, &req_size, &nelem, type); - if (error) - return (error); - - outp = buf; - str_remain = 0; - str_delim = '\0'; - - - if (type != BHND_NVRAM_TYPE_CSTR) { - /* Provide required size */ - if (outp == NULL) { - *len = req_size; - return (0); - } - - /* Check (and update) target buffer len */ - if (*len < req_size) - return (ENOMEM); - else - *len = req_size; - } else { - /* String length calculation requires performing - * the actual string formatting */ - KASSERT(req_size == 0, - ("req_size set for variable-length type")); - - /* If caller is querying length, the len argument - * may be uninitialized */ - if (outp != NULL) - str_remain = *len; - - /* Fetch delimiter for the variable's string format */ - str_delim = sprom_get_delim_char(sc, nv->sfmt); - } - - /* Read data */ - all1_offs = 0; - val = 0; - for (size_t i = 0; i < sv->num_offsets; i++) { - const struct bhnd_sprom_offset *off; - - off = &sv->offsets[i]; - KASSERT(!off->cont || i > 0, ("cont marked on first offset")); - - /* If not a continuation, advance the output buffer; if - * a C string, this requires appending a delimiter character */ - if (i > 0 && !off->cont) { - size_t width = bhnd_nvram_type_width(type); - - /* Non-fixed width types (such as CSTR) will have a 0 - * width value */ - if (width != 0) { - KASSERT(outp != NULL, ("NULL output buffer")); - outp = ((uint8_t *)outp) + width; - } - - /* Append CSTR delim, if necessary */ - if (type == BHND_NVRAM_TYPE_CSTR && - str_delim != '\0' && - i != 0) - { - if (outp != NULL && str_remain >= 1) { - *((char *)outp) = str_delim; - outp = ((char *)outp + 1); - - /* Drop outp reference if we hit 0 */ - if (str_remain-- == 0) - outp = NULL; - } - - if (SIZE_MAX - 1 < req_size) - return (EFTYPE); /* too long */ - req_size++; - } - } - - /* Read the value, widening to a common uint32 - * representation */ - SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_READ, sc, off, val); - - /* If IGNALL1, record whether value has all bits set. */ - if (nv->flags & BHND_NVRAM_VF_IGNALL1) { - uint32_t all1; - - all1 = off->mask; - if (off->shift > 0) - all1 >>= off->shift; - else if (off->shift < 0) - all1 <<= -off->shift; - - if ((val & all1) == all1) - all1_offs++; - } - - /* Skip writing if additional continuations remain */ - if (i+1 < sv->num_offsets && sv->offsets[i].cont) - continue; - - /* Perform write */ - if (type == BHND_NVRAM_TYPE_CSTR) { - const char *fmtstr; - int written; - - fmtstr = bhnd_nvram_type_fmt(off->type, nv->sfmt, i); - if (fmtstr == NULL) { - device_printf(sc->dev, "no NVRAM format string " - "for '%s' (type=%d)\n", name, off->type); - return (EOPNOTSUPP); - } - - SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_SNPRINTF, val, - outp, str_remain, fmtstr, written); - - if (written <= 0) - return (EFTYPE); - - /* Calculate remaining capacity, drop outp reference - * if we hit 0 -- otherwise, advance the buffer - * position */ - if (written >= str_remain) { - str_remain = 0; - outp = NULL; - } else { - str_remain -= written; - if (outp != NULL) - outp = (char *)outp + written; - } - - /* Add additional bytes to total length */ - if (SIZE_MAX - written < req_size) - return (EFTYPE); /* string too long */ - req_size += written; - } else { - /* Verify range */ - SPROM_SWITCH_TYPE(type, SPROM_VERIFY_RANGE, val, - off->type); - - /* Write the value, narrowing to the appropriate output - * width. */ - SPROM_SWITCH_TYPE(type, SPROM_GETVAR_WRITE, off, val, - outp); - } - } - - /* Should value should be treated as uninitialized? */ - if (nv->flags & BHND_NVRAM_VF_IGNALL1 && all1_offs == sv->num_offsets) - return (ENOENT); - - /* If this is a C string request, we need to provide the computed - * length. */ - if (type == BHND_NVRAM_TYPE_CSTR) { - /* Account for final trailing NUL */ - if (SIZE_MAX - 1 < req_size) - return (EFTYPE); /* string too long */ - req_size++; - - /* Return an error if a too-small output buffer was provided */ - if (buf != NULL && *len < req_size) { - *len = req_size; - return (ENOMEM); - } - - *len = req_size; - } - - return (0); -} - -/* Perform a read of a variable offset from _src, safely widening the result - * to its 32-bit representation before assigning it to @p _dest. */ -#define SPROM_SETVAR_READ(_type, _widen, _width, _min, _max, _off, \ - _src, _dest) \ -do { \ - _type _v = *(const _type *)_src; \ - if (_off->shift > 0) { \ - _v <<= _off->shift; \ - } else if (off->shift < 0) { \ - _v >>= -_off->shift; \ - } \ - _dest = ((uint32_t) (_widen) _v) & _off->mask; \ -} while(0) - - -/* Emit a value read using a SPROM offset descriptor, narrowing the - * result output representation and, if necessary, OR'ing it with the - * previously read value from @p _buf. */ -#define SPROM_SETVAR_WRITE(_type, _widen, _width, _min, _max, _sc, \ - _off, _src) \ -do { \ - _type _v = (_type) (_widen) _src; \ - if (_off->cont) \ - _v |= SPROM_READ_ ## _width(_sc, _off->offset); \ - SPROM_WRITE_ ## _width(_sc, _off->offset, _v); \ -} while(0) - -/** - * Set a local value for a SPROM variable, performing conversion to SPROM byte - * order. - * - * The new value will be written to the backing SPROM shadow. - * - * @param sc The SPROM parser state. - * @param name The SPROM 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_sprom_setvar(struct bhnd_sprom *sc, const char *name, const void *buf, - size_t len, bhnd_nvram_type type) -{ - const struct bhnd_nvram_vardefn *nv; - const struct bhnd_sprom_vardefn *sv; - size_t req_size, nelem; - int error; - uint8_t crc; - - error = sprom_get_var_defn(sc, name, &nv, &sv, &req_size, &nelem, type); - if (error) - return (error); - - /* String parsing is currently unsupported */ - if (type == BHND_NVRAM_TYPE_CSTR) - return (EOPNOTSUPP); - - /* Provide required size */ - if (len != req_size) - return (EINVAL); - - /* Write data */ - for (size_t i = 0; i < sv->num_offsets; i++) { - const struct bhnd_sprom_offset *off; - uint32_t val; - - off = &sv->offsets[i]; - KASSERT(!off->cont || i > 0, ("cont marked on first offset")); - - /* If not a continuation, advance the input pointer */ - if (i > 0 && !off->cont) { - buf = ((const uint8_t *)buf) + - bhnd_nvram_type_width(sv->offsets[i-1].type); - } - - /* Read the value, widening to a common uint32 - * representation */ - SPROM_SWITCH_TYPE(nv->type, SPROM_SETVAR_READ, off, buf, val); - - /* Verify range */ - SPROM_SWITCH_TYPE(nv->type, SPROM_VERIFY_RANGE, val, type); - - /* Write the value, narrowing to the appropriate output - * width. */ - SPROM_SWITCH_TYPE(off->type, SPROM_SETVAR_WRITE, sc, off, val); - } - - /* Update CRC */ - crc = ~bhnd_nvram_crc8(sc->sp_shadow, SPROM_CRC_LEN(sc), - BHND_NVRAM_CRC8_INITIAL); - SPROM_WRITE_1(sc, SPROM_CRC_OFF(sc), crc); - - return (0); -} - -/* Read and identify the SPROM image by incrementally performing - * read + CRC of all supported image formats */ -static int -sprom_populate_shadow(struct bhnd_sprom *sc) -{ - const struct sprom_fmt *fmt; - int error; - uint16_t sig; - uint8_t srom_rev; - uint8_t crc; - - crc = BHND_NVRAM_CRC8_INITIAL; - - /* Identify the SPROM revision (and populate the SPROM shadow) */ - for (size_t i = 0; i < nitems(sprom_fmts); i++) { - fmt = &sprom_fmts[i]; - - /* Read image data and check CRC */ - if ((error = sprom_extend_shadow(sc, fmt->size, &crc))) - return (error); - - /* Skip on invalid CRC */ - if (crc != BHND_NVRAM_CRC8_VALID) - continue; - - /* Fetch SROM revision */ - srom_rev = SPROM_REV(sc); - - /* Early sromrev 1 devices (specifically some BCM440x enet - * cards) are reported to have been incorrectly programmed - * with a revision of 0x10. */ - if (fmt->size == SPROM_SZ_R1_3 && srom_rev == 0x10) - srom_rev = 0x1; - - /* Verify revision range */ - if (srom_rev < fmt->rev_min || srom_rev > fmt->rev_max) - continue; - - /* Verify signature (if any) */ - sig = SPROM_SIG_NONE; - if (fmt->sig_offset != SPROM_SIG_NONE_OFF) - sig = SPROM_READ_2(sc, fmt->sig_offset); - - if (sig != fmt->sig_req) { - device_printf(sc->dev, - "invalid sprom %hhu signature: 0x%hx " - "(expected 0x%hx)\n", - srom_rev, sig, fmt->sig_req); - return (EINVAL); - } - - /* Identified */ - sc->sp_rev = srom_rev; - return (0); - } - - /* identification failed */ - device_printf(sc->dev, "unrecognized SPROM format\n"); - return (EINVAL); -} - -/* - * Extend the shadowed SPROM buffer to image_size, reading any required - * data from the backing SPROM resource and updating the CRC. - */ -static int -sprom_extend_shadow(struct bhnd_sprom *sc, size_t image_size, - uint8_t *crc) -{ - int error; - - KASSERT(image_size >= sc->sp_size, (("shadow truncation unsupported"))); - - /* Verify the request fits within our shadow buffer */ - if (image_size > sc->sp_capacity) - return (ENOSPC); - - /* Skip no-op requests */ - if (sc->sp_size == image_size) - return (0); - - /* Populate the extended range */ - error = sprom_direct_read(sc, sc->sp_size, sc->sp_shadow + sc->sp_size, - image_size - sc->sp_size, crc); - if (error) - return (error); - - sc->sp_size = image_size; - return (0); -} - -/** - * Read nbytes at the given offset from the backing SPROM resource, and - * update the CRC. - */ -static int -sprom_direct_read(struct bhnd_sprom *sc, size_t offset, void *buf, - size_t nbytes, uint8_t *crc) -{ - bus_size_t res_offset; - uint16_t *p; - - KASSERT(nbytes % sizeof(uint16_t) == 0, ("unaligned sprom size")); - KASSERT(offset % sizeof(uint16_t) == 0, ("unaligned sprom offset")); - - /* Check for read overrun */ - if (offset >= sc->sp_size_max || sc->sp_size_max - offset < nbytes) { - device_printf(sc->dev, "requested SPROM read would overrun\n"); - return (EINVAL); - } - - /* Perform read and update CRC */ - p = (uint16_t *)buf; - res_offset = sc->sp_res_off + offset; - - bhnd_bus_read_region_stream_2(sc->sp_res, res_offset, p, - (nbytes / sizeof(uint16_t))); - *crc = bhnd_nvram_crc8(p, nbytes, *crc); - - return (0); -} - - -/** - * Locate the variable and SPROM revision-specific definitions - * for variable with @p name. - */ -static int -sprom_get_var_defn(struct bhnd_sprom *sc, const char *name, - const struct bhnd_nvram_vardefn **var, - const struct bhnd_sprom_vardefn **sprom, - size_t *size, size_t *nelem, bhnd_nvram_type req_type) -{ - /* Find variable definition */ - *var = bhnd_nvram_find_vardefn(name); - if (*var == NULL) - return (ENOENT); - - /* Find revision-specific SPROM definition */ - for (size_t i = 0; i < (*var)->num_sp_defs; i++) { - const struct bhnd_sprom_vardefn *sp = &(*var)->sp_defs[i]; - - if (sc->sp_rev < sp->compat.first) - continue; - - if (sc->sp_rev > sp->compat.last) - continue; - - /* Found */ - *sprom = sp; - - /* Calculate element count and total size, in bytes */ - *nelem = 0; - for (size_t j = 0; j < sp->num_offsets; j++) - if (!sp->offsets[j].cont) - *nelem += 1; - - *size = bhnd_nvram_type_width(req_type) * (*nelem); - return (0); - } - - /* Not supported by this SPROM revision */ - return (ENOENT); -} - -/** - * Return the array element delimiter for @p sfmt, or '\0' if none. - */ -static char -sprom_get_delim_char(struct bhnd_sprom *sc, bhnd_nvram_sfmt sfmt) -{ - switch (sfmt) { - case BHND_NVRAM_SFMT_HEX: - case BHND_NVRAM_SFMT_DEC: - return (','); - - case BHND_NVRAM_SFMT_CCODE: - case BHND_NVRAM_SFMT_LEDDC: - return ('\0'); - - case BHND_NVRAM_SFMT_MACADDR: - return (':'); - - default: - device_printf(sc->dev, "unknown NVRAM string format: %d\n", - sfmt); - return (','); - } -} diff --git a/sys/dev/bhnd/nvram/bhnd_spromvar.h b/sys/dev/bhnd/nvram/bhnd_spromvar.h index df43c4c6106..f3a9bc3c4dc 100644 --- a/sys/dev/bhnd/nvram/bhnd_spromvar.h +++ b/sys/dev/bhnd/nvram/bhnd_spromvar.h @@ -34,7 +34,7 @@ #include -#include "bhnd_sprom_parser.h" +#include "bhnd_nvram_store.h" DECLARE_CLASS(bhnd_sprom_driver); @@ -49,11 +49,8 @@ int bhnd_sprom_detach(device_t dev); * softc structures. */ struct bhnd_sprom_softc { - device_t dev; - struct bhnd_resource *sprom_res; /**< SPROM resource */ - int sprom_rid; /**< SPROM RID */ - struct bhnd_sprom shadow; /**< SPROM shadow */ - struct mtx mtx; /**< SPROM shadow mutex */ + device_t dev; + struct bhnd_nvram_store *store; /**< nvram store */ }; #endif /* _BHND_NVRAM_BHND_SPROMVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/nvram_map b/sys/dev/bhnd/nvram/nvram_map index f38573500ab..33bbbb94e8a 100644 --- a/sys/dev/bhnd/nvram/nvram_map +++ b/sys/dev/bhnd/nvram/nvram_map @@ -21,1445 +21,3054 @@ # $FreeBSD$ # -# NVRAM variable definitions and revision-specific SPROM offsets. +# NVRAM variable and SPROM layout descriptions. # -# Processed by nvram_map_gen.awk to produce bhnd_nvram_map.h +# Process with nvram_map_gen.awk to produce bhnd_nvram_map.h and +# bhnd_nvram_map_data.h # # NOTE: file was originally generated automatically by using libclang # to analyze and extract format information and descriptions from Broadcom's # available ISC-licensed CIS and SROM code and associated headers. # -# Board Info -# - -u16 boardvendor {} # PCI vendor ID (SoC NVRAM-only) -u16 subvid { srom >= 2 0x6 } # PCI subvendor ID -u16 devid { srom >= 8 0x60 } # PCI device ID - -u32 boardflags { - srom 1 u16 0x72 - srom 2 u16 0x72 | u16 0x38 (<<16) - srom 3 u16 0x72 | u16 0x7A (<<16) - srom 4 0x44 - srom 5-7 0x4A - srom >= 8 0x84 -} -u32 boardflags2 { - srom 4 0x48 - srom 5-7 0x4E - srom >= 8 0x88 -} -u32 boardflags3 { - srom >= 11 0x8C -} - -# Board serial number, independent of mac addr -u16 boardnum { - srom 1-2 0x4C - srom 3 0x4E - srom 4 0x50 - srom 5-7 0x56 - srom 8-10 0x90 - srom >= 11 0x94 -} - -# Board revision -u16 boardrev { - srom 1-3 u8 0x5D - srom 4-7 0x42 - srom >= 8 0x82 -} - -# Board type -u16 boardtype { - srom >= 2 0x4 -} - -# SROM revision -u8 sromrev { - srom 1-3 0x74 - srom 4-9 0x1B6 - srom 10 0x1CA - srom 11 0x1D2 -} - - -# PMU Info -# - -# PMU min resource mask (embedded-only). -u32 rmin { - sfmt decimal -} - -# PMU min resource max (embedded-only). -u32 rmax { - sfmt decimal -} - - -# Antennas available -u8 aa2g { - srom 1-3 0x5C (&0x30, >>4) - srom 4-7 0x5D - srom 8-10 0x9D - srom >= 11 0xA1 -} -u8 aa5g { - srom 1-3 0x5C (&0xC0, >>6) - srom 4-7 0x5C - srom 8-10 0x9C - srom >= 11 0xA0 -} - -# ACPHY PA trimming parameters: 40 -u16[12] pa5gbw40a0 { - srom >= 11 0x110 -} - -# ACPHY PA trimming parameters: 80 -u16[12] pa5gbw80a0 { - srom >= 11 0x138 -} - -# ACPHY PA trimming parameters: 40/80 -u16[12] pa5gbw4080a0 { - srom >= 11 0x138 -} -u16[12] pa5gbw4080a1 { - srom >= 11 u16 0xB6, u16 0xBC, u16 0xCE, u16 0xD4, u16[8] 0x128 -} - -# ACPHY PA trimming parameters: CCK -u16[3] pa2gccka0 { - srom >= 11 0x102 -} - -# ACPHY Power-per-rate 2gpo -u16 dot11agofdmhrbw202gpo { - srom >= 11 0x15C -} -u16 ofdmlrbw202gpo { - srom >= 11 0x15E -} - -# ACPHY Power-per-rate 5gpo -u32 mcsbw805glpo { - srom >= 11 0x168 -} -u32 mcsbw805gmpo { - srom >= 11 0x178 -} -u32 mcsbw805ghpo { - srom >= 11 0x188 -} -u16 mcslr5glpo { - srom >= 11 0x190 (&0xFFF) -} -u16 mcslr5gmpo { - srom >= 11 0x192 -} -u16 mcslr5ghpo { - srom >= 11 0x194 -} - -# ACPHY Power-per-rate sbpo -u16 sb20in40hrpo { - srom >= 11 0x196 -} -u16 sb20in80and160hr5glpo { - srom >= 11 0x198 -} -u16 sb40and80hr5glpo { - srom >= 11 0x19A -} -u16 sb20in80and160hr5gmpo { - srom >= 11 0x19C -} -u16 sb40and80hr5gmpo { - srom >= 11 0x19E -} -u16 sb20in80and160hr5ghpo { - srom >= 11 0x1A0 -} -u16 sb40and80hr5ghpo { - srom >= 11 0x1A2 -} -u16 sb20in40lrpo { - srom >= 11 0x1A4 -} -u16 sb20in80and160lr5glpo { - srom >= 11 0x1A6 -} -u16 sb40and80lr5glpo { - srom >= 11 0x1A8 -} -u16 sb20in80and160lr5gmpo { - srom >= 11 0x1AA -} -u16 sb40and80lr5gmpo { - srom >= 11 0x1AC -} -u16 sb20in80and160lr5ghpo { - srom >= 11 0x1AE -} -u16 sb40and80lr5ghpo { - srom >= 11 0x1B0 -} -u16 dot11agduphrpo { - srom >= 11 0x1B2 -} -u16 dot11agduplrpo { - srom >= 11 0x1B4 -} - -# Antenna gain -u8 ag0 { - srom 1-3 0x75 - srom 4-7 0x5F - srom 8-10 0x9F -} -u8 ag1 { - srom 1-3 0x74 - srom 4-7 0x5E - srom 8-10 0x9E -} -u8 ag2 { - srom 4-7 0x61 - srom 8-10 0xA1 -} -u8 ag3 { - srom 4-7 0x60 - srom 8-10 0xA0 -} - -u8 agbg0 { - srom >= 11 0xA2 -} -u8 agbg1 { - srom >= 11 0xA3 -} -u8 agbg2 { - srom >= 11 0xA4 -} -u8 aga0 { - srom >= 11 0xA5 -} -u8 aga1 { - srom >= 11 0xA6 -} -u8 aga2 { - srom >= 11 0xA7 -} - -# Default country code (sromrev == 1) -u8 cc { - srom 1 0x5C (&0xF) -} - -# 2 bytes each -# CCK Power offsets for 20 MHz rates (11, 5.5, 2, 1Mbps) -# cckbw202gpo cckbw20ul2gpo -# -u16 cckbw202gpo { - srom 9-10 0x140 - srom >= 11 0x150 -} -u16 cckbw20ul2gpo { - srom 9-10 0x142 - srom >= 11 0x152 -} - -# Country code (2 bytes ascii + 1 byte cctl) -# in rev 2 -# -char[2] ccode { - sfmt ccode - srom 0-3 0x76 - srom 4 0x52 - srom 5-7 0x44 - srom 8-10 0x92 - srom >= 11 0x96 -} - -# 2 byte; txchain, rxchain -u8 txchain { - all1 ignore - srom 4-7 0x7B (&0xF) - srom 8-10 0xA3 (&0xF) - srom >= 11 0xA9 (&0xF) -} -u8 rxchain { - all1 ignore - srom 4-7 0x7B (&0xF0, >>4) - srom 8-10 0xA3 (&0xF0, >>4) - srom >= 11 0xA9 (&0xF0, >>4) -} -u16 antswitch { - all1 ignore - srom 4-7 u8 0x7A - srom 8-10 u8 0xA2 - srom >= 11 u8 0xA8 -} - -u8 elna2g { - srom 8-10 0xBB -} - -u8 elna5g { - srom 8-10 0xBA -} - -# 11n front-end specification -u8 antswctl2g { - srom 8-10 0xAE (&0xF8, >>3) -} -u8 triso2g { - srom 8-10 0xAE (&0x7) -} -u8 pdetrange2g { - srom 8-10 0xAF (&0xF8, >>3) -} -u8 extpagain2g { - srom 8-10 0xAF (&0x6, >>1) -} -u8 tssipos2g { - srom 8-10 0xAF (&0x1) -} -u8 antswctl5g { - srom 8-10 0xB0 (&0xF8, >>3) -} -u8 triso5g { - srom 8-10 0xB0 (&0x7) -} -u8 pdetrange5g { - srom 8-10 0xB1 (&0xF8, >>3) -} -u8 extpagain5g { - srom 8-10 0xB1 (&0x6, >>1) -} -u8 tssipos5g { - srom 8-10 0xB1 (&0x1) -} - -# FEM config -u8 femctrl { - sfmt decimal - srom >= 11 0xAA (&0xF8, >>3) -} -u8 papdcap2g { - sfmt decimal - srom >= 11 0xAA (&0x4, >>2) -} -u8 tworangetssi2g { - sfmt decimal - srom >= 11 0xAA (&0x2, >>1) -} -u8 pdgain2g { - sfmt decimal - srom >= 11 u16 0xAA (&0x1F0, >>4) -} -u8 epagain2g { - sfmt decimal - srom >= 11 0xAB (&0xE, >>1) -} -u8 tssiposslope2g { - sfmt decimal - srom >= 11 0xAB (&0x1) -} -u8 gainctrlsph { - sfmt decimal - srom >= 11 0xAC (&0xF8, >>3) -} -u8 papdcap5g { - sfmt decimal - srom >= 11 0xAC (&0x4, >>2) -} -u8 tworangetssi5g { - sfmt decimal - srom >= 11 0xAC (&0x2, >>1) -} -u8 pdgain5g { - sfmt decimal - srom >= 11 u16 0xAC (&0x1F0, >>4) -} -u8 epagain5g { - sfmt decimal - srom >= 11 0xAD (&0xE, >>1) -} -u8 tssiposslope5g { - sfmt decimal - srom >= 11 0xAD (&0x1) -} - -# LED duty cycle -u8[2] leddc { - sfmt led_dc - all1 ignore - srom 3 0x7C - srom 4 0x5A - srom 5-7 0x5A - srom 8-10 0x9A - srom >= 11 0x9E -} - -# LED set -u8 ledbh0 { - all1 ignore - srom 1-3 0x65 - srom 4 0x57 - srom 5-7 0x77 - srom 8-10 0x97 - srom >= 11 0x9B -} -u8 ledbh1 { - all1 ignore - srom 1-3 0x64 - srom 4 0x56 - srom 5-7 0x76 - srom 8-10 0x96 - srom >= 11 0x9A -} -u8 ledbh2 { - all1 ignore - srom 1-3 0x67 - srom 4 0x59 - srom 5-7 0x79 - srom 8-10 0x99 - srom >= 11 0x9D -} -u8 ledbh3 { - all1 ignore - srom 1-3 0x66 - srom 4 0x58 - srom 5-7 0x78 - srom 8-10 0x98 - srom >= 11 0x9C -} - -# 2 bytes total -# Additional power offset for Legacy Dup40 transmissions. -# Applied in addition to legofdmbw20ulXpo, X=2g, 5gl, 5gm, or 5gh. -# LSB nibble: 2G band, MSB nibble: 5G band high subband. -# leg40dup5ghpo, leg40dup5gmpo, leg40dup5glpo, leg40dup2gpo -# -u16 legofdm40duppo { - srom 9-10 0x196 -} - -# 4 bytes each -# OFDM power offsets for 20 MHz Legacy rates -# (54, 48, 36, 24, 18, 12, 9, 6 Mbps) -# legofdmbw202gpo legofdmbw20ul2gpo -# -u32 legofdmbw202gpo { - srom 9-10 0x144 -} -u32 legofdmbw20ul2gpo { - srom 9-10 0x148 -} - -# 4 bytes each -# 5G band: OFDM power offsets for 20 MHz Legacy rates -# (54, 48, 36, 24, 18, 12, 9, 6 Mbps) -# low subband : legofdmbw205glpo legofdmbw20ul2glpo -# mid subband :legofdmbw205gmpo legofdmbw20ul2gmpo -# high subband :legofdmbw205ghpo legofdmbw20ul2ghpo -# -u32 legofdmbw205glpo { - srom 9-10 0x14C -} -u32 legofdmbw20ul5glpo { - srom 9-10 0x150 -} -u32 legofdmbw205gmpo { - srom 9-10 0x154 -} -u32 legofdmbw20ul5gmpo { - srom 9-10 0x158 -} -u32 legofdmbw205ghpo { - srom 9-10 0x15C -} -u32 legofdmbw20ul5ghpo { - srom 9-10 0x160 -} - -# mac addr override for the standard CIS LAN_NID -u8[6] macaddr { - sfmt macaddr - srom 3 u8 0x4B, u8 0x4A, u8 0x4D, u8 0x4C, u8 0x4F, u8 0x4E - srom 4 u8 0x4D, u8 0x4C, u8 0x4F, u8 0x4E, u8 0x51, u8 0x50 - srom 5-7 u8 0x53, u8 0x52, u8 0x55, u8 0x54, u8 0x57, u8 0x56 - srom 8-10 u8 0x8D, u8 0x8C, u8 0x8F, u8 0x8E, u8 0x91, u8 0x90 - srom >= 11 u8 0x91, u8 0x90, u8 0x93, u8 0x92, u8 0x95, u8 0x94 -} - -# 4 bytes each -# mcs 0-7 power-offset. LSB nibble: m0, MSB nibble: m7 -# mcsbw202gpo mcsbw20ul2gpo mcsbw402gpo -# -u32 mcsbw202gpo { - srom 9-10 0x164 - srom >= 11 0x154 -} -u32 mcsbw20ul2gpo { - srom 9-10 0x168 -} -u32 mcsbw402gpo { - srom 9-10 0x16C - srom >= 11 0x158 -} - -# 4 bytes each -# 5G high subband mcs 0-7 power-offset. -# LSB nibble: m0, MSB nibble: m7 -# mcsbw205ghpo mcsbw20ul5ghpo mcsbw405ghpo -# -u32 mcsbw205ghpo { - srom 9-10 0x188 - srom >= 11 0x180 -} -u32 mcsbw20ul5ghpo { - srom 9-10 0x18C -} -u32 mcsbw405ghpo { - srom 9-10 0x190 - srom >= 11 0x184 -} - -# 4 bytes each -# 5G low subband mcs 0-7 power-offset. -# LSB nibble: m0, MSB nibble: m7 -# mcsbw205glpo mcsbw20ul5glpo mcsbw405glpo -# -u32 mcsbw205glpo { - srom 9-10 0x170 - srom >= 11 0x160 -} -u32 mcsbw20ul5glpo { - srom 9-10 0x174 -} -u32 mcsbw405glpo { - srom 9-10 0x178 - srom >= 11 0x164 -} - -# 4 bytes each -# 5G mid subband mcs 0-7 power-offset. -# LSB nibble: m0, MSB nibble: m7 -# mcsbw205gmpo mcsbw20ul5gmpo mcsbw405gmpo -# -u32 mcsbw205gmpo { - srom 9-10 0x17C - srom >= 11 0x170 -} -u32 mcsbw20ul5gmpo { - srom 9-10 0x180 -} -u32 mcsbw405gmpo { - srom 9-10 0x184 - srom >= 11 0x174 -} - -# 2 bytes total -# mcs-32 power offset for each band/subband. -# LSB nibble: 2G band, MSB nibble: -# mcs322ghpo, mcs325gmpo, mcs325glpo, mcs322gpo -# -u16 mcs32po { - srom 9-10 0x194 -} - -u8 measpower { - srom 8-10 0xB4 (&0xFE, >>1) - srom >= 11 0xB0 (&0xFE, >>1) -} -u8 measpower1 { - srom 8-10 0xBF (&0x7F) - srom >= 11 0xBB (&0x7F) -} -u8 measpower2 { - srom 8-10 u16 0xBE (&0x3F80, >>7) - srom >= 11 u16 0xBA (&0x3F80, >>7) -} -u16 rawtempsense { - srom 8-10 0xB4 (&0x1FF) - srom >= 11 0xB0 (&0x1FF) -} - -u8 noiselvl2ga0 { - sfmt decimal - srom 8-10 0x1AB (&0x1F) - srom >= 11 0x1BD (&0x1F) -} -u8 noiselvl2ga1 { - sfmt decimal - srom 8-10 u16 0x1AA (&0x3E0, >>5) - srom >= 11 u16 0x1BC (&0x3E0, >>5) -} -u8 noiselvl2ga2 { - sfmt decimal - srom 8-10 0x1AA (&0x7C, >>2) - srom >= 11 0x1BC (&0x7C, >>2) -} -u8[4] noiselvl5ga0 { - sfmt decimal - srom >= 11 u8 0x1BF (&0x1F), u8 0x1C1 (&0x1F), u8 0x1C3 (&0x1F), u8 0x1C5 (&0x1F) -} -u8[4] noiselvl5ga1 { - sfmt decimal - srom >= 11 u16[4] 0x1BE (&0x3E0, >>5) -} -u8[4] noiselvl5ga2 { - sfmt decimal - srom >= 11 u8 0x1BE (&0x7C, >>2), u8 0x1C0 (&0x7C, >>2), u8 0x1C2 (&0x7C, >>2), u8 0x1C4 (&0x7C, >>2) -} - -# paparambwver -u8 paparambwver { - sfmt decimal - srom >= 11 0x190 (&0xF0, >>4) -} - -# PA parameters: 8 (sromrev == 1) -# or 9 (sromrev > 1) bytes -# -u16 pa0b0 { - sfmt decimal - srom 1-3 0x5E - srom 8-10 0xC2 -} -u16 pa0b1 { - sfmt decimal - srom 1-3 0x60 - srom 8-10 0xC4 -} -u16 pa0b2 { - sfmt decimal - srom 1-3 0x62 - srom 8-10 0xC6 -} -u8 pa0itssit { - sfmt decimal - srom 1-3 0x71 - srom 8-10 0xC0 -} -u8 pa0maxpwr { - sfmt decimal - srom 1-3 0x69 - srom 8-10 0xC1 -} -u8 opo { - srom 2-3 0x79 - srom 8-10 0x143 -} - -# 5G PA params -u16 pa1b0 { - sfmt decimal - srom 1-3 0x6A - srom 8-10 0xCC -} -u16 pa1b1 { - sfmt decimal - srom 1-3 0x6C - srom 8-10 0xCE -} -u16 pa1b2 { - sfmt decimal - srom 1-3 0x6E - srom 8-10 0xD0 -} -u16 pa1lob0 { - sfmt decimal - srom 2-3 0x3C - srom 8-10 0xD2 -} -u16 pa1lob1 { - sfmt decimal - srom 2-3 0x3E - srom 8-10 0xD4 -} -u16 pa1lob2 { - sfmt decimal - srom 2-3 0x40 - srom 8-10 0xD6 -} -u16 pa1hib0 { - sfmt decimal - srom 2-3 0x42 - srom 8-10 0xD8 -} -u16 pa1hib1 { - sfmt decimal - srom 2-3 0x44 - srom 8-10 0xDA -} -u16 pa1hib2 { - sfmt decimal - srom 2-3 0x46 - srom 8-10 0xDC -} -u8 pa1itssit { - sfmt decimal - srom 1-3 0x70 - srom 8-10 0xC8 -} -u8 pa1maxpwr { - sfmt decimal - srom 1-3 0x68 - srom 8-10 0xC9 -} -u8 pa1lomaxpwr { - sfmt decimal - srom 2-3 0x3A - srom 8-10 0xCA -} -u8 pa1himaxpwr { - sfmt decimal - srom 2-3 0x3B - srom 8-10 0xCB -} - -u16 pdoffset40ma0 { - srom >= 11 0xCA -} -u16 pdoffset40ma1 { - srom >= 11 0xCC -} -u16 pdoffset40ma2 { - srom >= 11 0xCE -} -u16 pdoffset80ma0 { - srom >= 11 0xD0 -} -u16 pdoffset80ma1 { - srom >= 11 0xD2 -} -u16 pdoffset80ma2 { - srom >= 11 0xD4 -} - -u8 pdoffset2g40ma0 { - srom >= 11 0xC9 (&0xF) -} -u8 pdoffset2g40ma1 { - srom >= 11 0xC9 (&0xF0, >>4) -} -u8 pdoffset2g40ma2 { - srom >= 11 0xC8 (&0xF) -} -u8 pdoffset2g40mvalid { - srom >= 11 0xC8 (&0x80, >>7) -} - -# 40Mhz channel 2g/5g power offset -u16 bw40po { - srom 4-7 0x18E - srom 8 0x196 -} - -# 40Mhz channel dup 2g/5g power offset -u16 bwduppo { - srom 4-7 0x190 - srom 8 0x198 -} - -# cck2g/ofdm2g/ofdm5g power offset -u16 cck2gpo { - srom 4-7 0x138 - srom 8 0x140 -} -u32 ofdm2gpo { - srom 4-7 0x13A - srom 8 0x142 -} -u32 ofdm5gpo { - srom 4-7 0x13E - srom 8 0x146 -} -u32 ofdm5glpo { - srom 4-7 0x142 - srom 8 0x14A -} -u32 ofdm5ghpo { - srom 4-7 0x146 - srom 8 0x14E -} - -# cdd2g/5g power offset -u16 cddpo { - srom 4-7 0x18A - srom 8 0x192 -} - -# mcs2g power offset -u16 mcs2gpo0 { - srom 4-7 0x14A - srom 8 0x152 -} -u16 mcs2gpo1 { - srom 4-7 0x14C - srom 8 0x154 -} -u16 mcs2gpo2 { - srom 4-7 0x14E - srom 8 0x156 -} -u16 mcs2gpo3 { - srom 4-7 0x150 - srom 8 0x158 -} -u16 mcs2gpo4 { - srom 4-7 0x152 - srom 8 0x15A -} -u16 mcs2gpo5 { - srom 4-7 0x154 - srom 8 0x15C -} -u16 mcs2gpo6 { - srom 4-7 0x156 - srom 8 0x15E -} -u16 mcs2gpo7 { - srom 4-7 0x158 - srom 8 0x160 -} - -# mcs5g low-high band power offset -u16 mcs5glpo0 { - srom 4-7 0x16A - srom 8 0x172 -} -u16 mcs5glpo1 { - srom 4-7 0x16C - srom 8 0x174 -} -u16 mcs5glpo2 { - srom 4-7 0x16E - srom 8 0x176 -} -u16 mcs5glpo3 { - srom 4-7 0x170 - srom 8 0x178 -} -u16 mcs5glpo4 { - srom 4-7 0x172 - srom 8 0x17A -} -u16 mcs5glpo5 { - srom 4-7 0x174 - srom 8 0x17C -} -u16 mcs5glpo6 { - srom 4-7 0x176 - srom 8 0x17E -} -u16 mcs5glpo7 { - srom 4-7 0x178 - srom 8 0x180 -} -u16 mcs5ghpo0 { - srom 4-7 0x17A - srom 8 0x182 -} -u16 mcs5ghpo1 { - srom 4-7 0x17C - srom 8 0x184 -} -u16 mcs5ghpo2 { - srom 4-7 0x17E - srom 8 0x186 -} -u16 mcs5ghpo3 { - srom 4-7 0x180 - srom 8 0x188 -} -u16 mcs5ghpo4 { - srom 4-7 0x182 - srom 8 0x18A -} -u16 mcs5ghpo5 { - srom 4-7 0x184 - srom 8 0x18C -} -u16 mcs5ghpo6 { - srom 4-7 0x186 - srom 8 0x18E -} -u16 mcs5ghpo7 { - srom 4-7 0x188 - srom 8 0x190 -} - -# mcs5g mid band power offset -u16 mcs5gpo0 { - srom 4-7 0x15A - srom 8 0x162 -} -u16 mcs5gpo1 { - srom 4-7 0x15C - srom 8 0x164 -} -u16 mcs5gpo2 { - srom 4-7 0x15E - srom 8 0x166 -} -u16 mcs5gpo3 { - srom 4-7 0x160 - srom 8 0x168 -} -u16 mcs5gpo4 { - srom 4-7 0x162 - srom 8 0x16A -} -u16 mcs5gpo5 { - srom 4-7 0x164 - srom 8 0x16C -} -u16 mcs5gpo6 { - srom 4-7 0x166 - srom 8 0x16E -} -u16 mcs5gpo7 { - srom 4-7 0x168 - srom 8 0x170 -} - -# stbc2g/5g power offset -u16 stbcpo { - srom 4-7 0x18C - srom 8 0x194 -} - -u8 regrev { - srom 3 0x78 - srom 4 0x55 - srom 5-7 0x47 - srom 8-10 0x95 - srom >= 11 0x99 -} - -# 4328 2G RSSI mid pt sel & board switch arch, -# 2 bytes, rev 3. -# -u8 rssismf2g { - srom 3 0x51 (&0xF) - srom 8-10 0xA5 (&0xF) -} -u8 rssismc2g { - srom 3 0x51 (&0xF0, >>4) - srom 8-10 0xA5 (&0xF0, >>4) -} -u8 rssisav2g { - srom 3 0x50 (&0x7) - srom 8-10 0xA4 (&0x7) -} -u8 bxa2g { - srom 3 0x50 (&0x18, >>3) - srom 8-10 0xA4 (&0x18, >>3) -} - -# 4328 5G RSSI mid pt sel & board switch arch, -# 2 bytes, rev 3. -# -u8 rssismf5g { - srom 3 0x53 (&0xF) - srom 8-10 0xA7 (&0xF) -} -u8 rssismc5g { - srom 3 0x53 (&0xF0, >>4) - srom 8-10 0xA7 (&0xF0, >>4) -} -u8 rssisav5g { - srom 3 0x52 (&0x7) - srom 8-10 0xA6 (&0x7) -} -u8 bxa5g { - srom 3 0x52 (&0x18, >>3) - srom 8-10 0xA6 (&0x18, >>3) -} - -u8 rxgainerr2ga0 { - srom 8-10 0x19B (&0x3F) - srom >= 11 0x1C7 (&0x3F) -} -u8 rxgainerr2ga1 { - srom 8-10 u16 0x19A (&0x7C0, >>6) - srom >= 11 u16 0x1C6 (&0x7C0, >>6) -} -u8 rxgainerr2ga2 { - srom 8-10 0x19A (&0xF8, >>3) - srom >= 11 0x1C6 (&0xF8, >>3) -} -u8[4] rxgainerr5ga0 { - srom >= 11 u8 0x1C9 (&0x3F), u8 0x1CB (&0x3F), u8 0x1CD (&0x3F), u8 0x1CF (&0x3F) -} -u8[4] rxgainerr5ga1 { - srom >= 11 u16[4] 0x1C8 (&0x7C0, >>6) -} -u8[4] rxgainerr5ga2 { - srom >= 11 u8 0x1C8 (&0xF8, >>3), u8 0x1CA (&0xF8, >>3), u8 0x1CC (&0xF8, >>3), u8 0x1CE (&0xF8, >>3) -} -u8 rxgainerr5gha0 { - srom 8-10 0x1A1 (&0x3F) -} -u8 rxgainerr5gha1 { - srom 8-10 u16 0x1A0 (&0x7C0, >>6) -} -u8 rxgainerr5gha2 { - srom 8-10 0x1A0 (&0xF8, >>3) -} -u8 rxgainerr5gla0 { - srom 8-10 0x19D (&0x3F) -} -u8 rxgainerr5gla1 { - srom 8-10 u16 0x19C (&0x7C0, >>6) -} -u8 rxgainerr5gla2 { - srom 8-10 0x19C (&0xF8, >>3) -} -u8 rxgainerr5gma0 { - srom 8-10 0x19F (&0x3F) -} -u8 rxgainerr5gma1 { - srom 8-10 u16 0x19E (&0x7C0, >>6) -} -u8 rxgainerr5gma2 { - srom 8-10 0x19E (&0xF8, >>3) -} -u8 rxgainerr5gua0 { - srom 8-10 0x1A3 (&0x3F) -} -u8 rxgainerr5gua1 { - srom 8-10 u16 0x1A2 (&0x7C0, >>6) -} -u8 rxgainerr5gua2 { - srom 8-10 0x1A2 (&0xF8, >>3) -} - -# 4328 2G RX power offset -i8 rxpo2g { - sfmt decimal - srom 3 0x5B - srom 8-10 0xAD -} - -# 4328 5G RX power offset -i8 rxpo5g { - sfmt decimal - srom 3 0x5A - srom 8-10 0xAC -} - -u16 subband5gver { - srom 8-10 u8 0x1A5 (&0x7) - srom >= 11 0xD6 -} - -# 2 bytes -# byte1 tempthresh -# byte2 period(msb 4 bits) | hysterisis(lsb 4 bits) -# -u8 tempthresh { - srom 8-10 0xB2 - srom >= 11 0xAE -} -u8 temps_period { - sfmt decimal - srom 8-10 0xBC (&0xF) - srom >= 11 0xB8 (&0xF) -} -u8 temps_hysteresis { - sfmt decimal - srom 8-10 0xBC (&0xF0, >>4) - srom >= 11 0xB8 (&0xF0, >>4) -} -u8 tempoffset { - sfmt decimal - srom 8-10 0xB3 - srom >= 11 0xAF -} -u8 tempsense_slope { - srom 8-10 0xB7 - srom >= 11 0xB3 -} -u8 tempcorrx { - srom 8-10 0xB6 (&0xFC, >>2) - srom >= 11 0xB2 (&0xFC, >>2) -} -u8 tempsense_option { - srom 8-10 0xB6 (&0x3) - srom >= 11 0xB2 (&0x3) -} -u8 phycal_tempdelta { - sfmt decimal - srom 8-10 0xBD - srom >= 11 0xB9 -} - -# 4328 2G TR isolation, 1 byte -u8 tri2g { - srom 3 0x55 - srom 8-10 0xA9 -} - -# 4328 5G TR isolation, 3 bytes -u8 tri5gl { - srom 3 0x57 - srom 8-10 0xAB -} -u8 tri5g { - srom 3 0x54 - srom 8-10 0xA8 -} -u8 tri5gh { - srom 3 0x56 - srom 8-10 0xAA -} - -# phy txbf rpcalvars -u16 rpcal2g { - srom >= 11 0x16C -} -u16 rpcal5gb0 { - srom >= 11 0x16E -} -u16 rpcal5gb1 { - srom >= 11 0x17C -} -u16 rpcal5gb2 { - srom >= 11 0x17E -} -u16 rpcal5gb3 { - srom >= 11 0x18C -} - -# Crystal frequency in kilohertz -u32 xtalfreq { - sfmt decimal - srom >= 11 u16 0xB4 -} - -# N-PHY tx power workaround -u8 txpid2ga0 { - srom 4-7 0x63 -} -u8 txpid2ga1 { - srom 4-7 0x62 -} -u8 txpid2ga2 { - srom 4-7 0x65 -} -u8 txpid2ga3 { - srom 4-7 0x64 -} -u8 txpid5ga0 { - srom 4-7 0x67 -} -u8 txpid5ga1 { - srom 4-7 0x66 -} -u8 txpid5ga2 { - srom 4-7 0x69 -} -u8 txpid5ga3 { - srom 4-7 0x68 -} -u8 txpid5gha0 { - srom 4-7 0x6F -} -u8 txpid5gha1 { - srom 4-7 0x6E -} -u8 txpid5gha2 { - srom 4-7 0x71 -} -u8 txpid5gha3 { - srom 4-7 0x70 -} -u8 txpid5gla0 { - srom 4-7 0x6B -} -u8 txpid5gla1 { - srom 4-7 0x6A -} -u8 txpid5gla2 { - srom 4-7 0x6D -} -u8 txpid5gla3 { - srom 4-7 0x6C -} - -u16 cckPwrOffset { - srom 10 0x1B4 -} -u8[6] et1macaddr { - sfmt macaddr - srom 0-2 u8 0x55, u8 0x54, u8 0x57, u8 0x56, u8 0x59, u8 0x58 -} -u8 eu_edthresh2g { - srom 8 0x1A9 - srom 9 0x199 - srom 10 0x199 - srom 11 0x1D1 -} -u8 eu_edthresh5g { - srom 8 0x1A8 - srom 9 0x198 - srom 10 0x198 - srom 11 0x1D0 -} -u8 freqoffset_corr { - srom 8-10 0xB9 (&0xF) -} -u8 hw_iqcal_en { - srom 8-10 0xB9 (&0x20, >>5) -} -u8[6] il0macaddr { - sfmt macaddr - srom 0-2 u8 0x49, u8 0x48, u8 0x51, u8 0x50, u8 0x53, u8 0x52 -} -u8 iqcal_swp_dis { - srom 8-10 0xB9 (&0x10, >>4) -} - -u8 noisecaloffset { - srom 8-9 0x1B5 -} -u8 noisecaloffset5g { - srom 8-9 0x1B4 -} -u8 noiselvl5gha0 { - srom 8-10 0x1B1 (&0x1F) -} -u8 noiselvl5gha1 { - srom 8-10 u16 0x1B0 (&0x3E0, >>5) -} -u8 noiselvl5gha2 { - srom 8-10 0x1B0 (&0x7C, >>2) -} -u8 noiselvl5gla0 { - srom 8-10 0x1AD (&0x1F) -} -u8 noiselvl5gla1 { - srom 8-10 u16 0x1AC (&0x3E0, >>5) -} -u8 noiselvl5gla2 { - srom 8-10 0x1AC (&0x7C, >>2) -} -u8 noiselvl5gma0 { - srom 8-10 0x1AF (&0x1F) -} -u8 noiselvl5gma1 { - srom 8-10 u16 0x1AE (&0x3E0, >>5) -} -u8 noiselvl5gma2 { - srom 8-10 0x1AE (&0x7C, >>2) -} -u8 noiselvl5gua0 { - srom 8-10 0x1B3 (&0x1F) -} -u8 noiselvl5gua1 { - srom 8-10 u16 0x1B2 (&0x3E0, >>5) -} -u8 noiselvl5gua2 { - srom 8-10 0x1B2 (&0x7C, >>2) -} - -u8 pcieingress_war { - srom 8-10 0x1A7 (&0xF) -} - -u8 pdoffsetcckma0 { - srom >= 11 0x18F (&0xF) -} -u8 pdoffsetcckma1 { - srom >= 11 0x18F (&0xF0, >>4) -} -u8 pdoffsetcckma2 { - srom >= 11 0x18E (&0xF) -} - -u8 sar2g { - srom 9-10 0x1A9 - srom >= 11 0x1BB -} -u8 sar5g { - srom 9-10 0x1A8 - srom >= 11 0x1BA -} - -u32[5] swctrlmap_2g { - srom 10 u32[4] 0x1B8, u16 0x1C8 -} - -u16 tssifloor2g { - srom >= 11 0xBE (&0x3FF) -} -u16[4] tssifloor5g { - srom >= 11 0xC0 (&0x3FF) -} - -u8 txidxcap2g { - srom >= 11 u16 0x1A8 (&0xFF0, >>4) -} -u8 txidxcap5g { - srom >= 11 u16 0x1AC (&0xFF0, >>4) -} - -# -# Any variables defined within a `struct` block will be interpreted relative to -# the provided array of SPROM base addresses; this is used to define -# a common layout defined at the given base addresses. -# -# To produce SPROM variable names matching those used in the Broadcom HND -# ASCII 'key=value\0' NVRAM, the index number of the variable's -# struct instance will be appended (e.g., given a variable of noiselvl5ga, the -# generated variable instances will be named noiselvl5ga0, noiselvl5ga1, -# noiselvl5ga2, noiselvl5ga3 ...) -# - -# PHY chain[0-4] parameters -struct phy_chains[] { - srom 4-7 [0x080, 0x0AE, 0x0DC, 0x10A] - srom 8-10 [0x0C0, 0x0E0, 0x100, 0x120] - srom >= 11 [0x0D8, 0x100, 0x128] - - # AC-PHY PA parameters - u8[4] maxp5ga { - srom 4-7 u8 0xB - srom 8-10 u8 0x9 - srom >= 11 u8 0xD, u8 0xC, u8 0xF, u8 0xE +group "Antenna Configuration" { + u8 aa2g { + desc "Available 2.4GHz Antennas" + help "Antennas 0-3 are marked as available if the + corresponding bit is set." } - u16[3] pa2ga { - srom >= 11 0x2 - } - u8 maxp2ga { - srom 4-7 0x1 - srom 8-10 0x1 - srom >= 11 0x1 - } - u16[12] pa5ga { - srom >= 11 0x10 - } - - # AC-PHY rxgains - u8 rxgains5ghtrelnabypa { - srom >= 11 0x8 (&0x80, >>7) - } - u8 rxgains5ghelnagaina { - srom >= 11 0x8 (&0x7) - } - u8 rxgains5gelnagaina { - srom >= 11 0xA (&0x7) - } - u8 rxgains5gmtrelnabypa { - srom >= 11 0x9 (&0x80, >>7) - } - u8 rxgains2gtrelnabypa { - srom >= 11 0xB (&0x80, >>7) - } - u8 rxgains5gmtrisoa { - srom >= 11 0x9 (&0x78, >>3) - } - u8 rxgains5gmelnagaina { - srom >= 11 0x9 (&0x7) - } - u8 rxgains2gelnagaina { - srom >= 11 0xB (&0x7) - } - u8 rxgains5gtrisoa { - srom >= 11 0xA (&0x78, >>3) - } - u8 rxgains5gtrelnabypa { - srom >= 11 0xA (&0x80, >>7) - } - u8 rxgains2gtrisoa { - srom >= 11 0xB (&0x78, >>3) - } - u8 rxgains5ghtrisoa { - srom >= 11 0x8 (&0x78, >>3) + u8 aa5g { + desc "Available 5GHz Antennas" + help "Antennas 0-3 are marked as available if the + corresponding bit is set." } - # 11n PA parameters - u16 pa5gw2a { - srom 4-7 0x12 - srom 8-10 0x10 + u8 ag0 { + desc "Antenna 0 Gain" + help "The lower 6 bits represent dB as a signed number. + The high 2 bits represent a positive number of + quarter dBs to be added to the dB value" } - u16 pa5ghw1a { - srom 4-7 0x20 - srom 8-10 0x1A + u8 ag1 { + desc "Antenna 1 Gain" + help "The lower 6 bits represent dB as a signed number. + The high 2 bits represent a positive number of + quarter dBs to be added to the dB value" } - u16 pa5glw3a { - srom 4-7 0x1C + u8 ag2 { + desc "Antenna 2 Gain" + help "The lower 6 bits represent dB as a signed number. + The high 2 bits represent a positive number of + quarter dBs to be added to the dB value" } - u16 pa5glw1a { - srom 4-7 0x18 - srom 8-10 0x14 + u8 ag3 { + desc "Antenna 3 Gain" + help "The lower 6 bits represent dB as a signed number. + The high 2 bits represent a positive number of + quarter dBs to be added to the dB value" } - u16 pa5gw1a { - srom 4-7 0x10 - srom 8-10 0xE + + u8 txchain { + desc "Available TX Chains" + help "TX chains 0-3 are marked as available if the + corresponding bit is set." + + all1 ignore } - u16 pa5glw0a { - srom 4-7 0x16 - srom 8-10 0x12 + + u8 rxchain { + desc "Available RX Chains" + help "RX chains 0-3 are marked as available if the + corresponding bit is set." + + all1 ignore } - u16 pa5gw3a { - srom 4-7 0x14 - } - u16 pa5glw2a { - srom 4-7 0x1A - srom 8-10 0x16 - } - u16 pa5ghw3a { - srom 4-7 0x24 - } - u16 pa5gw0a { - srom 4-7 0xE - srom 8-10 0xC - } - u8 maxp5gha { - srom 4-7 0xD - srom 8-10 0xB - } - u16 pa5ghw2a { - srom 4-7 0x22 - srom 8-10 0x1C - } - u16 pa5ghw0a { - srom 4-7 0x1E - srom 8-10 0x18 - } - u16 pa2gw3a { - srom 4-7 0x8 - } - u16 pa2gw2a { - srom 4-7 0x6 - srom 8-10 0x6 - } - u16 pa2gw1a { - srom 4-7 0x4 - srom 8-10 0x4 - } - u16 pa2gw0a { - srom 4-7 0x2 - srom 8-10 0x2 - } - u8 maxp5gla { - srom 4-7 0xC - srom 8-10 0xA - } - u8 itt5ga { - srom 4-7 0xA - srom 8-10 0x8 - } - u8 itt2ga { - srom 4-7 0x0 - srom 8-10 0x0 + + u16 antswitch { + desc "Antenna Diversity Switch Type" + help "The antenna diversity switch configuration used by + this device. The value is hardware-specific." + all1 ignore } } + +u8 aga0 { + #desc + #help +} +u8 aga1 { + #desc + #help +} +u8 aga2 { + #desc + #help +} +u8 agbg0 { + #desc + #help +} +u8 agbg1 { + #desc + #help +} +u8 agbg2 { + #desc + #help +} +u8 antswctl2g { + #desc + #help +} +u8 antswctl5g { + #desc + #help +} +u32 boardflags { + #desc + #help +} +u32 boardflags2 { + #desc + #help +} +u32 boardflags3 { + #desc + #help +} +u16 boardnum { + fmt decimal + #desc + #help +} +u16 boardrev { + #desc + #help +} +u16 boardtype { + #desc + #help +} +u16 boardvendor { + #desc + #help +} +u16 bw40po { + #desc + #help +} +u16 bwduppo { + #desc + #help +} +u8 bxa2g { + #desc + #help +} +u8 bxa5g { + #desc + #help +} +u8 cc { + fmt decimal + #desc + #help +} +u16 cck2gpo { + #desc + #help +} +u16 cckPwrOffset { + #desc + #help +} +u16 cckbw202gpo { + #desc + #help +} +u16 cckbw20ul2gpo { + #desc + #help +} +char ccode[2] { + #desc + #help +} +u16 cddpo { + #desc + #help +} +u16 devid { + #desc + #help +} +u16 dot11agduphrpo { + #desc + #help +} +u16 dot11agduplrpo { + #desc + #help +} +u16 dot11agofdmhrbw202gpo { + #desc + #help +} +u8 elna2g { + #desc + #help +} +u8 elna5g { + #desc + #help +} +u8 epagain2g { + fmt decimal + #desc + #help +} +u8 epagain5g { + fmt decimal + #desc + #help +} +u8 et1macaddr[6] { + fmt macaddr + #desc + #help +} +u8 eu_edthresh2g { + #desc + #help +} +u8 eu_edthresh5g { + #desc + #help +} +u8 extpagain2g { + #desc + #help +} +u8 extpagain5g { + #desc + #help +} +u8 femctrl { + fmt decimal + #desc + #help +} +u8 freqoffset_corr { + #desc + #help +} +u8 gainctrlsph { + fmt decimal + #desc + #help +} +u8 hw_iqcal_en { + #desc + #help +} +u8 il0macaddr[6] { + fmt macaddr + #desc + #help +} +u8 iqcal_swp_dis { + #desc + #help +} +u8 itt2ga0 { + #desc + #help +} +u8 itt2ga1 { + #desc + #help +} +u8 itt2ga2 { + #desc + #help +} +u8 itt2ga3 { + #desc + #help +} +u8 itt5ga0 { + #desc + #help +} +u8 itt5ga1 { + #desc + #help +} +u8 itt5ga2 { + #desc + #help +} +u8 itt5ga3 { + #desc + #help +} +u8 ledbh0 { + all1 ignore + #desc + #help +} +u8 ledbh1 { + all1 ignore + #desc + #help +} +u8 ledbh2 { + all1 ignore + #desc + #help +} +u8 ledbh3 { + all1 ignore + #desc + #help +} +u32 leddc { + all1 ignore + fmt leddc + #desc + #help +} +u16 legofdm40duppo { + #desc + #help +} +u32 legofdmbw202gpo { + #desc + #help +} +u32 legofdmbw205ghpo { + #desc + #help +} +u32 legofdmbw205glpo { + #desc + #help +} +u32 legofdmbw205gmpo { + #desc + #help +} +u32 legofdmbw20ul2gpo { + #desc + #help +} +u32 legofdmbw20ul5ghpo { + #desc + #help +} +u32 legofdmbw20ul5glpo { + #desc + #help +} +u32 legofdmbw20ul5gmpo { + #desc + #help +} +u8 macaddr[6] { + fmt macaddr + #desc + #help +} +u8 maxp2ga0 { + #desc + #help +} +u8 maxp2ga1 { + #desc + #help +} +u8 maxp2ga2 { + #desc + #help +} +u8 maxp2ga3 { + #desc + #help +} +u8 maxp5ga0[4] { + #desc + #help +} +u8 maxp5ga1[4] { + #desc + #help +} +u8 maxp5ga2[4] { + #desc + #help +} +u8 maxp5ga3[1] { + #desc + #help +} +u8 maxp5gha0 { + #desc + #help +} +u8 maxp5gha1 { + #desc + #help +} +u8 maxp5gha2 { + #desc + #help +} +u8 maxp5gha3 { + #desc + #help +} +u8 maxp5gla0 { + #desc + #help +} +u8 maxp5gla1 { + #desc + #help +} +u8 maxp5gla2 { + #desc + #help +} +u8 maxp5gla3 { + #desc + #help +} +u16 mcs2gpo0 { + #desc + #help +} +u16 mcs2gpo1 { + #desc + #help +} +u16 mcs2gpo2 { + #desc + #help +} +u16 mcs2gpo3 { + #desc + #help +} +u16 mcs2gpo4 { + #desc + #help +} +u16 mcs2gpo5 { + #desc + #help +} +u16 mcs2gpo6 { + #desc + #help +} +u16 mcs2gpo7 { + #desc + #help +} +u16 mcs32po { + #desc + #help +} +u16 mcs5ghpo0 { + #desc + #help +} +u16 mcs5ghpo1 { + #desc + #help +} +u16 mcs5ghpo2 { + #desc + #help +} +u16 mcs5ghpo3 { + #desc + #help +} +u16 mcs5ghpo4 { + #desc + #help +} +u16 mcs5ghpo5 { + #desc + #help +} +u16 mcs5ghpo6 { + #desc + #help +} +u16 mcs5ghpo7 { + #desc + #help +} +u16 mcs5glpo0 { + #desc + #help +} +u16 mcs5glpo1 { + #desc + #help +} +u16 mcs5glpo2 { + #desc + #help +} +u16 mcs5glpo3 { + #desc + #help +} +u16 mcs5glpo4 { + #desc + #help +} +u16 mcs5glpo5 { + #desc + #help +} +u16 mcs5glpo6 { + #desc + #help +} +u16 mcs5glpo7 { + #desc + #help +} +u16 mcs5gpo0 { + #desc + #help +} +u16 mcs5gpo1 { + #desc + #help +} +u16 mcs5gpo2 { + #desc + #help +} +u16 mcs5gpo3 { + #desc + #help +} +u16 mcs5gpo4 { + #desc + #help +} +u16 mcs5gpo5 { + #desc + #help +} +u16 mcs5gpo6 { + #desc + #help +} +u16 mcs5gpo7 { + #desc + #help +} +u32 mcsbw202gpo { + #desc + #help +} +u32 mcsbw205ghpo { + #desc + #help +} +u32 mcsbw205glpo { + #desc + #help +} +u32 mcsbw205gmpo { + #desc + #help +} +u32 mcsbw20ul2gpo { + #desc + #help +} +u32 mcsbw20ul5ghpo { + #desc + #help +} +u32 mcsbw20ul5glpo { + #desc + #help +} +u32 mcsbw20ul5gmpo { + #desc + #help +} +u32 mcsbw402gpo { + #desc + #help +} +u32 mcsbw405ghpo { + #desc + #help +} +u32 mcsbw405glpo { + #desc + #help +} +u32 mcsbw405gmpo { + #desc + #help +} +u32 mcsbw805ghpo { + #desc + #help +} +u32 mcsbw805glpo { + #desc + #help +} +u32 mcsbw805gmpo { + #desc + #help +} +u16 mcslr5ghpo { + #desc + #help +} +u16 mcslr5glpo { + #desc + #help +} +u16 mcslr5gmpo { + #desc + #help +} +u8 measpower { + #desc + #help +} +u8 measpower1 { + #desc + #help +} +u8 measpower2 { + #desc + #help +} +u8 noisecaloffset { + #desc + #help +} +u8 noisecaloffset5g { + #desc + #help +} +u8 noiselvl2ga0 { + fmt decimal + #desc + #help +} +u8 noiselvl2ga1 { + fmt decimal + #desc + #help +} +u8 noiselvl2ga2 { + fmt decimal + #desc + #help +} +u8 noiselvl5ga0[4] { + fmt decimal + #desc + #help +} +u8 noiselvl5ga1[4] { + fmt decimal + #desc + #help +} +u8 noiselvl5ga2[4] { + fmt decimal + #desc + #help +} +u8 noiselvl5gha0 { + #desc + #help +} +u8 noiselvl5gha1 { + #desc + #help +} +u8 noiselvl5gha2 { + #desc + #help +} +u8 noiselvl5gla0 { + #desc + #help +} +u8 noiselvl5gla1 { + #desc + #help +} +u8 noiselvl5gla2 { + #desc + #help +} +u8 noiselvl5gma0 { + #desc + #help +} +u8 noiselvl5gma1 { + #desc + #help +} +u8 noiselvl5gma2 { + #desc + #help +} +u8 noiselvl5gua0 { + #desc + #help +} +u8 noiselvl5gua1 { + #desc + #help +} +u8 noiselvl5gua2 { + #desc + #help +} +u32 ofdm2gpo { + #desc + #help +} +u32 ofdm5ghpo { + #desc + #help +} +u32 ofdm5glpo { + #desc + #help +} +u32 ofdm5gpo { + #desc + #help +} +u16 ofdmlrbw202gpo { + #desc + #help +} +u8 opo { + fmt decimal + #desc + #help +} +i16 pa0b0 { + fmt decimal + #desc + #help +} +i16 pa0b1 { + fmt decimal + #desc + #help +} +i16 pa0b2 { + fmt decimal + #desc + #help +} +u8 pa0itssit { + fmt decimal + #desc + #help +} +u8 pa0maxpwr { + fmt decimal + #desc + #help +} +i16 pa1b0 { + fmt decimal + #desc + #help +} +i16 pa1b1 { + fmt decimal + #desc + #help +} +i16 pa1b2 { + fmt decimal + #desc + #help +} +i16 pa1hib0 { + fmt decimal + #desc + #help +} +i16 pa1hib1 { + fmt decimal + #desc + #help +} +i16 pa1hib2 { + fmt decimal + #desc + #help +} +u8 pa1himaxpwr { + fmt decimal + #desc + #help +} +u8 pa1itssit { + fmt decimal + #desc + #help +} +i16 pa1lob0 { + fmt decimal + #desc + #help +} +i16 pa1lob1 { + fmt decimal + #desc + #help +} +i16 pa1lob2 { + fmt decimal + #desc + #help +} +u8 pa1lomaxpwr { + fmt decimal + #desc + #help +} +u8 pa1maxpwr { + fmt decimal + #desc + #help +} +i16 pa2ga0[3] { + fmt decimal + #desc + #help +} +i16 pa2ga1[3] { + fmt decimal + #desc + #help +} +i16 pa2ga2[3] { + fmt decimal + #desc + #help +} +i16 pa2ga3[3] { + fmt decimal + #desc + #help +} +u16 pa2gccka0[3] { + #desc + #help +} +u16 pa2gw0a0 { + #desc + #help +} +u16 pa2gw0a1 { + #desc + #help +} +u16 pa2gw0a2 { + #desc + #help +} +u16 pa2gw0a3 { + #desc + #help +} +u16 pa2gw1a0 { + #desc + #help +} +u16 pa2gw1a1 { + #desc + #help +} +u16 pa2gw1a2 { + #desc + #help +} +u16 pa2gw1a3 { + #desc + #help +} +u16 pa2gw2a0 { + #desc + #help +} +u16 pa2gw2a1 { + #desc + #help +} +u16 pa2gw2a2 { + #desc + #help +} +u16 pa2gw2a3 { + #desc + #help +} +u16 pa2gw3a0 { + #desc + #help +} +u16 pa2gw3a1 { + #desc + #help +} +u16 pa2gw3a2 { + #desc + #help +} +u16 pa2gw3a3 { + #desc + #help +} +i16 pa5ga0[12] { + fmt decimal + #desc + #help +} +i16 pa5ga1[12] { + fmt decimal + #desc + #help +} +i16 pa5ga2[12] { + fmt decimal + #desc + #help +} +i16 pa5ga3[12] { + fmt decimal + #desc + #help +} +u16 pa5gbw4080a0[12] { + #desc + #help +} +u16 pa5gbw4080a1[12] { + #desc + #help +} +u16 pa5gbw40a0[12] { + #desc + #help +} +u16 pa5gbw80a0[12] { + #desc + #help +} +u16 pa5ghw0a0 { + #desc + #help +} +u16 pa5ghw0a1 { + #desc + #help +} +u16 pa5ghw0a2 { + #desc + #help +} +u16 pa5ghw0a3 { + #desc + #help +} +u16 pa5ghw1a0 { + #desc + #help +} +u16 pa5ghw1a1 { + #desc + #help +} +u16 pa5ghw1a2 { + #desc + #help +} +u16 pa5ghw1a3 { + #desc + #help +} +u16 pa5ghw2a0 { + #desc + #help +} +u16 pa5ghw2a1 { + #desc + #help +} +u16 pa5ghw2a2 { + #desc + #help +} +u16 pa5ghw2a3 { + #desc + #help +} +u16 pa5ghw3a0 { + #desc + #help +} +u16 pa5ghw3a1 { + #desc + #help +} +u16 pa5ghw3a2 { + #desc + #help +} +u16 pa5ghw3a3 { + #desc + #help +} +u16 pa5glw0a0 { + #desc + #help +} +u16 pa5glw0a1 { + #desc + #help +} +u16 pa5glw0a2 { + #desc + #help +} +u16 pa5glw0a3 { + #desc + #help +} +u16 pa5glw1a0 { + #desc + #help +} +u16 pa5glw1a1 { + #desc + #help +} +u16 pa5glw1a2 { + #desc + #help +} +u16 pa5glw1a3 { + #desc + #help +} +u16 pa5glw2a0 { + #desc + #help +} +u16 pa5glw2a1 { + #desc + #help +} +u16 pa5glw2a2 { + #desc + #help +} +u16 pa5glw2a3 { + #desc + #help +} +u16 pa5glw3a0 { + #desc + #help +} +u16 pa5glw3a1 { + #desc + #help +} +u16 pa5glw3a2 { + #desc + #help +} +u16 pa5glw3a3 { + #desc + #help +} +u16 pa5gw0a0 { + #desc + #help +} +u16 pa5gw0a1 { + #desc + #help +} +u16 pa5gw0a2 { + #desc + #help +} +u16 pa5gw0a3 { + #desc + #help +} +u16 pa5gw1a0 { + #desc + #help +} +u16 pa5gw1a1 { + #desc + #help +} +u16 pa5gw1a2 { + #desc + #help +} +u16 pa5gw1a3 { + #desc + #help +} +u16 pa5gw2a0 { + #desc + #help +} +u16 pa5gw2a1 { + #desc + #help +} +u16 pa5gw2a2 { + #desc + #help +} +u16 pa5gw2a3 { + #desc + #help +} +u16 pa5gw3a0 { + #desc + #help +} +u16 pa5gw3a1 { + #desc + #help +} +u16 pa5gw3a2 { + #desc + #help +} +u16 pa5gw3a3 { + #desc + #help +} +u8 paparambwver { + fmt decimal + #desc + #help +} +u8 papdcap2g { + fmt decimal + #desc + #help +} +u8 papdcap5g { + fmt decimal + #desc + #help +} +u8 pcieingress_war { + #desc + #help +} +u8 pdetrange2g { + #desc + #help +} +u8 pdetrange5g { + #desc + #help +} +u8 pdgain2g { + fmt decimal + #desc + #help +} +u8 pdgain5g { + fmt decimal + #desc + #help +} +u8 pdoffset2g40ma0 { + #desc + #help +} +u8 pdoffset2g40ma1 { + #desc + #help +} +u8 pdoffset2g40ma2 { + #desc + #help +} +u8 pdoffset2g40mvalid { + #desc + #help +} +u16 pdoffset40ma0 { + #desc + #help +} +u16 pdoffset40ma1 { + #desc + #help +} +u16 pdoffset40ma2 { + #desc + #help +} +u16 pdoffset80ma0 { + #desc + #help +} +u16 pdoffset80ma1 { + #desc + #help +} +u16 pdoffset80ma2 { + #desc + #help +} +u8 pdoffsetcckma0 { + #desc + #help +} +u8 pdoffsetcckma1 { + #desc + #help +} +u8 pdoffsetcckma2 { + #desc + #help +} +u8 phycal_tempdelta { + fmt decimal + #desc + #help +} +u16 rawtempsense { + #desc + #help +} +u8 regrev { + fmt decimal + #desc + #help +} +u32 rmax { + fmt decimal + #desc + #help +} +u32 rmin { + fmt decimal + #desc + #help +} +u16 rpcal2g { + #desc + #help +} +u16 rpcal5gb0 { + #desc + #help +} +u16 rpcal5gb1 { + #desc + #help +} +u16 rpcal5gb2 { + #desc + #help +} +u16 rpcal5gb3 { + #desc + #help +} +u8 rssisav2g { + #desc + #help +} +u8 rssisav5g { + #desc + #help +} +u8 rssismc2g { + #desc + #help +} +u8 rssismc5g { + #desc + #help +} +u8 rssismf2g { + #desc + #help +} +u8 rssismf5g { + #desc + #help +} +u8 rxgainerr2ga0 { + #desc + #help +} +u8 rxgainerr2ga1 { + #desc + #help +} +u8 rxgainerr2ga2 { + #desc + #help +} +u8 rxgainerr5ga0[4] { + #desc + #help +} +u8 rxgainerr5ga1[4] { + #desc + #help +} +u8 rxgainerr5ga2[4] { + #desc + #help +} +u8 rxgainerr5gha0 { + #desc + #help +} +u8 rxgainerr5gha1 { + #desc + #help +} +u8 rxgainerr5gha2 { + #desc + #help +} +u8 rxgainerr5gla0 { + #desc + #help +} +u8 rxgainerr5gla1 { + #desc + #help +} +u8 rxgainerr5gla2 { + #desc + #help +} +u8 rxgainerr5gma0 { + #desc + #help +} +u8 rxgainerr5gma1 { + #desc + #help +} +u8 rxgainerr5gma2 { + #desc + #help +} +u8 rxgainerr5gua0 { + #desc + #help +} +u8 rxgainerr5gua1 { + #desc + #help +} +u8 rxgainerr5gua2 { + #desc + #help +} +u8 rxgains2gelnagaina0 { + #desc + #help +} +u8 rxgains2gelnagaina1 { + #desc + #help +} +u8 rxgains2gelnagaina2 { + #desc + #help +} +u8 rxgains2gelnagaina3 { + #desc + #help +} +u8 rxgains2gtrelnabypa0 { + #desc + #help +} +u8 rxgains2gtrelnabypa1 { + #desc + #help +} +u8 rxgains2gtrelnabypa2 { + #desc + #help +} +u8 rxgains2gtrelnabypa3 { + #desc + #help +} +u8 rxgains2gtrisoa0 { + #desc + #help +} +u8 rxgains2gtrisoa1 { + #desc + #help +} +u8 rxgains2gtrisoa2 { + #desc + #help +} +u8 rxgains2gtrisoa3 { + #desc + #help +} +u8 rxgains5gelnagaina0 { + #desc + #help +} +u8 rxgains5gelnagaina1 { + #desc + #help +} +u8 rxgains5gelnagaina2 { + #desc + #help +} +u8 rxgains5gelnagaina3 { + #desc + #help +} +u8 rxgains5ghelnagaina0 { + #desc + #help +} +u8 rxgains5ghelnagaina1 { + #desc + #help +} +u8 rxgains5ghelnagaina2 { + #desc + #help +} +u8 rxgains5ghelnagaina3 { + #desc + #help +} +u8 rxgains5ghtrelnabypa0 { + #desc + #help +} +u8 rxgains5ghtrelnabypa1 { + #desc + #help +} +u8 rxgains5ghtrelnabypa2 { + #desc + #help +} +u8 rxgains5ghtrelnabypa3 { + #desc + #help +} +u8 rxgains5ghtrisoa0 { + #desc + #help +} +u8 rxgains5ghtrisoa1 { + #desc + #help +} +u8 rxgains5ghtrisoa2 { + #desc + #help +} +u8 rxgains5ghtrisoa3 { + #desc + #help +} +u8 rxgains5gmelnagaina0 { + #desc + #help +} +u8 rxgains5gmelnagaina1 { + #desc + #help +} +u8 rxgains5gmelnagaina2 { + #desc + #help +} +u8 rxgains5gmelnagaina3 { + #desc + #help +} +u8 rxgains5gmtrelnabypa0 { + #desc + #help +} +u8 rxgains5gmtrelnabypa1 { + #desc + #help +} +u8 rxgains5gmtrelnabypa2 { + #desc + #help +} +u8 rxgains5gmtrelnabypa3 { + #desc + #help +} +u8 rxgains5gmtrisoa0 { + #desc + #help +} +u8 rxgains5gmtrisoa1 { + #desc + #help +} +u8 rxgains5gmtrisoa2 { + #desc + #help +} +u8 rxgains5gmtrisoa3 { + #desc + #help +} +u8 rxgains5gtrelnabypa0 { + #desc + #help +} +u8 rxgains5gtrelnabypa1 { + #desc + #help +} +u8 rxgains5gtrelnabypa2 { + #desc + #help +} +u8 rxgains5gtrelnabypa3 { + #desc + #help +} +u8 rxgains5gtrisoa0 { + #desc + #help +} +u8 rxgains5gtrisoa1 { + #desc + #help +} +u8 rxgains5gtrisoa2 { + #desc + #help +} +u8 rxgains5gtrisoa3 { + #desc + #help +} +i8 rxpo2g { + fmt decimal + #desc + #help +} +i8 rxpo5g { + fmt decimal + #desc + #help +} +u8 sar2g { + #desc + #help +} +u8 sar5g { + #desc + #help +} +u16 sb20in40hrpo { + #desc + #help +} +u16 sb20in40lrpo { + #desc + #help +} +u16 sb20in80and160hr5ghpo { + #desc + #help +} +u16 sb20in80and160hr5glpo { + #desc + #help +} +u16 sb20in80and160hr5gmpo { + #desc + #help +} +u16 sb20in80and160lr5ghpo { + #desc + #help +} +u16 sb20in80and160lr5glpo { + #desc + #help +} +u16 sb20in80and160lr5gmpo { + #desc + #help +} +u16 sb40and80hr5ghpo { + #desc + #help +} +u16 sb40and80hr5glpo { + #desc + #help +} +u16 sb40and80hr5gmpo { + #desc + #help +} +u16 sb40and80lr5ghpo { + #desc + #help +} +u16 sb40and80lr5glpo { + #desc + #help +} +u16 sb40and80lr5gmpo { + #desc + #help +} +u8 sromrev { + #desc + #help +} +u16 stbcpo { + #desc + #help +} +u16 subband5gver { + #desc + #help +} +u16 subvid { + #desc + #help +} +u32 swctrlmap_2g[5] { + #desc + #help +} +u8 tempcorrx { + #desc + #help +} +u8 tempoffset { + fmt decimal + #desc + #help +} +u8 temps_hysteresis { + fmt decimal + #desc + #help +} +u8 temps_period { + fmt decimal + #desc + #help +} +u8 tempsense_option { + #desc + #help +} +u8 tempsense_slope { + #desc + #help +} +u8 tempthresh { + #desc + #help +} +u8 tri2g { + #desc + #help +} +u8 tri5g { + #desc + #help +} +u8 tri5gh { + #desc + #help +} +u8 tri5gl { + #desc + #help +} +u8 triso2g { + #desc + #help +} +u8 triso5g { + #desc + #help +} +u16 tssifloor2g { + #desc + #help +} +u16 tssifloor5g[4] { + #desc + #help +} +u8 tssipos2g { + #desc + #help +} +u8 tssipos5g { + #desc + #help +} +u8 tssiposslope2g { + fmt decimal + #desc + #help +} +u8 tssiposslope5g { + fmt decimal + #desc + #help +} +u8 tworangetssi2g { + fmt decimal + #desc + #help +} +u8 tworangetssi5g { + fmt decimal + #desc + #help +} +u8 txidxcap2g { + #desc + #help +} +u8 txidxcap5g { + #desc + #help +} +u8 txpid2ga0 { + #desc + #help +} +u8 txpid2ga1 { + #desc + #help +} +u8 txpid2ga2 { + #desc + #help +} +u8 txpid2ga3 { + #desc + #help +} +u8 txpid5ga0 { + #desc + #help +} +u8 txpid5ga1 { + #desc + #help +} +u8 txpid5ga2 { + #desc + #help +} +u8 txpid5ga3 { + #desc + #help +} +u8 txpid5gha0 { + #desc + #help +} +u8 txpid5gha1 { + #desc + #help +} +u8 txpid5gha2 { + #desc + #help +} +u8 txpid5gha3 { + #desc + #help +} +u8 txpid5gla0 { + #desc + #help +} +u8 txpid5gla1 { + #desc + #help +} +u8 txpid5gla2 { + #desc + #help +} +u8 txpid5gla3 { + #desc + #help +} +u32 xtalfreq { + fmt decimal + #desc + #help +} + +srom 1 { + 0x048: u8 il0macaddr[6] { +0x1, +0x0, +0x3, +0x2, +0x5, +0x4 } + 0x04C: u16 boardnum + 0x054: u8 et1macaddr[6] { +0x1, +0x0, +0x3, +0x2, +0x5, +0x4 } + 0x05C: u8 boardrev + 0x05D: u8 aa5g (&0xC0, >>6) + 0x05D: u8 cc (&0xF) + 0x05D: u8 aa2g (&0x30, >>4) + 0x05E: i16 pa0b0 + 0x060: i16 pa0b1 + 0x062: i16 pa0b2 + 0x064: u8 ledbh0 + 0x065: u8 ledbh1 + 0x066: u8 ledbh2 + 0x067: u8 ledbh3 + 0x068: u8 pa0maxpwr + 0x069: u8 pa1maxpwr + 0x06A: i16 pa1b0 + 0x06C: i16 pa1b1 + 0x06E: i16 pa1b2 + 0x070: u8 pa0itssit + 0x071: u8 pa1itssit + 0x072: u16 boardflags + 0x074: u8 ag0 + 0x075: u8 ag1 + 0x076: char ccode[2] { +0x1, +0x0 } + 0x07E: u8 sromrev + 0x07F: u8 +} + +srom 2-3 { + 0x004: u16 boardtype + 0x006: u16 subvid + 0x03A: u8 pa1himaxpwr + 0x03B: u8 pa1lomaxpwr + 0x03C: i16 pa1lob0 + 0x03E: i16 pa1lob1 + 0x040: i16 pa1lob2 + 0x042: i16 pa1hib0 + 0x044: i16 pa1hib1 + 0x046: i16 pa1hib2 + srom 2 { + 0x048: u8 il0macaddr[6] { + +0x1, +0x0, +0x3, +0x2, +0x5, +0x4 + } + 0x04C: u16 boardnum + 0x054: u8 et1macaddr[6] { + +0x1, +0x0, +0x3, +0x2, +0x5, +0x4 + } + } + srom 3 { + 0x04A: u8 macaddr[6] { + +0x1, +0x0, +0x3, +0x2, +0x5, +0x4 + } + 0x04E: u16 boardnum + 0x050: u8 rssismf2g (&0xF) + 0x050: u8 rssismc2g (&0xF0, >>4) + 0x051: u8 bxa2g (&0x18, >>3) + 0x051: u8 rssisav2g (&0x7) + 0x052: u8 rssismc5g (&0xF0, >>4) + 0x052: u8 rssismf5g (&0xF) + 0x053: u8 bxa5g (&0x18, >>3) + 0x053: u8 rssisav5g (&0x7) + 0x054: u8 tri2g + 0x055: u8 tri5g + 0x056: u8 tri5gl + 0x057: u8 tri5gh + 0x05A: i8 rxpo2g + 0x05B: i8 rxpo5g + } + 0x05C: u8 boardrev + 0x05D: u8 aa5g (&0xC0, >>6) + 0x05D: u8 aa2g (&0x30, >>4) + 0x05E: i16 pa0b0 + 0x060: i16 pa0b1 + 0x062: i16 pa0b2 + 0x064: u8 ledbh0 + 0x065: u8 ledbh1 + 0x066: u8 ledbh2 + 0x067: u8 ledbh3 + 0x068: u8 pa0maxpwr + 0x069: u8 pa1maxpwr + 0x06A: i16 pa1b0 + 0x06C: i16 pa1b1 + 0x06E: i16 pa1b2 + 0x070: u8 pa0itssit + 0x071: u8 pa1itssit + srom 2 { + 0x072: u32 boardflags { + +0x0: u16 | + 0x038: u16 (<<16) + } + } + srom 3 { + 0x072: u32 boardflags { + +0x0: u16 | + +0x8: u16 (<<16) + } + } + 0x074: u8 ag0 + 0x075: u8 ag1 + 0x076: char ccode[2] { +0x1, +0x0 } + 0x078: u8 opo + srom 3 { + 0x079: u8 regrev + 0x07C: u16 leddc + } + 0x07E: u8 sromrev + 0x07F: u8 +} + + +srom 4 { + 0x004: u16 boardtype + 0x006: u16 subvid + 0x040: u16 (=0x5372) + 0x042: u16 boardrev + 0x044: u32 boardflags + 0x048: u32 boardflags2 + 0x04C: u8 macaddr[6] { +0x1, +0x0, +0x3, +0x2, +0x5, +0x4 } + 0x050: u16 boardnum + 0x052: char ccode[2] { +0x1, +0x0 } + 0x054: u8 regrev + 0x056: u8 ledbh0 + 0x057: u8 ledbh1 + 0x058: u8 ledbh2 + 0x059: u8 ledbh3 + 0x05A: u16 leddc + 0x05C: u8 aa2g + 0x05D: u8 aa5g + 0x05E: u8 ag0 + 0x05F: u8 ag1 + 0x060: u8 ag2 + 0x061: u8 ag3 + 0x062: u8 txpid2ga0 + 0x063: u8 txpid2ga1 + 0x064: u8 txpid2ga2 + 0x065: u8 txpid2ga3 + 0x066: u8 txpid5ga0 + 0x067: u8 txpid5ga1 + 0x068: u8 txpid5ga2 + 0x069: u8 txpid5ga3 + 0x06A: u8 txpid5gla0 + 0x06B: u8 txpid5gla1 + 0x06C: u8 txpid5gla2 + 0x06D: u8 txpid5gla3 + 0x06E: u8 txpid5gha0 + 0x06F: u8 txpid5gha1 + 0x070: u8 txpid5gha2 + 0x071: u8 txpid5gha3 + 0x07A: u8 rxchain (&0xF0, >>4) + 0x07A: u8 txchain (&0xF) + 0x07B: u8 antswitch + 0x080: u8 maxp2ga0 + 0x081: u8 itt2ga0 + 0x082: u16 pa2gw0a0 + 0x084: u16 pa2gw1a0 + 0x086: u16 pa2gw2a0 + 0x088: u16 pa2gw3a0 + 0x08A: u8 maxp5ga0[1] + 0x08B: u8 itt5ga0 + 0x08C: u8 maxp5gha0 + 0x08D: u8 maxp5gla0 + 0x08E: u16 pa5gw0a0 + 0x090: u16 pa5gw1a0 + 0x092: u16 pa5gw2a0 + 0x094: u16 pa5gw3a0 + 0x096: u16 pa5glw0a0 + 0x098: u16 pa5glw1a0 + 0x09A: u16 pa5glw2a0 + 0x09C: u16 pa5glw3a0 + 0x09E: u16 pa5ghw0a0 + 0x0A0: u16 pa5ghw1a0 + 0x0A2: u16 pa5ghw2a0 + 0x0A4: u16 pa5ghw3a0 + 0x0AE: u8 maxp2ga1 + 0x0AF: u8 itt2ga1 + 0x0B0: u16 pa2gw0a1 + 0x0B2: u16 pa2gw1a1 + 0x0B4: u16 pa2gw2a1 + 0x0B6: u16 pa2gw3a1 + 0x0B8: u8 maxp5ga1[1] + 0x0B9: u8 itt5ga1 + 0x0BA: u8 maxp5gha1 + 0x0BB: u8 maxp5gla1 + 0x0BC: u16 pa5gw0a1 + 0x0BE: u16 pa5gw1a1 + 0x0C0: u16 pa5gw2a1 + 0x0C2: u16 pa5gw3a1 + 0x0C4: u16 pa5glw0a1 + 0x0C6: u16 pa5glw1a1 + 0x0C8: u16 pa5glw2a1 + 0x0CA: u16 pa5glw3a1 + 0x0CC: u16 pa5ghw0a1 + 0x0CE: u16 pa5ghw1a1 + 0x0D0: u16 pa5ghw2a1 + 0x0D2: u16 pa5ghw3a1 + 0x0DC: u8 maxp2ga2 + 0x0DD: u8 itt2ga2 + 0x0DE: u16 pa2gw0a2 + 0x0E0: u16 pa2gw1a2 + 0x0E2: u16 pa2gw2a2 + 0x0E4: u16 pa2gw3a2 + 0x0E6: u8 maxp5ga2[1] + 0x0E7: u8 itt5ga2 + 0x0E8: u8 maxp5gha2 + 0x0E9: u8 maxp5gla2 + 0x0EA: u16 pa5gw0a2 + 0x0EC: u16 pa5gw1a2 + 0x0EE: u16 pa5gw2a2 + 0x0F0: u16 pa5gw3a2 + 0x0F2: u16 pa5glw0a2 + 0x0F4: u16 pa5glw1a2 + 0x0F6: u16 pa5glw2a2 + 0x0F8: u16 pa5glw3a2 + 0x0FA: u16 pa5ghw0a2 + 0x0FC: u16 pa5ghw1a2 + 0x0FE: u16 pa5ghw2a2 + 0x100: u16 pa5ghw3a2 + 0x10A: u8 maxp2ga3 + 0x10B: u8 itt2ga3 + 0x10C: u16 pa2gw0a3 + 0x10E: u16 pa2gw1a3 + 0x110: u16 pa2gw2a3 + 0x112: u16 pa2gw3a3 + 0x114: u8 maxp5ga3[1] + 0x115: u8 itt5ga3 + 0x116: u8 maxp5gha3 + 0x117: u8 maxp5gla3 + 0x118: u16 pa5gw0a3 + 0x11A: u16 pa5gw1a3 + 0x11C: u16 pa5gw2a3 + 0x11E: u16 pa5gw3a3 + 0x120: u16 pa5glw0a3 + 0x122: u16 pa5glw1a3 + 0x124: u16 pa5glw2a3 + 0x126: u16 pa5glw3a3 + 0x128: u16 pa5ghw0a3 + 0x12A: u16 pa5ghw1a3 + 0x12C: u16 pa5ghw2a3 + 0x12E: u16 pa5ghw3a3 + 0x138: u16 cck2gpo + 0x13A: u32 ofdm2gpo + 0x13E: u32 ofdm5gpo + 0x142: u32 ofdm5glpo + 0x146: u32 ofdm5ghpo + 0x14A: u16 mcs2gpo0 + 0x14C: u16 mcs2gpo1 + 0x14E: u16 mcs2gpo2 + 0x150: u16 mcs2gpo3 + 0x152: u16 mcs2gpo4 + 0x154: u16 mcs2gpo5 + 0x156: u16 mcs2gpo6 + 0x158: u16 mcs2gpo7 + 0x15A: u16 mcs5gpo0 + 0x15C: u16 mcs5gpo1 + 0x15E: u16 mcs5gpo2 + 0x160: u16 mcs5gpo3 + 0x162: u16 mcs5gpo4 + 0x164: u16 mcs5gpo5 + 0x166: u16 mcs5gpo6 + 0x168: u16 mcs5gpo7 + 0x16A: u16 mcs5glpo0 + 0x16C: u16 mcs5glpo1 + 0x16E: u16 mcs5glpo2 + 0x170: u16 mcs5glpo3 + 0x172: u16 mcs5glpo4 + 0x174: u16 mcs5glpo5 + 0x176: u16 mcs5glpo6 + 0x178: u16 mcs5glpo7 + 0x17A: u16 mcs5ghpo0 + 0x17C: u16 mcs5ghpo1 + 0x17E: u16 mcs5ghpo2 + 0x180: u16 mcs5ghpo3 + 0x182: u16 mcs5ghpo4 + 0x184: u16 mcs5ghpo5 + 0x186: u16 mcs5ghpo6 + 0x188: u16 mcs5ghpo7 + 0x18A: u16 cddpo + 0x18C: u16 stbcpo + 0x18E: u16 bw40po + 0x190: u16 bwduppo + 0x1B6: u8 sromrev + 0x1B7: u8 +} + + +srom 5-7 { + 0x004: u16 boardtype + 0x006: u16 subvid + 0x042: u16 boardrev + 0x044: char ccode[2] { +0x1, +0x0 } + 0x046: u8 regrev + 0x04A: u32 boardflags + 0x04E: u32 boardflags2 + 0x052: u8 macaddr[6] { +0x1, +0x0, +0x3, +0x2, +0x5, +0x4 } + 0x056: u16 boardnum + 0x05A: u16 leddc + 0x05C: u8 aa2g + 0x05D: u8 aa5g + 0x05E: u8 ag0 + 0x05F: u8 ag1 + 0x060: u8 ag2 + 0x061: u8 ag3 + 0x062: u8 txpid2ga0 + 0x063: u8 txpid2ga1 + 0x064: u8 txpid2ga2 + 0x065: u8 txpid2ga3 + 0x066: u8 txpid5ga0 + 0x067: u8 txpid5ga1 + 0x068: u8 txpid5ga2 + 0x069: u8 txpid5ga3 + 0x06A: u8 txpid5gla0 + 0x06B: u8 txpid5gla1 + 0x06C: u8 txpid5gla2 + 0x06D: u8 txpid5gla3 + 0x06E: u8 txpid5gha0 + 0x06F: u8 txpid5gha1 + 0x070: u8 txpid5gha2 + 0x071: u8 txpid5gha3 + 0x076: u8 ledbh0 + 0x077: u8 ledbh1 + 0x078: u8 ledbh2 + 0x079: u8 ledbh3 + 0x07A: u8 txchain (&0xF) + 0x07A: u8 rxchain (&0xF0, >>4) + 0x07B: u8 antswitch + 0x080: u8 maxp2ga0 + 0x081: u8 itt2ga0 + 0x082: u16 pa2gw0a0 + 0x084: u16 pa2gw1a0 + 0x086: u16 pa2gw2a0 + 0x088: u16 pa2gw3a0 + 0x08A: u8 maxp5ga0[1] + 0x08B: u8 itt5ga0 + 0x08C: u8 maxp5gha0 + 0x08D: u8 maxp5gla0 + 0x08E: u16 pa5gw0a0 + 0x090: u16 pa5gw1a0 + 0x092: u16 pa5gw2a0 + 0x094: u16 pa5gw3a0 + 0x096: u16 pa5glw0a0 + 0x098: u16 pa5glw1a0 + 0x09A: u16 pa5glw2a0 + 0x09C: u16 pa5glw3a0 + 0x09E: u16 pa5ghw0a0 + 0x0A0: u16 pa5ghw1a0 + 0x0A2: u16 pa5ghw2a0 + 0x0A4: u16 pa5ghw3a0 + 0x0AE: u8 maxp2ga1 + 0x0AF: u8 itt2ga1 + 0x0B0: u16 pa2gw0a1 + 0x0B2: u16 pa2gw1a1 + 0x0B4: u16 pa2gw2a1 + 0x0B6: u16 pa2gw3a1 + 0x0B8: u8 maxp5ga1[1] + 0x0B9: u8 itt5ga1 + 0x0BA: u8 maxp5gha1 + 0x0BB: u8 maxp5gla1 + 0x0BC: u16 pa5gw0a1 + 0x0BE: u16 pa5gw1a1 + 0x0C0: u16 pa5gw2a1 + 0x0C2: u16 pa5gw3a1 + 0x0C4: u16 pa5glw0a1 + 0x0C6: u16 pa5glw1a1 + 0x0C8: u16 pa5glw2a1 + 0x0CA: u16 pa5glw3a1 + 0x0CC: u16 pa5ghw0a1 + 0x0CE: u16 pa5ghw1a1 + 0x0D0: u16 pa5ghw2a1 + 0x0D2: u16 pa5ghw3a1 + 0x0DC: u8 maxp2ga2 + 0x0DD: u8 itt2ga2 + 0x0DE: u16 pa2gw0a2 + 0x0E0: u16 pa2gw1a2 + 0x0E2: u16 pa2gw2a2 + 0x0E4: u16 pa2gw3a2 + 0x0E6: u8 maxp5ga2[1] + 0x0E7: u8 itt5ga2 + 0x0E8: u8 maxp5gha2 + 0x0E9: u8 maxp5gla2 + 0x0EA: u16 pa5gw0a2 + 0x0EC: u16 pa5gw1a2 + 0x0EE: u16 pa5gw2a2 + 0x0F0: u16 pa5gw3a2 + 0x0F2: u16 pa5glw0a2 + 0x0F4: u16 pa5glw1a2 + 0x0F6: u16 pa5glw2a2 + 0x0F8: u16 pa5glw3a2 + 0x0FA: u16 pa5ghw0a2 + 0x0FC: u16 pa5ghw1a2 + 0x0FE: u16 pa5ghw2a2 + 0x100: u16 pa5ghw3a2 + 0x10A: u8 maxp2ga3 + 0x10B: u8 itt2ga3 + 0x10C: u16 pa2gw0a3 + 0x10E: u16 pa2gw1a3 + 0x110: u16 pa2gw2a3 + 0x112: u16 pa2gw3a3 + 0x114: u8 maxp5ga3[1] + 0x115: u8 itt5ga3 + 0x116: u8 maxp5gha3 + 0x117: u8 maxp5gla3 + 0x118: u16 pa5gw0a3 + 0x11A: u16 pa5gw1a3 + 0x11C: u16 pa5gw2a3 + 0x11E: u16 pa5gw3a3 + 0x120: u16 pa5glw0a3 + 0x122: u16 pa5glw1a3 + 0x124: u16 pa5glw2a3 + 0x126: u16 pa5glw3a3 + 0x128: u16 pa5ghw0a3 + 0x12A: u16 pa5ghw1a3 + 0x12C: u16 pa5ghw2a3 + 0x12E: u16 pa5ghw3a3 + 0x138: u16 cck2gpo + 0x13A: u32 ofdm2gpo + 0x13E: u32 ofdm5gpo + 0x142: u32 ofdm5glpo + 0x146: u32 ofdm5ghpo + 0x14A: u16 mcs2gpo0 + 0x14C: u16 mcs2gpo1 + 0x14E: u16 mcs2gpo2 + 0x150: u16 mcs2gpo3 + 0x152: u16 mcs2gpo4 + 0x154: u16 mcs2gpo5 + 0x156: u16 mcs2gpo6 + 0x158: u16 mcs2gpo7 + 0x15A: u16 mcs5gpo0 + 0x15C: u16 mcs5gpo1 + 0x15E: u16 mcs5gpo2 + 0x160: u16 mcs5gpo3 + 0x162: u16 mcs5gpo4 + 0x164: u16 mcs5gpo5 + 0x166: u16 mcs5gpo6 + 0x168: u16 mcs5gpo7 + 0x16A: u16 mcs5glpo0 + 0x16C: u16 mcs5glpo1 + 0x16E: u16 mcs5glpo2 + 0x170: u16 mcs5glpo3 + 0x172: u16 mcs5glpo4 + 0x174: u16 mcs5glpo5 + 0x176: u16 mcs5glpo6 + 0x178: u16 mcs5glpo7 + 0x17A: u16 mcs5ghpo0 + 0x17C: u16 mcs5ghpo1 + 0x17E: u16 mcs5ghpo2 + 0x180: u16 mcs5ghpo3 + 0x182: u16 mcs5ghpo4 + 0x184: u16 mcs5ghpo5 + 0x186: u16 mcs5ghpo6 + 0x188: u16 mcs5ghpo7 + 0x18A: u16 cddpo + 0x18C: u16 stbcpo + 0x18E: u16 bw40po + 0x190: u16 bwduppo + 0x1B6: u8 sromrev + 0x1B7: u8 +} + + +srom 8 { + 0x004: u16 boardtype + 0x006: u16 subvid + 0x060: u16 devid + 0x080: u16 (=0x5372) + 0x082: u16 boardrev + 0x084: u32 boardflags + 0x088: u32 boardflags2 + 0x08C: u8 macaddr[6] { +0x1, +0x0, +0x3, +0x2, +0x5, +0x4 } + 0x090: u16 boardnum + 0x092: char ccode[2] { +0x1, +0x0 } + 0x094: u8 regrev + 0x096: u8 ledbh0 + 0x097: u8 ledbh1 + 0x098: u8 ledbh2 + 0x099: u8 ledbh3 + 0x09A: u16 leddc + 0x09C: u8 aa2g + 0x09D: u8 aa5g + 0x09E: u8 ag0 + 0x09F: u8 ag1 + 0x0A0: u8 ag2 + 0x0A1: u8 ag3 + 0x0A2: u8 txchain (&0xF) + 0x0A2: u8 rxchain (&0xF0, >>4) + 0x0A3: u8 antswitch + 0x0A4: u8 rssismf2g (&0xF) + 0x0A4: u8 rssismc2g (&0xF0, >>4) + 0x0A5: u8 bxa2g (&0x18, >>3) + 0x0A5: u8 rssisav2g (&0x7) + 0x0A6: u8 rssismc5g (&0xF0, >>4) + 0x0A6: u8 rssismf5g (&0xF) + 0x0A7: u8 bxa5g (&0x18, >>3) + 0x0A7: u8 rssisav5g (&0x7) + 0x0A8: u8 tri2g + 0x0A9: u8 tri5g + 0x0AA: u8 tri5gl + 0x0AB: u8 tri5gh + 0x0AC: i8 rxpo2g + 0x0AD: i8 rxpo5g + 0x0AE: u8 tssipos2g (&0x1) + 0x0AE: u8 pdetrange2g (&0xF8, >>3) + 0x0AE: u8 extpagain2g (&0x6, >>1) + 0x0AF: u8 antswctl2g (&0xF8, >>3) + 0x0AF: u8 triso2g (&0x7) + 0x0B0: u8 pdetrange5g (&0xF8, >>3) + 0x0B0: u8 tssipos5g (&0x1) + 0x0B0: u8 extpagain5g (&0x6, >>1) + 0x0B1: u8 triso5g (&0x7) + 0x0B1: u8 antswctl5g (&0xF8, >>3) + 0x0B2: u8 tempoffset + 0x0B3: u8 tempthresh + 0x0B4: u16 rawtempsense (&0x1FF) + 0x0B5: u8 measpower (&0xFE, >>1) + 0x0B6: u8 tempsense_slope + 0x0B7: u8 tempsense_option (&0x3) + 0x0B7: u8 tempcorrx (&0xFC, >>2) + 0x0B8: u8 hw_iqcal_en (&0x20, >>5) + 0x0B8: u8 freqoffset_corr (&0xF) + 0x0B8: u8 iqcal_swp_dis (&0x10, >>4) + 0x0BA: u8 elna2g + 0x0BB: u8 elna5g + 0x0BC: u8 phycal_tempdelta + 0x0BD: u8 temps_period (&0xF) + 0x0BD: u8 temps_hysteresis (&0xF0, >>4) + 0x0BE: u8 measpower1 (&0x7F) + 0x0BE: u8 measpower2 { + +0x0: u16 (&0x3F80, >>7) + } + 0x0C0: u8 pa0maxpwr + 0x0C0: u8 maxp2ga0 + 0x0C1: u8 pa0itssit + 0x0C1: u8 itt2ga0 + 0x0C2: i16 pa0b0 + 0x0C2: u16 pa2gw0a0 + 0x0C4: u16 pa2gw1a0 + 0x0C4: i16 pa0b1 + 0x0C6: i16 pa0b2 + 0x0C6: u16 pa2gw2a0 + 0x0C8: u8 pa1maxpwr + 0x0C8: u8 maxp5ga0[1] + 0x0C9: u8 itt5ga0 + 0x0C9: u8 pa1itssit + 0x0CA: u8 maxp5gha0 + 0x0CA: u8 pa1himaxpwr + 0x0CB: u8 maxp5gla0 + 0x0CB: u8 pa1lomaxpwr + 0x0CC: u16 pa5gw0a0 + 0x0CC: i16 pa1b0 + 0x0CE: i16 pa1b1 + 0x0CE: u16 pa5gw1a0 + 0x0D0: i16 pa1b2 + 0x0D0: u16 pa5gw2a0 + 0x0D2: i16 pa1lob0 + 0x0D2: u16 pa5glw0a0 + 0x0D4: u16 pa5glw1a0 + 0x0D4: i16 pa1lob1 + 0x0D6: u16 pa5glw2a0 + 0x0D6: i16 pa1lob2 + 0x0D8: i16 pa1hib0 + 0x0D8: u16 pa5ghw0a0 + 0x0DA: i16 pa1hib1 + 0x0DA: u16 pa5ghw1a0 + 0x0DC: i16 pa1hib2 + 0x0DC: u16 pa5ghw2a0 + 0x0E0: u8 maxp2ga1 + 0x0E1: u8 itt2ga1 + 0x0E2: u16 pa2gw0a1 + 0x0E4: u16 pa2gw1a1 + 0x0E6: u16 pa2gw2a1 + 0x0E8: u8 maxp5ga1[1] + 0x0E9: u8 itt5ga1 + 0x0EA: u8 maxp5gha1 + 0x0EB: u8 maxp5gla1 + 0x0EC: u16 pa5gw0a1 + 0x0EE: u16 pa5gw1a1 + 0x0F0: u16 pa5gw2a1 + 0x0F2: u16 pa5glw0a1 + 0x0F4: u16 pa5glw1a1 + 0x0F6: u16 pa5glw2a1 + 0x0F8: u16 pa5ghw0a1 + 0x0FA: u16 pa5ghw1a1 + 0x0FC: u16 pa5ghw2a1 + 0x100: u8 maxp2ga2 + 0x101: u8 itt2ga2 + 0x102: u16 pa2gw0a2 + 0x104: u16 pa2gw1a2 + 0x106: u16 pa2gw2a2 + 0x108: u8 maxp5ga2[1] + 0x109: u8 itt5ga2 + 0x10A: u8 maxp5gha2 + 0x10B: u8 maxp5gla2 + 0x10C: u16 pa5gw0a2 + 0x10E: u16 pa5gw1a2 + 0x110: u16 pa5gw2a2 + 0x112: u16 pa5glw0a2 + 0x114: u16 pa5glw1a2 + 0x116: u16 pa5glw2a2 + 0x118: u16 pa5ghw0a2 + 0x11A: u16 pa5ghw1a2 + 0x11C: u16 pa5ghw2a2 + 0x120: u8 maxp2ga3 + 0x121: u8 itt2ga3 + 0x122: u16 pa2gw0a3 + 0x124: u16 pa2gw1a3 + 0x126: u16 pa2gw2a3 + 0x128: u8 maxp5ga3[1] + 0x129: u8 itt5ga3 + 0x12A: u8 maxp5gha3 + 0x12B: u8 maxp5gla3 + 0x12C: u16 pa5gw0a3 + 0x12E: u16 pa5gw1a3 + 0x130: u16 pa5gw2a3 + 0x132: u16 pa5glw0a3 + 0x134: u16 pa5glw1a3 + 0x136: u16 pa5glw2a3 + 0x138: u16 pa5ghw0a3 + 0x13A: u16 pa5ghw1a3 + 0x13C: u16 pa5ghw2a3 + 0x140: u16 cck2gpo + 0x142: u32 ofdm2gpo + 0x142: u8 opo + 0x146: u32 ofdm5gpo + 0x14A: u32 ofdm5glpo + 0x14E: u32 ofdm5ghpo + 0x152: u16 mcs2gpo0 + 0x154: u16 mcs2gpo1 + 0x156: u16 mcs2gpo2 + 0x158: u16 mcs2gpo3 + 0x15A: u16 mcs2gpo4 + 0x15C: u16 mcs2gpo5 + 0x15E: u16 mcs2gpo6 + 0x160: u16 mcs2gpo7 + 0x162: u16 mcs5gpo0 + 0x164: u16 mcs5gpo1 + 0x166: u16 mcs5gpo2 + 0x168: u16 mcs5gpo3 + 0x16A: u16 mcs5gpo4 + 0x16C: u16 mcs5gpo5 + 0x16E: u16 mcs5gpo6 + 0x170: u16 mcs5gpo7 + 0x172: u16 mcs5glpo0 + 0x174: u16 mcs5glpo1 + 0x176: u16 mcs5glpo2 + 0x178: u16 mcs5glpo3 + 0x17A: u16 mcs5glpo4 + 0x17C: u16 mcs5glpo5 + 0x17E: u16 mcs5glpo6 + 0x180: u16 mcs5glpo7 + 0x182: u16 mcs5ghpo0 + 0x184: u16 mcs5ghpo1 + 0x186: u16 mcs5ghpo2 + 0x188: u16 mcs5ghpo3 + 0x18A: u16 mcs5ghpo4 + 0x18C: u16 mcs5ghpo5 + 0x18E: u16 mcs5ghpo6 + 0x190: u16 mcs5ghpo7 + 0x192: u16 cddpo + 0x194: u16 stbcpo + 0x196: u16 bw40po + 0x198: u16 bwduppo + 0x19A: u8 rxgainerr2ga0 (&0x3F) + 0x19A: u8 rxgainerr2ga1 { + +0x0: u16 (&0x7C0, >>6) + } + 0x19B: u8 rxgainerr2ga2 (&0xF8, >>3) + 0x19C: u8 rxgainerr5gla0 (&0x3F) + 0x19C: u8 rxgainerr5gla1 { + +0x0: u16 (&0x7C0, >>6) + } + 0x19D: u8 rxgainerr5gla2 (&0xF8, >>3) + 0x19E: u8 rxgainerr5gma0 (&0x3F) + 0x19E: u8 rxgainerr5gma1 { + +0x0: u16 (&0x7C0, >>6) + } + 0x19F: u8 rxgainerr5gma2 (&0xF8, >>3) + 0x1A0: u8 rxgainerr5gha1 { + +0x0: u16 (&0x7C0, >>6) + } + 0x1A0: u8 rxgainerr5gha0 (&0x3F) + 0x1A1: u8 rxgainerr5gha2 (&0xF8, >>3) + 0x1A2: u8 rxgainerr5gua1 { + +0x0: u16 (&0x7C0, >>6) + } + 0x1A2: u8 rxgainerr5gua0 (&0x3F) + 0x1A3: u8 rxgainerr5gua2 (&0xF8, >>3) + 0x1A4: u8 subband5gver (&0x7) + 0x1A6: u8 pcieingress_war (&0xF) + 0x1A8: u8 eu_edthresh2g + 0x1A9: u8 eu_edthresh5g + 0x1AA: u8 noiselvl2ga0 (&0x1F) + 0x1AA: u8 noiselvl2ga1 { + +0x0: u16 (&0x3E0, >>5) + } + 0x1AB: u8 noiselvl2ga2 (&0x7C, >>2) + 0x1AC: u8 noiselvl5gla1 { + +0x0: u16 (&0x3E0, >>5) + } + 0x1AC: u8 noiselvl5gla0 (&0x1F) + 0x1AD: u8 noiselvl5gla2 (&0x7C, >>2) + 0x1AE: u8 noiselvl5gma0 (&0x1F) + 0x1AE: u8 noiselvl5gma1 { + +0x0: u16 (&0x3E0, >>5) + } + 0x1AF: u8 noiselvl5gma2 (&0x7C, >>2) + 0x1B0: u8 noiselvl5gha0 (&0x1F) + 0x1B0: u8 noiselvl5gha1 { + +0x0: u16 (&0x3E0, >>5) + } + 0x1B1: u8 noiselvl5gha2 (&0x7C, >>2) + 0x1B2: u8 noiselvl5gua0 (&0x1F) + 0x1B2: u8 noiselvl5gua1 { + +0x0: u16 (&0x3E0, >>5) + } + 0x1B3: u8 noiselvl5gua2 (&0x7C, >>2) + 0x1B4: u8 noisecaloffset + 0x1B5: u8 noisecaloffset5g + 0x1B6: u8 sromrev + 0x1B7: u8 +} + + +srom 9-10 { + 0x004: u16 boardtype + 0x006: u16 subvid + 0x060: u16 devid + srom 9 { + 0x080: u16 (=0x5372) + } + 0x082: u16 boardrev + 0x084: u32 boardflags + 0x088: u32 boardflags2 + 0x08C: u8 macaddr[6] { +0x1, +0x0, +0x3, +0x2, +0x5, +0x4 } + 0x090: u16 boardnum + 0x092: char ccode[2] { +0x1, +0x0 } + 0x094: u8 regrev + 0x096: u8 ledbh0 + 0x097: u8 ledbh1 + 0x098: u8 ledbh2 + 0x099: u8 ledbh3 + 0x09A: u16 leddc + 0x09C: u8 aa2g + 0x09D: u8 aa5g + 0x09E: u8 ag0 + 0x09F: u8 ag1 + 0x0A0: u8 ag2 + 0x0A1: u8 ag3 + 0x0A2: u8 txchain (&0xF) + 0x0A2: u8 rxchain (&0xF0, >>4) + 0x0A3: u8 antswitch + 0x0A4: u8 rssismf2g (&0xF) + 0x0A4: u8 rssismc2g (&0xF0, >>4) + 0x0A5: u8 rssisav2g (&0x7) + 0x0A5: u8 bxa2g (&0x18, >>3) + 0x0A6: u8 rssismf5g (&0xF) + 0x0A6: u8 rssismc5g (&0xF0, >>4) + 0x0A7: u8 bxa5g (&0x18, >>3) + 0x0A7: u8 rssisav5g (&0x7) + 0x0A8: u8 tri2g + 0x0A9: u8 tri5g + 0x0AA: u8 tri5gl + 0x0AB: u8 tri5gh + 0x0AC: i8 rxpo2g + 0x0AD: i8 rxpo5g + 0x0AE: u8 extpagain2g (&0x6, >>1) + 0x0AE: u8 tssipos2g (&0x1) + 0x0AE: u8 pdetrange2g (&0xF8, >>3) + 0x0AF: u8 antswctl2g (&0xF8, >>3) + 0x0AF: u8 triso2g (&0x7) + 0x0B0: u8 extpagain5g (&0x6, >>1) + 0x0B0: u8 pdetrange5g (&0xF8, >>3) + 0x0B0: u8 tssipos5g (&0x1) + 0x0B1: u8 triso5g (&0x7) + 0x0B1: u8 antswctl5g (&0xF8, >>3) + 0x0B2: u8 tempoffset + 0x0B3: u8 tempthresh + 0x0B4: u16 rawtempsense (&0x1FF) + 0x0B5: u8 measpower (&0xFE, >>1) + 0x0B6: u8 tempsense_slope + 0x0B7: u8 tempsense_option (&0x3) + 0x0B7: u8 tempcorrx (&0xFC, >>2) + 0x0B8: u8 iqcal_swp_dis (&0x10, >>4) + 0x0B8: u8 freqoffset_corr (&0xF) + 0x0B8: u8 hw_iqcal_en (&0x20, >>5) + 0x0BA: u8 elna2g + 0x0BB: u8 elna5g + 0x0BC: u8 phycal_tempdelta + 0x0BD: u8 temps_hysteresis (&0xF0, >>4) + 0x0BD: u8 temps_period (&0xF) + 0x0BE: u8 measpower2 { + +0x0: u16 (&0x3F80, >>7) + } + 0x0BE: u8 measpower1 (&0x7F) + 0x0C0: u8 pa0maxpwr + 0x0C0: u8 maxp2ga0 + 0x0C1: u8 itt2ga0 + 0x0C1: u8 pa0itssit + 0x0C2: u16 pa2gw0a0 + 0x0C2: i16 pa0b0 + 0x0C4: i16 pa0b1 + 0x0C4: u16 pa2gw1a0 + 0x0C6: u16 pa2gw2a0 + 0x0C6: i16 pa0b2 + 0x0C8: u8 pa1maxpwr + 0x0C8: u8 maxp5ga0[1] + 0x0C9: u8 itt5ga0 + 0x0C9: u8 pa1itssit + 0x0CA: u8 pa1himaxpwr + 0x0CA: u8 maxp5gha0 + 0x0CB: u8 maxp5gla0 + 0x0CB: u8 pa1lomaxpwr + 0x0CC: i16 pa1b0 + 0x0CC: u16 pa5gw0a0 + 0x0CE: u16 pa5gw1a0 + 0x0CE: i16 pa1b1 + 0x0D0: i16 pa1b2 + 0x0D0: u16 pa5gw2a0 + 0x0D2: u16 pa5glw0a0 + 0x0D2: i16 pa1lob0 + 0x0D4: u16 pa5glw1a0 + 0x0D4: i16 pa1lob1 + 0x0D6: u16 pa5glw2a0 + 0x0D6: i16 pa1lob2 + 0x0D8: i16 pa1hib0 + 0x0D8: u16 pa5ghw0a0 + 0x0DA: u16 pa5ghw1a0 + 0x0DA: i16 pa1hib1 + 0x0DC: u16 pa5ghw2a0 + 0x0DC: i16 pa1hib2 + 0x0E0: u8 maxp2ga1 + 0x0E1: u8 itt2ga1 + 0x0E2: u16 pa2gw0a1 + 0x0E4: u16 pa2gw1a1 + 0x0E6: u16 pa2gw2a1 + 0x0E8: u8 maxp5ga1[1] + 0x0E9: u8 itt5ga1 + 0x0EA: u8 maxp5gha1 + 0x0EB: u8 maxp5gla1 + 0x0EC: u16 pa5gw0a1 + 0x0EE: u16 pa5gw1a1 + 0x0F0: u16 pa5gw2a1 + 0x0F2: u16 pa5glw0a1 + 0x0F4: u16 pa5glw1a1 + 0x0F6: u16 pa5glw2a1 + 0x0F8: u16 pa5ghw0a1 + 0x0FA: u16 pa5ghw1a1 + 0x0FC: u16 pa5ghw2a1 + 0x100: u8 maxp2ga2 + 0x101: u8 itt2ga2 + 0x102: u16 pa2gw0a2 + 0x104: u16 pa2gw1a2 + 0x106: u16 pa2gw2a2 + 0x108: u8 maxp5ga2[1] + 0x109: u8 itt5ga2 + 0x10A: u8 maxp5gha2 + 0x10B: u8 maxp5gla2 + 0x10C: u16 pa5gw0a2 + 0x10E: u16 pa5gw1a2 + 0x110: u16 pa5gw2a2 + 0x112: u16 pa5glw0a2 + 0x114: u16 pa5glw1a2 + 0x116: u16 pa5glw2a2 + 0x118: u16 pa5ghw0a2 + 0x11A: u16 pa5ghw1a2 + 0x11C: u16 pa5ghw2a2 + 0x120: u8 maxp2ga3 + 0x121: u8 itt2ga3 + 0x122: u16 pa2gw0a3 + 0x124: u16 pa2gw1a3 + 0x126: u16 pa2gw2a3 + 0x128: u8 maxp5ga3[1] + 0x129: u8 itt5ga3 + 0x12A: u8 maxp5gha3 + 0x12B: u8 maxp5gla3 + 0x12C: u16 pa5gw0a3 + 0x12E: u16 pa5gw1a3 + 0x130: u16 pa5gw2a3 + 0x132: u16 pa5glw0a3 + 0x134: u16 pa5glw1a3 + 0x136: u16 pa5glw2a3 + 0x138: u16 pa5ghw0a3 + 0x13A: u16 pa5ghw1a3 + 0x13C: u16 pa5ghw2a3 + 0x140: u16 cckbw202gpo + 0x142: u8 opo + 0x142: u16 cckbw20ul2gpo + 0x144: u32 legofdmbw202gpo + 0x148: u32 legofdmbw20ul2gpo + 0x14C: u32 legofdmbw205glpo + 0x150: u32 legofdmbw20ul5glpo + 0x154: u32 legofdmbw205gmpo + 0x158: u32 legofdmbw20ul5gmpo + 0x15C: u32 legofdmbw205ghpo + 0x160: u32 legofdmbw20ul5ghpo + 0x164: u32 mcsbw202gpo + 0x168: u32 mcsbw20ul2gpo + 0x16C: u32 mcsbw402gpo + 0x170: u32 mcsbw205glpo + 0x174: u32 mcsbw20ul5glpo + 0x178: u32 mcsbw405glpo + 0x17C: u32 mcsbw205gmpo + 0x180: u32 mcsbw20ul5gmpo + 0x184: u32 mcsbw405gmpo + 0x188: u32 mcsbw205ghpo + 0x18C: u32 mcsbw20ul5ghpo + 0x190: u32 mcsbw405ghpo + 0x194: u16 mcs32po + 0x196: u16 legofdm40duppo + 0x198: u8 eu_edthresh2g + 0x199: u8 eu_edthresh5g + 0x19A: u8 rxgainerr2ga0 (&0x3F) + 0x19A: u8 rxgainerr2ga1 { + +0x0: u16 (&0x7C0, >>6) + } + 0x19B: u8 rxgainerr2ga2 (&0xF8, >>3) + 0x19C: u8 rxgainerr5gla0 (&0x3F) + 0x19C: u8 rxgainerr5gla1 { + +0x0: u16 (&0x7C0, >>6) + } + 0x19D: u8 rxgainerr5gla2 (&0xF8, >>3) + 0x19E: u8 rxgainerr5gma0 (&0x3F) + 0x19E: u8 rxgainerr5gma1 { + +0x0: u16 (&0x7C0, >>6) + } + 0x19F: u8 rxgainerr5gma2 (&0xF8, >>3) + 0x1A0: u8 rxgainerr5gha0 (&0x3F) + 0x1A0: u8 rxgainerr5gha1 { + +0x0: u16 (&0x7C0, >>6) + } + 0x1A1: u8 rxgainerr5gha2 (&0xF8, >>3) + 0x1A2: u8 rxgainerr5gua0 (&0x3F) + 0x1A2: u8 rxgainerr5gua1 { + +0x0: u16 (&0x7C0, >>6) + } + 0x1A3: u8 rxgainerr5gua2 (&0xF8, >>3) + 0x1A4: u8 subband5gver (&0x7) + 0x1A6: u8 pcieingress_war (&0xF) + 0x1A8: u8 sar2g + 0x1A9: u8 sar5g + 0x1AA: u8 noiselvl2ga1 { + +0x0: u16 (&0x3E0, >>5) + } + 0x1AA: u8 noiselvl2ga0 (&0x1F) + 0x1AB: u8 noiselvl2ga2 (&0x7C, >>2) + 0x1AC: u8 noiselvl5gla0 (&0x1F) + 0x1AC: u8 noiselvl5gla1 { + +0x0: u16 (&0x3E0, >>5) + } + 0x1AD: u8 noiselvl5gla2 (&0x7C, >>2) + 0x1AE: u8 noiselvl5gma1 { + +0x0: u16 (&0x3E0, >>5) + } + 0x1AE: u8 noiselvl5gma0 (&0x1F) + 0x1AF: u8 noiselvl5gma2 (&0x7C, >>2) + 0x1B0: u8 noiselvl5gha0 (&0x1F) + 0x1B0: u8 noiselvl5gha1 { + +0x0: u16 (&0x3E0, >>5) + } + 0x1B1: u8 noiselvl5gha2 (&0x7C, >>2) + 0x1B2: u8 noiselvl5gua0 (&0x1F) + 0x1B2: u8 noiselvl5gua1 { + +0x0: u16 (&0x3E0, >>5) + } + 0x1B3: u8 noiselvl5gua2 (&0x7C, >>2) + srom 9 { + 0x1B4: u8 noisecaloffset + 0x1B5: u8 noisecaloffset5g + 0x1B6: u8 sromrev + 0x1B7: u8 + } + srom 10 { + 0x1B4: u16 cckPwrOffset + 0x1B6: u16 (=0x5372) + 0x1B8: u32 swctrlmap_2g[5] { + +0x0: u32[4], + +0x10: u16 + } + 0x1CA: u8 sromrev + 0x1CB: u8 + } +} + + +srom 11 { + 0x004: u16 boardtype + 0x006: u16 subvid + 0x060: u16 devid + 0x080: u16 (=0x0634) + 0x082: u16 boardrev + 0x084: u32 boardflags + 0x088: u32 boardflags2 + 0x08C: u32 boardflags3 + 0x090: u8 macaddr[6] { +0x1, +0x0, +0x3, +0x2, +0x5, +0x4 } + 0x094: u16 boardnum + 0x096: char ccode[2] { +0x1, +0x0 } + 0x098: u8 regrev + 0x09A: u8 ledbh0 + 0x09B: u8 ledbh1 + 0x09C: u8 ledbh2 + 0x09D: u8 ledbh3 + 0x09E: u16 leddc + 0x0A0: u8 aa2g + 0x0A1: u8 aa5g + 0x0A2: u8 agbg1 + 0x0A3: u8 agbg0 + 0x0A4: u8 aga0 + 0x0A5: u8 agbg2 + 0x0A6: u8 aga2 + 0x0A7: u8 aga1 + 0x0A8: u8 txchain (&0xF) + 0x0A8: u8 rxchain (&0xF0, >>4) + 0x0A9: u8 antswitch + 0x0AA: u8 epagain2g (&0xE, >>1) + 0x0AA: u8 tssiposslope2g (&0x1) + 0x0AA: u8 pdgain2g { + +0x0: u16 (&0x1F0, >>4) + } + 0x0AB: u8 papdcap2g (&0x4, >>2) + 0x0AB: u8 tworangetssi2g (&0x2, >>1) + 0x0AB: u8 femctrl (&0xF8, >>3) + 0x0AC: u8 epagain5g (&0xE, >>1) + 0x0AC: u8 tssiposslope5g (&0x1) + 0x0AC: u8 pdgain5g { + +0x0: u16 (&0x1F0, >>4) + } + 0x0AD: u8 tworangetssi5g (&0x2, >>1) + 0x0AD: u8 gainctrlsph (&0xF8, >>3) + 0x0AD: u8 papdcap5g (&0x4, >>2) + 0x0AE: u8 tempoffset + 0x0AF: u8 tempthresh + 0x0B0: u16 rawtempsense (&0x1FF) + 0x0B1: u8 measpower (&0xFE, >>1) + 0x0B2: u8 tempsense_slope + 0x0B3: u8 tempcorrx (&0xFC, >>2) + 0x0B3: u8 tempsense_option (&0x3) + 0x0B4: u16 xtalfreq + 0x0B6: u16 pa5gbw4080a1[12] { + +0x0: u16, + +0x6: u16, + 0x0CE: u16, + +0x1E: u16, + 0x128: u16[8] + } + 0x0B8: u8 phycal_tempdelta + 0x0B9: u8 temps_period (&0xF) + 0x0B9: u8 temps_hysteresis (&0xF0, >>4) + 0x0BA: u8 measpower2 { + +0x0: u16 (&0x3F80, >>7) + } + 0x0BA: u8 measpower1 (&0x7F) + 0x0BE: u16 tssifloor2g (&0x3FF) + 0x0C0: u16 tssifloor5g[4] (&0x3FF) + 0x0C8: u8 pdoffset2g40ma0 (&0xF) + 0x0C8: u8 pdoffset2g40ma1 (&0xF0, >>4) + 0x0C9: u8 pdoffset2g40mvalid (&0x80, >>7) + 0x0C9: u8 pdoffset2g40ma2 (&0xF) + 0x0CA: u16 pdoffset40ma0 + 0x0CC: u16 pdoffset40ma1 + 0x0CE: u16 pdoffset40ma2 + 0x0D0: u16 pdoffset80ma0 + 0x0D2: u16 pdoffset80ma1 + 0x0D4: u16 pdoffset80ma2 + 0x0D6: u16 subband5gver + 0x0D8: u8 maxp2ga0 + 0x0DA: i16 pa2ga0[3] + 0x0E0: u8 rxgains5gmtrisoa0 (&0x78, >>3) + 0x0E0: u8 rxgains5gmelnagaina0 (&0x7) + 0x0E0: u8 rxgains5gmtrelnabypa0 (&0x80, >>7) + 0x0E1: u8 rxgains5ghtrisoa0 (&0x78, >>3) + 0x0E1: u8 rxgains5ghelnagaina0 (&0x7) + 0x0E1: u8 rxgains5ghtrelnabypa0 (&0x80, >>7) + 0x0E2: u8 rxgains2gtrelnabypa0 (&0x80, >>7) + 0x0E2: u8 rxgains2gelnagaina0 (&0x7) + 0x0E2: u8 rxgains2gtrisoa0 (&0x78, >>3) + 0x0E3: u8 rxgains5gtrelnabypa0 (&0x80, >>7) + 0x0E3: u8 rxgains5gtrisoa0 (&0x78, >>3) + 0x0E3: u8 rxgains5gelnagaina0 (&0x7) + 0x0E4: u8 maxp5ga0[4] + 0x0E8: i16 pa5ga0[12] + 0x100: u8 maxp2ga1 + 0x102: u16 pa2gccka0[3] + 0x102: i16 pa2ga1[3] + 0x108: u8 rxgains5gmtrisoa1 (&0x78, >>3) + 0x108: u8 rxgains5gmelnagaina1 (&0x7) + 0x108: u8 rxgains5gmtrelnabypa1 (&0x80, >>7) + 0x109: u8 rxgains5ghtrisoa1 (&0x78, >>3) + 0x109: u8 rxgains5ghelnagaina1 (&0x7) + 0x109: u8 rxgains5ghtrelnabypa1 (&0x80, >>7) + 0x10A: u8 rxgains2gtrelnabypa1 (&0x80, >>7) + 0x10A: u8 rxgains2gtrisoa1 (&0x78, >>3) + 0x10A: u8 rxgains2gelnagaina1 (&0x7) + 0x10B: u8 rxgains5gtrisoa1 (&0x78, >>3) + 0x10B: u8 rxgains5gtrelnabypa1 (&0x80, >>7) + 0x10B: u8 rxgains5gelnagaina1 (&0x7) + 0x10C: u8 maxp5ga1[4] + 0x110: u16 pa5gbw40a0[12] + 0x110: i16 pa5ga1[12] + 0x128: u8 maxp2ga2 + 0x12A: i16 pa2ga2[3] + 0x130: u8 rxgains5gmtrelnabypa2 (&0x80, >>7) + 0x130: u8 rxgains5gmtrisoa2 (&0x78, >>3) + 0x130: u8 rxgains5gmelnagaina2 (&0x7) + 0x131: u8 rxgains5ghtrisoa2 (&0x78, >>3) + 0x131: u8 rxgains5ghtrelnabypa2 (&0x80, >>7) + 0x131: u8 rxgains5ghelnagaina2 (&0x7) + 0x132: u8 rxgains2gtrisoa2 (&0x78, >>3) + 0x132: u8 rxgains2gelnagaina2 (&0x7) + 0x132: u8 rxgains2gtrelnabypa2 (&0x80, >>7) + 0x133: u8 rxgains5gtrisoa2 (&0x78, >>3) + 0x133: u8 rxgains5gtrelnabypa2 (&0x80, >>7) + 0x133: u8 rxgains5gelnagaina2 (&0x7) + 0x134: u8 maxp5ga2[4] + 0x138: i16 pa5ga2[12] + 0x138: u16 pa5gbw80a0[12] + 0x138: u16 pa5gbw4080a0[12] + 0x150: u16 cckbw202gpo + 0x152: u16 cckbw20ul2gpo + 0x154: u32 mcsbw202gpo + 0x158: u32 mcsbw402gpo + 0x15C: u16 dot11agofdmhrbw202gpo + 0x15E: u16 ofdmlrbw202gpo + 0x160: u32 mcsbw205glpo + 0x164: u32 mcsbw405glpo + 0x168: u32 mcsbw805glpo + 0x16C: u16 rpcal2g + 0x16E: u16 rpcal5gb0 + 0x170: u32 mcsbw205gmpo + 0x174: u32 mcsbw405gmpo + 0x178: u32 mcsbw805gmpo + 0x17C: u16 rpcal5gb1 + 0x17E: u16 rpcal5gb2 + 0x180: u32 mcsbw205ghpo + 0x184: u32 mcsbw405ghpo + 0x188: u32 mcsbw805ghpo + 0x18C: u16 rpcal5gb3 + 0x18E: u8 pdoffsetcckma1 (&0xF0, >>4) + 0x18E: u8 pdoffsetcckma0 (&0xF) + 0x18F: u8 pdoffsetcckma2 (&0xF) + 0x190: u16 mcslr5glpo (&0xFFF) + 0x191: u8 paparambwver (&0xF0, >>4) + 0x192: u16 mcslr5gmpo + 0x194: u16 mcslr5ghpo + 0x196: u16 sb20in40hrpo + 0x198: u16 sb20in80and160hr5glpo + 0x19A: u16 sb40and80hr5glpo + 0x19C: u16 sb20in80and160hr5gmpo + 0x19E: u16 sb40and80hr5gmpo + 0x1A0: u16 sb20in80and160hr5ghpo + 0x1A2: u16 sb40and80hr5ghpo + 0x1A4: u16 sb20in40lrpo + 0x1A6: u16 sb20in80and160lr5glpo + 0x1A8: u8 txidxcap2g { + +0x0: u16 (&0xFF0, >>4) + } + 0x1A8: u16 sb40and80lr5glpo + 0x1AA: u16 sb20in80and160lr5gmpo + 0x1AC: u8 txidxcap5g { + +0x0: u16 (&0xFF0, >>4) + } + 0x1AC: u16 sb40and80lr5gmpo + 0x1AE: u16 sb20in80and160lr5ghpo + 0x1B0: u16 sb40and80lr5ghpo + 0x1B2: u16 dot11agduphrpo + 0x1B4: u16 dot11agduplrpo + 0x1BA: u8 sar2g + 0x1BB: u8 sar5g + 0x1BC: u8 noiselvl2ga0 (&0x1F) + 0x1BC: u8 noiselvl2ga1 { + +0x0: u16 (&0x3E0, >>5) + } + 0x1BD: u8 noiselvl2ga2 (&0x7C, >>2) + 0x1BE: u8 noiselvl5ga1[4] { + +0x0: u16[4] (&0x3E0, >>5) + } + 0x1BE: u8 noiselvl5ga0[4] { + +0x0: u8 (&0x1F), + +0x2: u8 (&0x1F), + +0x4: u8 (&0x1F), + +0x6: u8 (&0x1F) + } + 0x1BF: u8 noiselvl5ga2[4] { + +0x0: u8 (&0x7C, >>2), + +0x2: u8 (&0x7C, >>2), + +0x4: u8 (&0x7C, >>2), + +0x6: u8 (&0x7C, >>2) + } + 0x1C6: u8 rxgainerr2ga1 { + +0x0: u16 (&0x7C0, >>6) + } + 0x1C6: u8 rxgainerr2ga0 (&0x3F) + 0x1C7: u8 rxgainerr2ga2 (&0xF8, >>3) + 0x1C8: u8 rxgainerr5ga1[4] { + +0x0: u16[4] (&0x7C0, >>6) + } + 0x1C8: u8 rxgainerr5ga0[4] { + +0x0: u8 (&0x3F), + +0x2: u8 (&0x3F), + +0x4: u8 (&0x3F), + +0x6: u8 (&0x3F) + } + 0x1C9: u8 rxgainerr5ga2[4] { + +0x0: u8 (&0xF8, >>3), + +0x2: u8 (&0xF8, >>3), + +0x4: u8 (&0xF8, >>3), + +0x6: u8 (&0xF8, >>3) + } + 0x1D0: u8 eu_edthresh2g + 0x1D1: u8 eu_edthresh5g + 0x1D2: u8 sromrev + 0x1D3: u8 +} diff --git a/sys/dev/bhnd/tools/nvram_map_gen.awk b/sys/dev/bhnd/tools/nvram_map_gen.awk index cbc6cebcb9d..834bd1fa15f 100755 --- a/sys/dev/bhnd/tools/nvram_map_gen.awk +++ b/sys/dev/bhnd/tools/nvram_map_gen.awk @@ -30,14 +30,28 @@ # # $FreeBSD$ -BEGIN { +BEGIN { main() } +END { at_exit() } + +# +# Print usage +# +function usage() { + print "usage: bhnd_nvram_map.awk [-hd] [-o output file]" + _EARLY_EXIT = 1 + exit 1 +} + +function main(_i) { RS="\n" - depth = 0 - symbols[depth,"_file"] = FILENAME - num_output_vars = 0 OUTPUT_FILE = null + # Probe awk implementation's hex digit handling + if ("0xA" + 0 != 10) { + AWK_REQ_HEX_PARSING=1 + } + # Seed rand() srand() @@ -46,36 +60,39 @@ BEGIN { OUT_T_HEADER = "HEADER" OUT_T_DATA = "DATA" + # Tab width to use when calculating output alignment + TAB_WIDTH = 8 + # Enable debug output DEBUG = 0 # Maximum revision - REV_MAX = 255 + REV_MAX = 256 # Parse arguments if (ARGC < 2) usage() - for (i = 1; i < ARGC; i++) { - if (ARGV[i] == "--debug") { + for (_i = 1; _i < ARGC; _i++) { + if (ARGV[_i] == "--debug") { DEBUG = 1 - } else if (ARGV[i] == "-d" && OUT_T == null) { + } else if (ARGV[_i] == "-d" && OUT_T == null) { OUT_T = OUT_T_DATA - } else if (ARGV[i] == "-h" && OUT_T == null) { + } else if (ARGV[_i] == "-h" && OUT_T == null) { OUT_T = OUT_T_HEADER - } else if (ARGV[i] == "-o") { - i++ - if (i >= ARGC) + } else if (ARGV[_i] == "-o") { + _i++ + if (_i >= ARGC) usage() - OUTPUT_FILE = ARGV[i] - } else if (ARGV[i] == "--") { - i++ + OUTPUT_FILE = ARGV[_i] + } else if (ARGV[_i] == "--") { + _i++ break - } else if (ARGV[i] !~ /^-/) { - FILENAME = ARGV[i] + } else if (ARGV[_i] !~ /^-/) { + FILENAME = ARGV[_i] } else { - print "unknown option " ARGV[i] + print "unknown option " ARGV[_i] usage() } } @@ -95,8 +112,8 @@ BEGIN { if (OUTPUT_FILE == "-") { OUTPUT_FILE = "/dev/stdout" } else if (OUTPUT_FILE == null) { - _bi = split(FILENAME, _paths, "/") - OUTPUT_FILE = _paths[_bi] + OUTPUT_FILE_IDX = split(FILENAME, _g_output_path, "/") + OUTPUT_FILE = _g_output_path[OUTPUT_FILE_IDX] if (OUTPUT_FILE !~ /^bhnd_/) OUTPUT_FILE = "bhnd_" OUTPUT_FILE @@ -107,317 +124,352 @@ BEGIN { OUTPUT_FILE = OUTPUT_FILE "_data.h" } - # Format Constants - FMT["hex"] = "BHND_NVRAM_SFMT_HEX" - FMT["decimal"] = "BHND_NVRAM_SFMT_DEC" - FMT["ccode"] = "BHND_NVRAM_SFMT_CCODE" - FMT["macaddr"] = "BHND_NVRAM_SFMT_MACADDR" - FMT["led_dc"] = "BHND_NVRAM_SFMT_LEDDC" - - # Data Type Constants - DTYPE["u8"] = "BHND_NVRAM_TYPE_UINT8" - DTYPE["u16"] = "BHND_NVRAM_TYPE_UINT16" - DTYPE["u32"] = "BHND_NVRAM_TYPE_UINT32" - DTYPE["i8"] = "BHND_NVRAM_TYPE_INT8" - DTYPE["i16"] = "BHND_NVRAM_TYPE_INT16" - DTYPE["i32"] = "BHND_NVRAM_TYPE_INT32" - DTYPE["char"] = "BHND_NVRAM_TYPE_CHAR" - - # Default masking for standard types - TMASK["u8"] = "0x000000FF" - TMASK["u16"] = "0x0000FFFF" - TMASK["u32"] = "0xFFFFFFFF" - TMASK["i8"] = TMASK["u8"] - TMASK["i16"] = TMASK["u16"] - TMASK["i32"] = TMASK["u32"] - TMASK["char"] = TMASK["u8"] - - # Byte sizes for standard types - TSIZE["u8"] = "1" - TSIZE["u16"] = "2" - TSIZE["u32"] = "4" - TSIZE["i8"] = TSIZE["u8"] - TSIZE["i16"] = TSIZE["u8"] - TSIZE["i32"] = TSIZE["u8"] - TSIZE["char"] = "1" - # Common Regexs - INT_REGEX = "^(0|[1-9][0-9]*),?$" - HEX_REGEX = "^0x[A-Fa-f0-9]+,?$" + UINT_REGEX = "^(0|[1-9][0-9]*)$" + HEX_REGEX = "^(0x[A-Fa-f0-9]+)$" + OFF_REGEX = "^(0|[1-9][0-9]*)|^(0x[A-Fa-f0-9]+)" + REL_OFF_REGEX = "^\\+(0|[1-9][0-9]*)|^\\+(0x[A-Fa-f0-9]+)" ARRAY_REGEX = "\\[(0|[1-9][0-9]*)\\]" - TYPES_REGEX = "^(((u|i)(8|16|32))|char)("ARRAY_REGEX")?,?$" + TYPES_REGEX = "^(((u|i)(8|16|32))|char)("ARRAY_REGEX")?$" - IDENT_REGEX = "^[A-Za-z_][A-Za-z0-9_]*,?$" - SROM_OFF_REGEX = "("TYPES_REGEX"|"HEX_REGEX")" + IDENT_REGEX = "[A-Za-z_][A-Za-z0-9_]*" + SVAR_IDENT_REGEX = "^<"IDENT_REGEX">{?$" # identifiers + VAR_IDENT_REGEX = "^"IDENT_REGEX"{?$" # var identifiers - # Parser states types - ST_STRUCT_BLOCK = "struct" # struct block - ST_VAR_BLOCK = "var" # variable block - ST_SROM_DEFN = "srom" # srom offset defn - ST_NONE = "NONE" # default state + VACCESS_REGEX = "^(private|internal)$" - # Property types - PROP_T_SFMT = "sfmt" - PROP_T_ALL1 = "all1" + # Property array keys + PROP_ID = "p_id" + PROP_NAME = "p_name" - # Internal variables used for parser state - # tracking - STATE_TYPE = "_state_type" - STATE_IDENT = "_state_block_name" - STATE_LINENO = "_state_first_line" - STATE_ISBLOCK = "_state_is_block" + # Prop path array keys + PPATH_HEAD = "ppath_head" + PPATH_TAIL = "ppath_tail" - # Common array keys - DEF_LINE = "def_line" - NUM_REVS = "num_revs" - REV = "rev" + # Object array keys + OBJ_IS_CLS = "o_is_cls" + OBJ_SUPER = "o_super" + OBJ_PROP = "o_prop" - # Revision array keys - REV_START = "rev_start" - REV_END = "rev_end" - REV_DESC = "rev_decl" - REV_NUM_OFFS = "num_offs" + # Class array keys + CLS_NAME = "cls_name" + CLS_PROP = "cls_prop" - # Offset array keys - OFF = "off" - OFF_NUM_SEGS = "off_num_segs" - OFF_SEG = "off_seg" + # C SPROM binding opcodes/opcode flags + SPROM_OPCODE_EOF = "SPROM_OPCODE_EOF" + SPROM_OPCODE_NELEM = "SPROM_OPCODE_NELEM" + SPROM_OPCODE_VAR_END = "SPROM_OPCODE_VAR_END" + SPROM_OPCODE_VAR_IMM = "SPROM_OPCODE_VAR_IMM" + SPROM_OPCODE_VAR_REL_IMM = "SPROM_OPCODE_VAR_REL_IMM" + SPROM_OPCODE_VAR = "SPROM_OPCODE_VAR" + SPROM_OPCODE_REV_IMM = "SPROM_OPCODE_REV_IMM" + SPROM_OPCODE_REV_RANGE = "SPROM_OPCODE_REV_RANGE" + SPROM_OP_REV_START_MASK = "SPROM_OP_REV_START_MASK" + SPROM_OP_REV_START_SHIFT = "SPROM_OP_REV_START_SHIFT" + SPROM_OP_REV_END_MASK = "SPROM_OP_REV_END_MASK" + SPROM_OP_REV_END_SHIFT = "SPROM_OP_REV_END_SHIFT" + SPROM_OPCODE_MASK_IMM = "SPROM_OPCODE_MASK_IMM" + SPROM_OPCODE_MASK = "SPROM_OPCODE_MASK" + SPROM_OPCODE_SHIFT_IMM = "SPROM_OPCODE_SHIFT_IMM" + SPROM_OPCODE_SHIFT = "SPROM_OPCODE_SHIFT" + SPROM_OPCODE_OFFSET_REL_IMM = "SPROM_OPCODE_OFFSET_REL_IMM" + SPROM_OPCODE_OFFSET = "SPROM_OPCODE_OFFSET" + SPROM_OPCODE_TYPE = "SPROM_OPCODE_TYPE" + SPROM_OPCODE_TYPE_IMM = "SPROM_OPCODE_TYPE_IMM" + SPROM_OPCODE_DO_BINDN_IMM = "SPROM_OPCODE_DO_BINDN_IMM" + SPROM_OPCODE_DO_BIND = "SPROM_OPCODE_DO_BIND" + SPROM_OPCODE_DO_BINDN = "SPROM_OPCODE_DO_BINDN" + SPROM_OP_BIND_SKIP_IN_MASK = "SPROM_OP_BIND_SKIP_IN_MASK" + SPROM_OP_BIND_SKIP_IN_SHIFT = "SPROM_OP_BIND_SKIP_IN_SHIFT" + SPROM_OP_BIND_SKIP_IN_SIGN = "SPROM_OP_BIND_SKIP_IN_SIGN" + SPROM_OP_BIND_SKIP_OUT_MASK = "SPROM_OP_BIND_SKIP_OUT_MASK" + SPROM_OP_BIND_SKIP_OUT_SHIFT = "SPROM_OP_BIND_SKIP_OUT_SHIFT" - # Segment array keys - SEG_ADDR = "seg_addr" - SEG_COUNT = "seg_count" - SEG_TYPE = "seg_type" - SEG_MASK = "seg_mask" - SEG_SHIFT = "seg_shift" + SPROM_OP_DATA_U8 = "SPROM_OP_DATA_U8" + SPROM_OP_DATA_U8_SCALED = "SPROM_OP_DATA_U8_SCALED" + SPROM_OP_DATA_U16 = "SPROM_OP_DATA_U16" + SPROM_OP_DATA_U32 = "SPROM_OP_DATA_U32" + SPROM_OP_DATA_I8 = "SPROM_OP_DATA_I8" - # Variable array keys - VAR_NAME = "v_name" - VAR_TYPE = "v_type" - VAR_BASE_TYPE = "v_base_type" - VAR_FMT = "v_fmt" - VAR_STRUCT = "v_parent_struct" - VAR_PRIVATE = "v_private" - VAR_ARRAY = "v_array" - VAR_IGNALL1 = "v_ignall1" + SPROM_OP_BIND_SKIP_IN_MAX = 3 # maximum SKIP_IN value + SPROM_OP_BIND_SKIP_IN_MIN = -3 # minimum SKIP_IN value + SPROM_OP_BIND_SKIP_OUT_MAX = 1 # maximum SKIP_OUT value + SPROM_OP_BIND_SKIP_OUT_MIN = 0 # minimum SKIP_OUT value + SPROM_OP_IMM_MAX = 15 # maximum immediate value + SPROM_OP_REV_RANGE_MAX = 15 # maximum SROM rev range value + + # SPROM opcode encoding state + SromOpStream = class_new("SromOpStream") + class_add_prop(SromOpStream, p_layout, "layout") + class_add_prop(SromOpStream, p_revisions, "revisions") + class_add_prop(SromOpStream, p_vid, "vid") + class_add_prop(SromOpStream, p_offset, "offset") + class_add_prop(SromOpStream, p_type, "type") + class_add_prop(SromOpStream, p_nelem, "nelem") + class_add_prop(SromOpStream, p_mask, "mask") + class_add_prop(SromOpStream, p_shift, "shift") + class_add_prop(SromOpStream, p_bind_total, "bind_total") + class_add_prop(SromOpStream, p_pending_bind, "pending_bind") + + # SROM pending bind operation + SromOpBind = class_new("SromOpBind") + class_add_prop(SromOpBind, p_segment, "segment") + class_add_prop(SromOpBind, p_count, "count") + class_add_prop(SromOpBind, p_offset, "offset") + class_add_prop(SromOpBind, p_width, "width") + class_add_prop(SromOpBind, p_skip_in, "skip_in") + class_add_prop(SromOpBind, p_skip_out, "skip_out") + class_add_prop(SromOpBind, p_buffer, "buffer") + + # Map class definition + Map = class_new("Map") + + # Array class definition + Array = class_new("Array") + class_add_prop(Array, p_count, "count") + + # MacroType class definition + # Used to define a set of known macro types that may be generated + MacroType = class_new("MacroType") + class_add_prop(MacroType, p_name, "name") + class_add_prop(MacroType, p_const_suffix, "const_suffix") + + MTypeVarName = macro_type_new("name", "") # var name + MTypeVarID = macro_type_new("id", "_ID") # var unique ID + MTypeVarMaxLen = macro_type_new("len", "_MAXLEN") # var max array length + + # Preprocessor Constant + MacroDefine = class_new("MacroDefine") + class_add_prop(MacroDefine, p_name, "name") + class_add_prop(MacroDefine, p_value, "value") + + # ParseState definition + ParseState = class_new("ParseState") + class_add_prop(ParseState, p_ctx, "ctx") + class_add_prop(ParseState, p_is_block, "is_block") + class_add_prop(ParseState, p_line, "line") + + # Value Formats + Fmt = class_new("Fmt") + class_add_prop(Fmt, p_name, "name") + class_add_prop(Fmt, p_symbol, "const") + + FmtHex = fmt_new("hex", "bhnd_nvram_val_bcm_hex_fmt") + FmtDec = fmt_new("decimal", "bhnd_nvram_val_bcm_decimal_fmt") + FmtMAC = fmt_new("macaddr", "bhnd_nvram_val_bcm_macaddr_fmt") + FmtLEDDC = fmt_new("leddc", "bhnd_nvram_val_bcm_leddc_fmt") + FmtStr = fmt_new("string", "bhnd_nvram_val_bcm_string_fmt") + + ValueFormats = map_new() + map_set(ValueFormats, get(FmtHex, p_name), FmtHex) + map_set(ValueFormats, get(FmtDec, p_name), FmtDec) + map_set(ValueFormats, get(FmtMAC, p_name), FmtMAC) + map_set(ValueFormats, get(FmtLEDDC, p_name), FmtLEDDC) + map_set(ValueFormats, get(FmtStr, p_name), FmtStr) + + # Data Types + Type = class_new("Type") + class_add_prop(Type, p_name, "name") + class_add_prop(Type, p_width, "width") + class_add_prop(Type, p_signed, "signed") + class_add_prop(Type, p_const, "const") + class_add_prop(Type, p_const_val, "const_val") + class_add_prop(Type, p_array_const, "array_const") + class_add_prop(Type, p_array_const_val, "array_const_val") + class_add_prop(Type, p_default_fmt, "default_fmt") + class_add_prop(Type, p_mask, "mask") + + ArrayType = class_new("ArrayType", AST) + class_add_prop(ArrayType, p_type, "type") + class_add_prop(ArrayType, p_count, "count") + + UInt8Max = 255 + UInt16Max = 65535 + UInt32Max = 4294967295 + Int8Min = -128 + Int8Max = 127 + Int16Min = -32768 + Int16Max = 32767 + Int32Min = -2147483648 + Int32Max = 2147483648 + CharMin = Int8Min + CharMax = Int8Max + + UInt8 = type_new("u8", 1, 0, "BHND_NVRAM_TYPE_UINT8", + "BHND_NVRAM_TYPE_UINT8_ARRAY", FmtHex, UInt8Max, 0, 16) + + UInt16 = type_new("u16", 2, 0, "BHND_NVRAM_TYPE_UINT16", + "BHND_NVRAM_TYPE_UINT16_ARRAY", FmtHex, UInt16Max, 1, 17) + + UInt32 = type_new("u32", 4, 0, "BHND_NVRAM_TYPE_UINT32", + "BHND_NVRAM_TYPE_UINT32_ARRAY", FmtHex, UInt32Max, 2, 18) + + Int8 = type_new("i8", 1, 1, "BHND_NVRAM_TYPE_INT8", + "BHND_NVRAM_TYPE_INT8_ARRAY", FmtDec, UInt8Max, 4, 20) + + Int16 = type_new("i16", 2, 1, "BHND_NVRAM_TYPE_INT16", + "BHND_NVRAM_TYPE_INT16_ARRAY", FmtDec, UInt16Max, 5, 21) + + Int32 = type_new("i32", 4, 1, "BHND_NVRAM_TYPE_INT32", + "BHND_NVRAM_TYPE_INT32_ARRAY", FmtDec, UInt32Max, 6, 22) + + Char = type_new("char", 1, 1, "BHND_NVRAM_TYPE_CHAR", + "BHND_NVRAM_TYPE_CHAR_ARRAY", FmtStr, UInt8Max, 8, 24) + + BaseTypes = map_new() + map_set(BaseTypes, get(UInt8, p_name), UInt8) + map_set(BaseTypes, get(UInt16, p_name), UInt16) + map_set(BaseTypes, get(UInt32, p_name), UInt32) + map_set(BaseTypes, get(Int8, p_name), Int8) + map_set(BaseTypes, get(Int16, p_name), Int16) + map_set(BaseTypes, get(Int32, p_name), Int32) + map_set(BaseTypes, get(Char, p_name), Char) + + BaseTypesArray = map_to_array(BaseTypes) + BaseTypesCount = array_size(BaseTypesArray) + + # Variable Flags + VFlag = class_new("VFlag") + class_add_prop(VFlag, p_name, "name") + class_add_prop(VFlag, p_const, "const") + + VFlagPrivate = vflag_new("private", "BHND_NVRAM_VF_MFGINT") + VFlagIgnoreAll1 = vflag_new("ignall1", "BHND_NVRAM_VF_IGNALL1") + + # Variable Access Type Constants + VAccess = class_new("VAccess") + VAccessPublic = obj_new(VAccess) # Public + VAccessPrivate = obj_new(VAccess) # MFG Private + VAccessInternal = obj_new(VAccess) # Implementation-Internal + + # + # AST node classes + # + AST = class_new("AST") + class_add_prop(AST, p_line, "line") + + SymbolContext = class_new("SymbolContext", AST) + class_add_prop(SymbolContext, p_vars, "vars") + + # NVRAM root parser context + NVRAM = class_new("NVRAM", SymbolContext) + class_add_prop(NVRAM, p_var_groups, "var_groups") + class_add_prop(NVRAM, p_srom_layouts, "srom_layouts") + class_add_prop(NVRAM, p_srom_table, "srom_table") + + # Variable Group + VarGroup = class_new("VarGroup", SymbolContext) + class_add_prop(VarGroup, p_name, "name") + + # Revision Range + RevRange = class_new("RevRange", AST) + class_add_prop(RevRange, p_start, "start") + class_add_prop(RevRange, p_end, "end") + + # String Constant + StringConstant = class_new("StringConstant", AST) + class_add_prop(StringConstant, p_value, "value") # string + class_add_prop(StringConstant, p_continued, "continued") # bool + + # Variable Declaration + Var = class_new("Var", AST) + class_add_prop(Var, p_access, "access") # VAccess + class_add_prop(Var, p_name, "name") # string + class_add_prop(Var, p_desc, "desc") # StringConstant + class_add_prop(Var, p_help, "help") # StringConstant + class_add_prop(Var, p_type, "type") # AbstractType + class_add_prop(Var, p_fmt, "fmt") # Fmt + class_add_prop(Var, p_ignall1, "ignall1") # bool + # ID is assigned once all variables are sorted + class_add_prop(Var, p_vid, "vid") # int + + # Common interface inherited by parser contexts that support + # registration of SROM variable entries + SromContext = class_new("SromContext", AST) + class_add_prop(SromContext, p_revisions, "revisions") + + # SROM Layout Node + SromLayout = class_new("SromLayout", SromContext) + class_add_prop(SromLayout, p_entries, "entries") # Array + class_add_prop(SromLayout, p_revmap, "revmap") # Map<(string,int), SromEntry> + class_add_prop(SromLayout, p_output_var_counts, # Map (rev->count) + "output_var_counts") + + # SROM Layout Filter Node + # Represents a filter over a parent SromLayout's revisions + SromLayoutFilter = class_new("SromLayoutFilter", SromContext) + class_add_prop(SromLayoutFilter, p_parent, "parent") + + # SROM variable entry + SromEntry = class_new("SromEntry", AST) + class_add_prop(SromEntry, p_var, "var") + class_add_prop(SromEntry, p_revisions, "revisions") + class_add_prop(SromEntry, p_base_offset, "base_offset") + class_add_prop(SromEntry, p_type, "type") + class_add_prop(SromEntry, p_offsets, "offsets") + + # SROM variable offset + SromOffset = class_new("SromOffset", AST) + class_add_prop(SromOffset, p_segments, "segments") + + # SROM variable offset segment + SromSegment = class_new("SromSegment", AST) + class_add_prop(SromSegment, p_offset, "offset") + class_add_prop(SromSegment, p_type, "type") + class_add_prop(SromSegment, p_mask, "mask") + class_add_prop(SromSegment, p_shift, "shift") + class_add_prop(SromSegment, p_value, "value") + + # Create the parse state stack + _g_parse_stack_depth = 0 + _g_parse_stack[0] = null + + # Push the root parse state + parser_state_push(nvram_new(), 0) } -# return the flag definition for variable `v` -function gen_var_flags (v) +function at_exit(_block_start, _state, _output_vars, _noutput_vars, _name, _var, + _i) { - _num_flags = 0; - if (vars[v,VAR_ARRAY]) - _flags[_num_flags++] = "BHND_NVRAM_VF_ARRAY" - - if (vars[v,VAR_PRIVATE]) - _flags[_num_flags++] = "BHND_NVRAM_VF_MFGINT" - - if (vars[v,VAR_IGNALL1]) - _flags[_num_flags++] = "BHND_NVRAM_VF_IGNALL1" - - if (_num_flags == 0) - _flags[_num_flags++] = "0" - - return (join(_flags, "|", _num_flags)) -} - -# emit the bhnd_sprom_offsets for a given variable revision key -function emit_var_sprom_offsets (v, revk) -{ - emit(sprintf("{{%u, %u}, (struct bhnd_sprom_offset[]) {\n", - vars[revk,REV_START], - vars[revk,REV_END])) - output_depth++ - - num_offs = vars[revk,REV_NUM_OFFS] - num_offs_written = 0 - elem_count = 0 - for (offset = 0; offset < num_offs; offset++) { - offk = subkey(revk, OFF, offset"") - num_segs = vars[offk,OFF_NUM_SEGS] - - for (seg = 0; seg < num_segs; seg++) { - segk = subkey(offk, OFF_SEG, seg"") - - for (seg_n = 0; seg_n < vars[segk,SEG_COUNT]; seg_n++) { - seg_addr = vars[segk,SEG_ADDR] - seg_addr += TSIZE[vars[segk,SEG_TYPE]] * seg_n - - emit(sprintf("{%s, %s, %s, %s, %s},\n", - seg_addr, - (seg > 0) ? "true" : "false", - DTYPE[vars[segk,SEG_TYPE]], - vars[segk,SEG_SHIFT], - vars[segk,SEG_MASK])) - - num_offs_written++ - } - } - } - - output_depth-- - emit("}, " num_offs_written "},\n") -} - -# emit a bhnd_nvram_vardef for variable name `v` -function emit_nvram_vardef (v) -{ - emit(sprintf("{\"%s\", %s, %s, %s, (struct bhnd_sprom_vardefn[]) {\n", - v suffix, - DTYPE[vars[v,VAR_BASE_TYPE]], - FMT[vars[v,VAR_FMT]], - gen_var_flags(v))) - output_depth++ - - for (rev = 0; rev < vars[v,NUM_REVS]; rev++) { - revk = subkey(v, REV, rev"") - emit_var_sprom_offsets(v, revk) - } - - output_depth-- - emit("}, " vars[v,NUM_REVS] "},\n") -} - -# emit a header name #define for variable `v` -function emit_var_namedef (v) -{ - emit("#define\tBHND_NVAR_" toupper(v) "\t\"" v "\"\n") -} - -# generate a set of var offset definitions for struct variable `st_vid` -function gen_struct_var_offsets (vid, revk, st_vid, st_revk, base_addr) -{ - # Copy all offsets to the new variable - for (offset = 0; offset < vars[v,REV_NUM_OFFS]; offset++) { - st_offk = subkey(st_revk, OFF, offset"") - offk = subkey(revk, OFF, offset"") - - # Copy all segments to the new variable, applying base - # address adjustment - num_segs = vars[st_offk,OFF_NUM_SEGS] - vars[offk,OFF_NUM_SEGS] = num_segs - - for (seg = 0; seg < num_segs; seg++) { - st_segk = subkey(st_offk, OFF_SEG, seg"") - segk = subkey(offk, OFF_SEG, seg"") - - vars[segk,SEG_ADDR] = vars[st_segk,SEG_ADDR] + \ - base_addr"" - vars[segk,SEG_COUNT] = vars[st_segk,SEG_COUNT] - vars[segk,SEG_TYPE] = vars[st_segk,SEG_TYPE] - vars[segk,SEG_MASK] = vars[st_segk,SEG_MASK] - vars[segk,SEG_SHIFT] = vars[st_segk,SEG_SHIFT] - } - } -} - -# generate a complete set of variable definitions for struct variable `st_vid`. -function gen_struct_vars (st_vid) -{ - st = vars[st_vid,VAR_STRUCT] - st_max_off = 0 - - # determine the total number of variables to generate - for (st_rev = 0; st_rev < structs[st,NUM_REVS]; st_rev++) { - srevk = subkey(st, REV, st_rev"") - for (off = 0; off < structs[srevk,REV_NUM_OFFS]; off++) { - if (off > st_max_off) - st_max_off = off - } - } - - # generate variable records for each defined struct offset - for (off = 0; off < st_max_off; off++) { - # Construct basic variable definition - v = st_vid off"" - vars[v,VAR_TYPE] = vars[st_vid,VAR_TYPE] - vars[v,VAR_BASE_TYPE] = vars[st_vid,VAR_BASE_TYPE] - vars[v,VAR_FMT] = vars[st_vid,VAR_FMT] - vars[v,VAR_PRIVATE] = vars[st_vid,VAR_PRIVATE] - vars[v,VAR_ARRAY] = vars[st_vid,VAR_ARRAY] - vars[v,VAR_IGNALL1] = vars[st_vid,VAR_IGNALL1] - vars[v,NUM_REVS] = 0 - - # Add to output variable list - output_vars[num_output_vars++] = v - - # Construct revision / offset entries - for (srev = 0; srev < structs[st,NUM_REVS]; srev++) { - # Struct revision key - st_revk = subkey(st, REV, srev"") - - # Skip offsets not defined for this revision - if (off > structs[st_revk,REV_NUM_OFFS]) - continue - - # Strut offset key and associated base address */ - offk = subkey(st_revk, OFF, off"") - base_addr = structs[offk,SEG_ADDR] - - for (vrev = 0; vrev < vars[st_vid,NUM_REVS]; vrev++) { - st_var_revk = subkey(st_vid, REV, vrev"") - v_start = vars[st_var_revk,REV_START] - v_end = vars[st_var_revk,REV_END] - s_start = structs[st_revk,REV_START] - s_end = structs[st_revk,REV_END] - - # We don't support computing the union - # of partially overlapping ranges - if ((v_start < s_start && v_end >= s_start) || - (v_start <= s_end && v_end > s_end)) - { - errorx("partially overlapping " \ - "revision ranges are not supported") - } - - # skip variables revs that are not within - # the struct offset's compatibility range - if (v_start < s_start || v_start > s_end || - v_end < s_start || v_end > s_end) - continue - - # Generate the new revision record - rev = vars[v,NUM_REVS] "" - revk = subkey(v, REV, rev) - vars[v,NUM_REVS]++ - - vars[revk,DEF_LINE] = vars[st_revk,DEF_LINE] - vars[revk,REV_START] = v_start - vars[revk,REV_END] = v_end - vars[revk,REV_NUM_OFFS] = \ - vars[st_var_revk,REV_NUM_OFFS] - - gen_struct_var_offsets(v, revk, st_vid, st_revk, - base_addr) - } - } - } -} - - -END { # Skip completion handling if exiting from an error if (_EARLY_EXIT) exit 1 # Check for complete block closure - if (depth > 0) { - block_start = g(STATE_LINENO) - errorx("missing '}' for block opened on line " block_start "") + if (!in_parser_context(NVRAM)) { + _state = parser_state_get() + _block_start = get(_state, p_line) + errorx("missing '}' for block opened on line " _block_start "") } - # Generate concrete variable definitions for all struct variables - for (v in var_names) { - if (vars[v,VAR_STRUCT] != null) { - gen_struct_vars(v) - } else { - output_vars[num_output_vars++] = v - } + # Apply lexicographical sorting to our variable names. To support more + # effecient table searching, we guarantee a stable sort order (using C + # collation). + # + # This also has a side-effect of generating a unique monotonic ID + # for all variables, which we will emit as a #define and can use as a + # direct index into the C variable table + _output_vars = array_new() + for (_name in _g_var_names) { + _var = _g_var_names[_name] + + # Don't include internal variables in the output + if (var_is_internal(_var)) + continue + + array_append(_output_vars, _var) } - # Apply lexicographical sorting. To support more effecient table - # searching, we guarantee a stable sort order (using C collation). - sort(output_vars) + # Sort by variable name + array_sort(_output_vars, prop_to_path(p_name)) + + # Set all variable ID properties to their newly assigned ID value + _noutput_vars = array_size(_output_vars) + for (_i = 0; _i < _noutput_vars; _i++) { + _var = array_get(_output_vars, _i) + set(_var, p_vid, _i) + } # Truncate output file and write common header printf("") > OUTPUT_FILE @@ -430,68 +482,1834 @@ END { # Emit all variable definitions if (OUT_T == OUT_T_DATA) { - emit("#include \n") - emit("static const struct bhnd_nvram_vardefn "\ - "bhnd_nvram_vardefs[] = {\n") - output_depth++ - for (i = 0; i < num_output_vars; i++) - emit_nvram_vardef(output_vars[i]) - output_depth-- - emit("};\n") + write_data(_output_vars) } else if (OUT_T == OUT_T_HEADER) { - for (i = 0; i < num_output_vars; i++) - emit_var_namedef(output_vars[i]) + write_header(_output_vars) } - printf("%u variable records written to %s\n", num_output_vars, + printf("%u variable records written to %s\n", array_size(_output_vars), OUTPUT_FILE) >> "/dev/stderr" } - -# -# Print usage -# -function usage () +# Write the public header (output type HEADER) +function write_header(output_vars, _noutput_vars, _var, + _tab_align, _macro, _macros, _num_macros, _i) { - print "usage: bhnd_nvram_map.awk [-hd] [-o output file]" - _EARLY_EXIT = 1 - exit 1 + # Produce our array of #defines + _num_macros = 0 + _noutput_vars = array_size(output_vars) + for (_i = 0; _i < _noutput_vars; _i++) { + _var = array_get(output_vars, _i) + + # Variable name + _macro = var_get_macro(_var, MTypeVarName, \ + "\"" get(_var, p_name) "\"") + _macros[_num_macros++] = _macro + + # Variable array length + if (var_has_array_type(_var)) { + _macro = var_get_macro(_var, MTypeVarMaxLen, + var_get_array_len(_var)) + _macros[_num_macros++] = _macro + } + } + + # Calculate value tab alignment position for our macros + _tab_align = macros_get_tab_alignment(_macros, _num_macros) + + # Write the macros + for (_i = 0; _i < _num_macros; _i++) + write_macro_define(_macros[_i], _tab_align) } -# -# Join all array elements with the given separator -# -function join (array, sep, count) +# Write the private data header (output type DATA) +function write_data(output_vars, _noutput_vars, _var, _nvram, _layouts, + _nlayouts, _layout, _revs, _rev, _rev_start, _rev_end, _base_type, + _srom_table, _nsrom_table, _i, _j) { - if (count == 0) - return ("") + _nvram = parser_state_get_context(NVRAM) + _layouts = get(_nvram, p_srom_layouts) + _nlayouts = array_size(_layouts) - _result = array[0] - for (_ji = 1; _ji < count; _ji++) - _result = _result sep array[_ji] + _noutput_vars = array_size(output_vars) + + # Write all our private NVAR_ID defines + write_data_defines(output_vars) + + # Write all layout binding opcodes, and build an array + # mapping SROM revision to corresponding SROM layout + _srom_table = array_new() + for (_i = 0; _i < _nlayouts; _i++) { + _layout = array_get(_layouts, _i) + + # Write binding opcode table to our output file + write_srom_bindings(_layout) + + # Add entries to _srom_table for all covered revisions + _revs = get(_layout, p_revisions) + _rev_start = get(_revs, p_start) + _rev_end = get(_revs, p_end) + + for (_j = _rev_start; _j <= _rev_end; _j++) + array_append(_srom_table, _j) + } + + # Sort in ascending order, by SROM revision + array_sort(_srom_table) + _nsrom_table = array_size(_srom_table) + + # Write the variable definitions + emit("/* Variable definitions */\n") + emit("const struct bhnd_nvram_vardefn " \ + "bhnd_nvram_vardefns[] = {\n") + output_depth++ + for (_i = 0; _i < _noutput_vars; _i++) { + write_data_nvram_vardefn(array_get(output_vars, _i)) + } + output_depth-- + emit("};\n") + emit("const size_t bhnd_nvram_num_vardefns = " _noutput_vars ";\n") + + # Write static asserts for raw type constant values that must be kept + # synchronized with the code + for (_i = 0; _i < BaseTypesCount; _i++) { + _base_type = array_get(BaseTypesArray, _i) + + emit(sprintf("_Static_assert(%s == %u, \"%s\");\n", + type_get_const(_base_type), type_get_const_val(_base_type), + "type constant out of sync")) + + emit(sprintf("_Static_assert(%s == %u, \"%s\");\n", + get(_base_type, p_array_const), + get(_base_type, p_array_const_val), + "array type constant out of sync")) + } + + # Write all top-level bhnd_sprom_layout entries + emit("/* SPROM layouts */\n") + emit("const struct bhnd_sprom_layout bhnd_sprom_layouts[] = {\n") + output_depth++ + for (_i = 0; _i < _nsrom_table; _i++) { + _rev = array_get(_srom_table, _i) + _layout = nvram_get_srom_layout(_nvram, _rev) + write_data_srom_layout(_layout, _rev) + } + output_depth-- + emit("};\n") + emit("const size_t bhnd_sprom_num_layouts = " _nsrom_table ";\n") +} + +# Write a bhnd_nvram_vardef entry for the given variable +function write_data_nvram_vardefn(v, _desc, _help, _type, _fmt) { + obj_assert_class(v, Var) + + _desc = get(v, p_desc) + _help = get(v, p_help) + _type = get(v, p_type) + _fmt = var_get_fmt(v) + + emit("{\n") + output_depth++ + emit(sprintf(".name = \"%s\",\n", get(v, p_name))) + + if (_desc != null) + emit(sprintf(".desc = \"%s\",\n", get(_desc, p_value))) + else + emit(".desc = NULL,\n") + + if (_help != null) + emit(sprintf(".help = \"%s\",\n", get(_help, p_value))) + else + emit(".help = NULL,\n") + + emit(".type = " type_get_const(_type) ",\n") + emit(".nelem = " var_get_array_len(v) ",\n") + emit(".fmt = &" get(_fmt, p_symbol) ",\n") + emit(".flags = " gen_var_flags(v) ",\n") + + output_depth-- + emit("},\n") +} + +# Write a top-level bhnd_sprom_layout entry for the given revision +# and layout definition +function write_data_srom_layout(layout, revision, _flags, _size, + _sromcrc, _crc_seg, + _sromsig, _sig_seg, _sig_offset, _sig_value, + _sromrev, _rev_seg, _rev_off, + _num_vars) +{ + _flags = array_new() + + # Calculate the size; it always follows the internal CRC variable + _sromcrc = srom_layout_find_entry(layout, "", revision) + if (_sromcrc == null) { + errorx("missing '' entry for '"revision"' layout, " \ + "cannot compute total size") + } else { + _crc_seg = srom_entry_get_single_segment(_sromcrc) + _size = get(_crc_seg, p_offset) + _size += get(get(_crc_seg, p_type), p_width) + } + + # Fetch signature definition + _sromsig = srom_layout_find_entry(layout, "", revision) + if (_sromsig == null) { + array_append(_flags, "SPROM_LAYOUT_MAGIC_NONE") + } else { + _sig_seg = srom_entry_get_single_segment(_sromsig) + + _sig_offset = get(_sig_seg, p_offset) + _sig_value = get(_sig_seg, p_value) + if (_sig_value == "") + errorc(get(_sromsig, p_line), "missing signature value") + } + + # Fetch sromrev definition + _sromrev = srom_layout_find_entry(layout, "sromrev", revision) + if (_sromrev == null) { + errorx("missing 'sromrev' entry for '"revision"' layout, " \ + "cannot determine offset") + } else { + # Must be a u8 value + if (!type_equal(get(_sromrev, p_type), UInt8)) { + errorx("'sromrev' entry has non-u8 type '" \ + type_to_string(get(_sromrev, p_type))) + } + + _rev_seg = srom_entry_get_single_segment(_sromrev) + _rev_off = get(_rev_seg, p_offset) + } + + # Write layout entry + emit("{\n") + output_depth++ + emit(".size = "_size",\n") + emit(".rev = "revision",\n") + + if (array_size(_flags) > 0) { + emit(".flags = " array_join(_flags, "|") ",\n") + } else { + emit(".flags = 0,\n") + } + + emit(".srev_offset = " _rev_off ",\n") + + if (_sromsig != null) { + emit(".magic_offset = " _sig_offset ",\n") + emit(".magic_value = " _sig_value ",\n") + } else { + emit(".magic_offset = 0,\n") + emit(".magic_value = 0,\n") + } + + emit(".bindings = " srom_layout_get_variable_name(layout) ",\n") + emit(".bindings_size = nitems(" \ + srom_layout_get_variable_name(layout) "),\n") + + emit(".num_vars = " srom_layout_num_output_vars(layout, revision) ",\n") + + obj_delete(_flags) + + output_depth-- + emit("},\n"); +} + +# Create a new opstream encoding state instance for the given layout +function srom_ops_new(layout, _obj) { + obj_assert_class(layout, SromLayout) + + _obj = obj_new(SromOpStream) + set(_obj, p_layout, layout) + set(_obj, p_revisions, get(layout, p_revisions)) + set(_obj, p_vid, 0) + set(_obj, p_offset, 0) + set(_obj, p_type, null) + set(_obj, p_mask, null) + set(_obj, p_shift, null) + + return (_obj) +} + +# Return the current type width, or throw an error if no type is currently +# specified. +function srom_ops_get_type_width(opstream, _type) +{ + obj_assert_class(opstream, SromOpStream) + + _type = get(opstream, p_type) + if (_type == null) + errorx("no type value set") + + return (get(type_get_base(_type), p_width)) +} + +# Write a string to the SROM opcode stream, either buffering the write, +# or emitting it directly. +function srom_ops_emit(opstream, string, _pending_bind, _buffer) { + obj_assert_class(opstream, SromOpStream) + + # Buffered? + if ((_pending_bind = get(opstream, p_pending_bind)) != null) { + _buffer = get(_pending_bind, p_buffer) + array_append(_buffer, string) + return + } + + # Emit directly + emit(string) +} + +# Emit a SROM opcode followed by up to four optional bytes +function srom_ops_emit_opcode(opstream, opcode, arg0, arg1, arg2, arg3) { + obj_assert_class(opstream, SromOpStream) + + srom_ops_emit(opstream, opcode",\n") + if (arg0 != "") srom_ops_emit(opstream, arg0",\n") + if (arg1 != "") srom_ops_emit(opstream, arg1",\n") + if (arg2 != "") srom_ops_emit(opstream, arg2",\n") + if (arg3 != "") srom_ops_emit(opstream, arg3",\n") +} + +# Emit a SROM opcode and associated integer value, choosing the best +# SROM_OP_DATA variant for encoding the value. +# +# opc: The standard opcode for non-IMM encoded data, or null if none +# opc_imm: The IMM opcode, or null if none +# value: The value to encode +# svalue: Symbolic representation of value to include in output, or null +function srom_ops_emit_int_opcode(opstream, opc, opc_imm, value, svalue, + _width, _offset, _delta) +{ + obj_assert_class(opstream, SromOpStream) + + # Fetch current type width + _width = srom_ops_get_type_width(opstream) + + # Special cases: + if (opc_imm == SPROM_OPCODE_SHIFT_IMM) { + # SHIFT_IMM -- the imm value must be positive and divisible by + # two (shift/2) to use the IMM form. + if (value >= 0 && value % 2 == 0) { + value = (value/2) + opc = null + } else { + opc_imm = null + } + } else if (opc_imm == SPROM_OPCODE_OFFSET_REL_IMM) { + # OFFSET_REL_IMM -- the imm value must be positive, divisible + # by the type width, and relative to the last offset to use + # the IMM form. + + # Assert that callers correctly flushed any pending bind before + # attempting to set a relative offset + if (get(opstream, p_pending_bind) != null) + errorx("can't set relative offset with a pending bind") + + # Fetch current offset, calculate relative value and determine + # whether we can issue an IMM opcode + _offset = get(opstream, p_offset) + _delta = value - _offset + if (_delta >= 0 && + _delta % _width == 0 && + (_delta/_width) <= SPROM_OP_IMM_MAX) + { + srom_ops_emit(opstream, + sprintf("/* %#x + %#x -> %#x */\n", _offset, + _delta, value)) + value = (_delta / _width) + opc = null + } else { + opc_imm = null + } + } + + # If no symbolic representation provided, write the raw value + if (svalue == null) + svalue = value + + # Try to encode as IMM value? + if (opc_imm != null && value >= 0 && value <= SPROM_OP_IMM_MAX) { + srom_ops_emit_opcode(opstream, "("opc_imm"|"svalue")") + return + } + + # Can't encode as immediate; do we have a non-immediate form? + if (opc == null) + errorx("can't encode '" value "' as immediate, and no " \ + "non-immediate form was provided") + + # Determine and emit minimal encoding + # We let the C compiler perform the bit operations, rather than + # trying to wrestle awk's floating point arithmetic + if (value < 0) { + # Only Int8 is used + if (value < Int8Min) + errorx("cannot int8 encode '" value "'") + + srom_ops_emit_opcode(opstream, + "("opc"|"SPROM_OP_DATA_I8")", svalue) + + } else if (value <= UInt8Max) { + + srom_ops_emit_opcode(opstream, + "("opc"|"SPROM_OP_DATA_U8")", svalue) + + } else if (value % _width == 0 && (value / _width) <= UInt8Max) { + + srom_ops_emit_opcode(opstream, + "("opc"|"SPROM_OP_DATA_U8_SCALED")", svalue / _width) + + } else if (value <= UInt16Max) { + + srom_ops_emit_opcode(opstream, + "("opc"|"SPROM_OP_DATA_U16")", + "("svalue" & 0xFF)", + "("svalue" >> 8)") + + } else if (value <= UInt32Max) { + + srom_ops_emit_opcode(opstream, + "("opc"|"SPROM_OP_DATA_U32")", + "("svalue" & 0xFF)", + "(("svalue" >> 8) & 0xFF)", + "(("svalue" >> 16) & 0xFF)", + "(("svalue" >> 24) & 0xFF)") + + } else { + errorx("can't encode '" value "' (too large)") + } +} + +# Emit initial OPCODE_VAR opcode and update opstream state +function srom_ops_reset_var(opstream, var, _vid_prev, _vid, _vid_name, + _type, _base_type) +{ + obj_assert_class(opstream, SromOpStream) + obj_assert_class(var, Var) + + # Flush any pending bind for the previous variable + srom_ops_flush_bind(opstream, 1) + + # Fetch current state + _vid_prev = get(opstream, p_vid) + + _vid = get(var, p_vid) + _vid_name = var_get_macro_name(var, MTypeVarID) + + # Update state + _type = get(var, p_type) + set(opstream, p_vid, _vid) + set(opstream, p_type, type_get_base(_type)) + set(opstream, p_nelem, var_get_array_len(var)) + set(opstream, p_mask, type_get_default_mask(_type)) + set(opstream, p_shift, 0) + set(opstream, p_bind_total, 0) + + # Always provide a human readable comment + srom_ops_emit(opstream, sprintf("/* %s (%#x) */\n", get(var, p_name), + get(opstream, p_offset))) + + # Prefer a single VAR_IMM byte + if (_vid_prev == 0 || _vid <= SPROM_OP_IMM_MAX) { + srom_ops_emit_int_opcode(opstream, + null, SPROM_OPCODE_VAR_IMM, + _vid, _vid_name) + return + } + + # Try encoding as a single VAR_REL_IMM byte + if (_vid_prev <= _vid && (_vid - _vid_prev) <= SPROM_OP_IMM_MAX) { + srom_ops_emit_int_opcode(opstream, + null, SPROM_OPCODE_VAR_REL_IMM, + _vid - _vid_prev, null) + return + } + + # Fall back on a multibyte encoding + srom_ops_emit_int_opcode(opstream, SPROM_OPCODE_VAR, null, _vid, + _vid_name) +} + +# Emit OPCODE_REV/OPCODE_REV_RANGE (if necessary) for a new revision range +function srom_ops_emit_revisions(opstream, revisions, _prev_revs, + _start, _end) +{ + obj_assert_class(opstream, SromOpStream) + _prev_revs = get(opstream, p_revisions) + + if (revrange_equal(_prev_revs, revisions)) + return; + + # Update stream state + set(opstream, p_revisions, revisions) + + _start = get(revisions, p_start) + _end = get(revisions, p_end) + + # Sanity-check range values + if (_start < 0 || _end < 0) + errorx("invalid range: " revrange_to_string(revisions)) + + # If range covers a single revision, and can be encoded within + # SROM_OP_IMM_MAX, we can use the single byte encoding + if (_start == _end && _start <= SPROM_OP_IMM_MAX) { + srom_ops_emit_int_opcode(opstream, + null, SPROM_OPCODE_REV_IMM, _start) + return + } + + # Otherwise, we need to use the two byte range encoding + if (_start > SPROM_OP_REV_RANGE_MAX || _end > SPROM_OP_REV_RANGE_MAX) { + errorx(sprintf("cannot encode range values %s (>= %u)", + revrange_to_string(revisions), SPROM_OP_REV_RANGE_MAX)) + } + + srom_ops_emit_opcode(opstream, + SPROM_OPCODE_REV_RANGE, + sprintf("(%u << %s) | (%u << %s)", + _start, SPROM_OP_REV_START_SHIFT, + _end, SPROM_OP_REV_END_SHIFT)) +} + +# Emit OPCODE_OFFSET (if necessary) for a new offset +function srom_ops_emit_offset(opstream, offset, _prev_offset, _rel_offset, + _bind) +{ + obj_assert_class(opstream, SromOpStream) + + # Flush any pending bind before adjusting the offset + srom_ops_flush_bind(opstream, 0) + + # Fetch current offset + _prev_offset = get(opstream, p_offset) + if (_prev_offset == offset) + return + + # Encode (possibly a relative, 1-byte form) of the offset opcode + srom_ops_emit_int_opcode(opstream, SPROM_OPCODE_OFFSET, + SPROM_OPCODE_OFFSET_REL_IMM, offset, null) + + # Update state + set(opstream, p_offset, offset) +} + +# Emit OPCODE_TYPE (if necessary) for a new type value; this also +# resets the mask to the type default. +function srom_ops_emit_type(opstream, type, _base_type, _prev_type, _prev_mask, + _default_mask) +{ + obj_assert_class(opstream, SromOpStream) + if (!obj_is_instanceof(type, ArrayType)) + obj_assert_class(type, Type) + + _default_mask = type_get_default_mask(type) + _base_type = type_get_base(type) + + # If state already matches the requested type, nothing to be + # done + _prev_type = get(opstream, p_type) + _prev_mask = get(opstream, p_mask) + if (type_equal(_prev_type, _base_type) && _prev_mask == _default_mask) + return + + # Update state + set(opstream, p_type, _base_type) + set(opstream, p_mask, _default_mask) + + # Emit opcode. + if (type_get_const_val(_base_type) <= SPROM_OP_IMM_MAX) { + # Single byte IMM encoding + srom_ops_emit_opcode(opstream, + SPROM_OPCODE_TYPE_IMM "|" type_get_const(_base_type)) + } else { + # Two byte encoding + srom_ops_emit_opcode(opstream, SPROM_OPCODE_TYPE, + type_get_const(_base_type)) + } +} + +# Emit OPCODE_MASK (if necessary) for a new mask value +function srom_ops_emit_mask(opstream, mask, _prev_mask) { + obj_assert_class(opstream, SromOpStream) + _prev_mask = get(opstream, p_mask) + + if (_prev_mask == mask) + return + + set(opstream, p_mask, mask) + srom_ops_emit_int_opcode(opstream, + SPROM_OPCODE_MASK, SPROM_OPCODE_MASK_IMM, + mask, sprintf("0x%x", mask)) +} + +# Emit OPCODE_SHIFT (if necessary) for a new shift value +function srom_ops_emit_shift(opstream, shift, _prev_shift) { + obj_assert_class(opstream, SromOpStream) + _prev_shift = get(opstream, p_shift) + + if (_prev_shift == shift) + return + + set(opstream, p_shift, shift) + srom_ops_emit_int_opcode(opstream, + SPROM_OPCODE_SHIFT, SPROM_OPCODE_SHIFT_IMM, + shift, null) +} + +# Return true if a valid BIND/BINDN encoding exists for the given SKIP_IN +# value, false if the skip values exceed the limits of the bind opcode +# family. +function srom_ops_can_encode_skip_in(skip_in) { + return (skip_in >= SPROM_OP_BIND_SKIP_IN_MIN && + skip_in <= SPROM_OP_BIND_SKIP_IN_MAX) +} + +# Return true if a valid BIND/BINDN encoding exists for the given SKIP_OUT +# value, false if the skip values exceed the limits of the bind opcode +# family. +function srom_ops_can_encode_skip_out(skip_out) { + return (skip_in >= SPROM_OP_BIND_SKIP_IN_MIN && + skip_in <= SPROM_OP_BIND_SKIP_IN_MAX) +} + +# Return true if a valid BIND/BINDN encoding exists for the given skip +# values, false if the skip values exceed the limits of the bind opcode +# family. +function srom_ops_can_encode_skip(skip_in, skip_out) { + return (srom_ops_can_encode_skip_in(skip_in) && + srom_ops_can_encode_skip_out(skip_out)) +} + +# Create a new SromOpBind instance for the given segment +function srom_opbind_new(segment, skip_in, skip_out, _obj, _type, _width, + _offset) +{ + obj_assert_class(segment, SromSegment) + + # Verify that an encoding exists for the skip values + if (!srom_ops_can_encode_skip_in(skip_in)) { + errorx(sprintf("cannot encode SKIP_IN=%d; maximum supported " \ + "range %d-%d", skip_in, + SPROM_OP_BIND_SKIP_IN_MIN, SPROM_OP_BIND_SKIP_IN_MAX)) + } + + if (!srom_ops_can_encode_skip_out(skip_out)) { + errorx(sprintf("cannot encode SKIP_OUT=%d; maximum supported " \ + "range %d-%d", skip_out, + SPROM_OP_BIND_SKIP_OUT_MIN, SPROM_OP_BIND_SKIP_OUT_MAX)) + } + + # Fetch basic segment info + _offset = get(segment, p_offset) + _type = srom_segment_get_base_type(segment) + _width = get(_type, p_width) + + # Construct new instance + _obj = obj_new(SromOpBind) + + set(_obj, p_segment, segment) + set(_obj, p_count, 1) + set(_obj, p_offset, _offset) + set(_obj, p_width, _width) + set(_obj, p_skip_in, skip_in) + set(_obj, p_skip_out, skip_out) + set(_obj, p_buffer, array_new()) + + return (_obj) +} + +# Try to coalesce a BIND for the given segment with an existing bind request, +# returning true on success, or false if the two segments cannot be coalesced +# into the existing request +function srom_opbind_append(bind, segment, skip_out, _bind_seg, _bind_off, + _width, _count, _skip_in, _seg_offset, _delta) +{ + obj_assert_class(bind, SromOpBind) + obj_assert_class(segment, SromSegment) + + # Are the segments compatible? + _bind_seg = get(bind, p_segment) + if (!srom_segment_attributes_equal(_bind_seg, segment)) + return (0) + + # Are the output skip values compatible? + if (get(bind, p_skip_out) != skip_out) + return (0) + + # Find bind offset/count/width/skip + _bind_off = get(bind, p_offset) + _count = get(bind, p_count) + _skip_in = get(bind, p_skip_in) + _width = get(bind, p_width) + + # Fetch new segment's offset + _seg_offset = get(segment, p_offset) + + # If there's only one segment in the bind op, we ned to compute the + # skip value to be used for all later segments (including the + # segment we're attempting to append) + # + # If there's already multiple segments, we just need to verify that + # the bind_offset + (count * width * skip_in) produces the new + # segment's offset + if (_count == 1) { + # Determine the delta between the two segment offsets. This + # must be a multiple of the type width to be encoded + # as a BINDN entry + _delta = _seg_offset - _bind_off + if ((_delta % _width) != 0) + return (0) + + # The skip byte count is calculated as (type width * skip) + _skip_in = _delta / _width + + # Is the skip encodable? + if (!srom_ops_can_encode_skip_in(_skip_in)) + return (0) + + # Save required skip + set(bind, p_skip_in, _skip_in) + } else if (_count > 1) { + # Get the final offset of the binding if we were to add + # one additional segment + _bind_off = _bind_off + (_width * _skip_in * (_count + 1)) + + # If it doesn't match our segment's offset, we can't + # append this segment + if (_bind_off != _seg_offset) + return (0) + } + + # Success! Increment the bind count in the existing bind + set(bind, p_count, _count + 1) + return (1) +} + +# Return true if the given binding operation can be omitted from the output +# if it would be immediately followed by a VAR, VAR_REL_IMM, or EOF opcode. +# +# The bind operatin must be configured with default count, skip_in, and +# skip_out values of 1, and must contain no buffered post-BIND opcodes +function srom_opbind_is_implicit_encodable(bind) { + obj_assert_class(bind, SromOpBind) + + if (get(bind, p_count) != 1) + return (0) + + if (get(bind, p_skip_in) != 1) + return (0) + + if (get(bind, p_skip_out) != 1) + return (0) + + if (array_size(get(bind, p_buffer)) != 0) + return (0) + + return (1) +} + + +# Encode all segment settings for a single offset segment, followed by a bind +# request. +# +# opstream: Opcode stream +# segment: Segment to be written +# continued: If this segment's value should be OR'd with the value of a +# following segment +function srom_ops_emit_segment(opstream, segment, continued, _value, + _bind, _skip_in, _skip_out) +{ + obj_assert_class(opstream, SromOpStream) + obj_assert_class(segment, SromSegment) + + # Determine basic bind parameters + _count = 1 + _skip_in = 1 + _skip_out = continued ? 0 : 1 + + # Try to coalesce with a pending binding + if ((_bind = get(opstream, p_pending_bind)) != null) { + if (srom_opbind_append(_bind, segment, _skip_out)) + return + } + + # Otherwise, flush any pending bind and enqueue our own + srom_ops_flush_bind(opstream, 0) + if (get(opstream, p_pending_bind)) + errorx("bind not flushed!") + + # Encode type + _value = get(segment, p_type) + srom_ops_emit_type(opstream, _value) + + # Encode offset + _value = get(segment, p_offset) + srom_ops_emit_offset(opstream, _value) + + # Encode mask + _value = get(segment, p_mask) + srom_ops_emit_mask(opstream, _value) + + # Encode shift + _value = get(segment, p_shift) + srom_ops_emit_shift(opstream, _value) + + # Enqueue binding with opstream + _bind = srom_opbind_new(segment, _skip_in, _skip_out) + set(opstream, p_pending_bind, _bind) +} + +# (private) Adjust the stream's input offset by applying the given bind +# operation's skip_in * width * count. +function _srom_ops_apply_bind_offset(opstream, bind, _count, _offset, _width, + _skip_in, _opstream_offset) +{ + obj_assert_class(opstream, SromOpStream) + obj_assert_class(bind, SromOpBind) + + _opstream_offset = get(opstream, p_offset) + _offset = get(bind, p_offset) + if (_opstream_offset != _offset) + errorx("stream/bind offset state mismatch") + + _count = get(bind, p_count) + _width = get(bind, p_width) + _skip_in = get(bind, p_skip_in) + + set(opstream, p_offset, + _opstream_offset + ((_width * _skip_in) * _count)) +} + +# (private) Write a bind instance and all buffered opcodes +function _srom_ops_emit_bind(opstream, bind, _count, _skip_in, _skip_out, + _off_start, _width, _si_signbit, _written, _nbuffer, _buffer) +{ + obj_assert_class(opstream, SromOpStream) + obj_assert_class(bind, SromOpBind) + + # Assert that any pending bind state has already been cleared + if (get(opstream, p_pending_bind) != null) + errorx("cannot flush bind with an existing pending_bind active") + + # Fetch (and assert valid) our skip values + _skip_in = get(bind, p_skip_in) + _skip_out = get(bind, p_skip_out) + + if (!srom_ops_can_encode_skip(_skip_in, _skip_out)) + errorx("invalid skip values in buffered bind") + + # Determine SKIP_IN sign bit + _si_signbit = "0" + if (_skip_in < 0) + _si_signbit = SPROM_OP_BIND_SKIP_IN_SIGN + + # Emit BIND/BINDN opcodes until the full count is encoded + _count = get(bind, p_count) + while (_count > 0) { + if (_count > 1 && _count <= SPROM_OP_IMM_MAX && + _skip_in == 1 && _skip_out == 1) + { + # The one-byte BINDN form requires encoding the count + # as a IMM, and has an implicit in/out skip of 1. + srom_ops_emit_opcode(opstream, + "("SPROM_OPCODE_DO_BINDN_IMM"|"_count")") + _count -= _count + + } else if (_count > 1) { + # The two byte BINDN form can encode skip values and a + # larger U8 count + _written = min(_count, UInt8Max) + + srom_ops_emit_opcode(opstream, + sprintf("(%s|%s|(%u<<%s)|(%u<<%s))", + SPROM_OPCODE_DO_BINDN, + _si_signbit, + abs(_skip_in), SPROM_OP_BIND_SKIP_IN_SHIFT, + _skip_out, SPROM_OP_BIND_SKIP_OUT_SHIFT), + _written) + _count -= _written + + } else { + # The 1-byte BIND form can encode the same SKIP values + # as the 2-byte BINDN, with a implicit count of 1 + srom_ops_emit_opcode(opstream, + sprintf("(%s|%s|(%u<<%s)|(%u<<%s))", + SPROM_OPCODE_DO_BIND, + _si_signbit, + abs(_skip_in), SPROM_OP_BIND_SKIP_IN_SHIFT, + _skip_out, SPROM_OP_BIND_SKIP_OUT_SHIFT)) + _count-- + } + } + + # Update the stream's input offset + _srom_ops_apply_bind_offset(opstream, bind) + + # Write any buffered post-BIND opcodes + _buffer = get(bind, p_buffer) + _nbuffer = array_size(_buffer) + for (_i = 0; _i < _nbuffer; _i++) + srom_ops_emit(opstream, array_get(_buffer, _i)) +} + +# Flush any buffered binding +function srom_ops_flush_bind(opstream, allow_implicit, _bind, _bind_total) +{ + obj_assert_class(opstream, SromOpStream) + + # If no pending bind, nothing to flush + if ((_bind = get(opstream, p_pending_bind)) == null) + return + + # Check the per-variable bind count to determine whether + # we can encode an implicit bind. + # + # If there have been any explicit bind statements, implicit binding + # cannot be used. + _bind_total = get(opstream, p_bind_total) + if (allow_implicit && _bind_total > 0) { + # Disable implicit encoding; explicit bind statements have + # been issued for this variable previously. + allow_implicit = 0 + } + + # Increment bind count + set(opstream, p_bind_total, _bind_total + 1) + + # Clear the property value + set(opstream, p_pending_bind, null) + + # If a pending bind operation can be encoded as an implicit bind, + # emit a descriptive comment and update the stream state. + # + # Otherwise, emit the full set of bind opcode(s) + _base_off = get(opstream, p_offset) + if (allow_implicit && srom_opbind_is_implicit_encodable(_bind)) { + # Update stream's input offset + _srom_ops_apply_bind_offset(opstream, _bind) + } else { + _srom_ops_emit_bind(opstream, _bind) + } + + # Provide bind information as a comment + srom_ops_emit(opstream, + sprintf("/* bind (%s @ %#x -> %#x) */\n", + type_to_string(get(opstream, p_type)), + _base_off, get(opstream, p_offset))) + + # Clean up + obj_delete(_bind) +} + +# Write OPCODE_EOF after flushing any buffered writes +function srom_ops_emit_eof(opstream) { + obj_assert_class(opstream, SromOpStream) + + # Flush any buffered writes + srom_ops_flush_bind(opstream, 1) + + # Emit an explicit VAR_END opcode for the last entry + srom_ops_emit_opcode(opstream, SPROM_OPCODE_VAR_END) + + # Emit EOF + srom_ops_emit_opcode(opstream, SPROM_OPCODE_EOF) +} + +# Write the SROM offset segment bindings to the opstream +function write_srom_offset_bindings(opstream, offsets, + _noffsets, _offset, _segs, _nsegs, _segment, _cont, + _i, _j) +{ + _noffsets = array_size(offsets) + for (_i = 0; _i < _noffsets; _i++) { + # Encode each segment in this offset + _offset = array_get(offsets, _i) + _segs = get(_offset, p_segments) + _nsegs = array_size(_segs) + + for (_j = 0; _j < _nsegs; _j++) { + _segment = array_get(_segs, _j) + _cont = 0 + + # Should this value be OR'd with the next segment? + if (_j+1 < _nsegs) + _cont = 1 + + # Encode segment + srom_ops_emit_segment(opstream, _segment, _cont) + } + } +} + +# Write the SROM entry stream for a SROM entry to the output file +function write_srom_entry_bindings(entry, opstream, _var, _vid, + _var_type, _entry_type, _offsets, _noffsets) +{ + _var = get(entry, p_var) + _vid = get(_var, p_vid) + + # Encode revision switch. This resets variable state, so must + # occur before any variable definitions to which it applies + srom_ops_emit_revisions(opstream, get(entry, p_revisions)) + + # Encode variable ID + srom_ops_reset_var(opstream, _var, _vid) + output_depth++ + + # Write entry-specific array length (SROM layouts may define array + # mappings with fewer elements than in the variable definition) + if (srom_entry_has_array_type(entry)) { + _var_type = get(_var, p_type) + _entry_type = get(entry, p_type) + + # If the array length differs from the variable default, + # write an OPCODE_EXT_NELEM entry + if (type_get_nelem(_var_type) != type_get_nelem(_entry_type)) { + srom_ops_emit_opcode(opstream, SPROM_OPCODE_NELEM, + srom_entry_get_array_len(entry)) + } + } + + # Write offset segment bindings + _offsets = get(entry, p_offsets) + write_srom_offset_bindings(opstream, _offsets) + output_depth-- +} + +# Write a SROM layout binding opcode table to the output file +function write_srom_bindings(layout, _varname, _var, _all_entries, + _nall_entries, _entries, _nentries, _entry, _opstream, _i) +{ + _varname = srom_layout_get_variable_name(layout) + _all_entries = get(layout, p_entries) + _opstream = srom_ops_new(layout) + + # + # Collect all entries to be included in the output, and then + # sort by their variable's assigned ID (ascending). + # + # The variable IDs were previously assigned in lexigraphical sort + # order; since the variable *offsets* tend to match this order, this + # works out well for our compact encoding, allowing us to make use of + # compact relative encoding of both variable IDs and variable offsets. + # + _entries = array_new() + _nall_entries = array_size(_all_entries) + for (_i = 0; _i < _nall_entries; _i++) { + _entry = array_get(_all_entries, _i) + _var = get(_entry, p_var) + + # Skip internal variables + if (var_is_internal(_var)) + continue + + # Sanity check variable ID assignment + if (get(_var, p_vid) == "") + errorx("missing variable ID for " obj_to_string(_var)) + + array_append(_entries, _entry) + } + + # Sort entries by variable ID, ascending + array_sort(_entries, prop_path_create(p_var, p_vid)) + + # Emit all entry binding opcodes + emit("static const uint8_t " _varname "[] = {\n") + output_depth++ + + _nentries = array_size(_entries) + for (_i = 0; _i < _nentries; _i++) { + _entry = array_get(_entries, _i) + write_srom_entry_bindings(_entry, _opstream) + } + + # Flush and write EOF + srom_ops_emit_eof(_opstream) + + output_depth-- + emit("};\n") + + obj_delete(_opstream) + obj_delete(_entries) +} + +# Write the BHND_NVAR__ID #defines to the output file +function write_data_defines(output_vars, _noutput_vars, _tab_align, _var, + _macro, _macros, _num_macros, _i) +{ + # Produce our array of #defines + _num_macros = 0 + _noutput_vars = array_size(output_vars) + for (_i = 0; _i < _noutput_vars; _i++) { + _var = array_get(output_vars, _i) + + # Variable ID + _macro = var_get_macro(_var, MTypeVarID, get(_var, p_vid)) + _macros[_num_macros++] = _macro + } + + # Calculate value tab alignment position for our macros + _tab_align = macros_get_tab_alignment(_macros, _num_macros) + + # Write the #defines + emit("/* ID constants provide an index into the variable array */\n") + for (_i = 0; _i < _num_macros; _i++) + write_macro_define(_macros[_i], _tab_align) + emit("\n\n"); +} + +# Calculate the common tab alignment to be used with a set of prefix strings +# with the given maximum length +function tab_alignment(max_len, _tab_align) { + _tab_align = max_len + _tab_align += (TAB_WIDTH - (_tab_align % TAB_WIDTH)) % TAB_WIDTH + _tab_align /= TAB_WIDTH + + return (_tab_align) +} + +# Generate and return a tab string that can be appended to a string of +# `strlen` to pad the column out to `align_to` +# +# Note: If the string from which strlen was derived contains tabs, the result +# is undefined +function tab_str(strlen, align_to, _lead, _pad, _result, _i) { + _lead = strlen + _lead -= (_lead % TAB_WIDTH); + _lead /= TAB_WIDTH; + + # Determine required padding to reach the desired alignment + if (align_to >= _lead) + _pad = align_to - _lead; + else + _pad = 1; + + for (_i = 0; _i < _pad; _i++) + _result = _result "\t" return (_result) } -# -# Sort a contiguous integer-indexed array, using standard awk comparison -# operators over its values. -# -function sort (array) { - # determine array size - _sort_alen = 0 - for (_ssort_key in array) - _sort_alen++ +# Write a MacroDefine constant, padding the constant out to `align_to` +function write_macro_define(macro, align_to, _tabstr, _i) { + # Determine required padding to reach the desired alignment + _tabstr = tab_str(length(get(macro, p_name)), align_to) - if (_sort_alen <= 1) - return - - # perform sort - _qsort(array, 0, _sort_alen-1) + emit("#define\t" get(macro, p_name) _tabstr get(macro, p_value) "\n") } -function _qsort (array, first, last) +# Calculate the tab alignment to be used with a given integer-indexed array +# of Macro instances. +function macros_get_tab_alignment(macros, macros_len, _macro, _max_len, _i) { + _max_len = 0 + for (_i = 0; _i < macros_len; _i++) { + _macro = macros[_i] + _max_len = max(_max_len, length(get(_macro, p_name))) + } + + return (tab_alignment(_max_len)) +} + +# Variable group block +$1 == "group" && in_parser_context(NVRAM) { + parse_variable_group() +} + +# Variable definition +(($1 ~ VACCESS_REGEX && $2 ~ TYPES_REGEX) || $1 ~ TYPES_REGEX) && + in_parser_context(SymbolContext) \ +{ + parse_variable_defn() +} + +# Variable "fmt" parameter +$1 == "fmt" && in_parser_context(Var) { + parse_variable_param($1) + next +} + +# Variable "all1" parameter +$1 == "all1" && in_parser_context(Var) { + parse_variable_param($1) + next +} + +# Variable desc/help parameters +($1 == "desc" || $1 == "help") && in_parser_context(Var) { + parse_variable_param($1) + next +} + +# SROM layout block +$1 == "srom" && in_parser_context(NVRAM) { + parse_srom_layout() +} + + +# SROM layout revision filter block +$1 == "srom" && in_parser_context(SromLayout) { + parse_srom_layout_filter() +} + +# SROM layout variable entry +$1 ~ "("OFF_REGEX"):$" && \ + (in_parser_context(SromLayout) || in_parser_context(SromLayoutFilter)) \ +{ + parse_srom_variable_entry() +} + + +# SROM entry segment +$1 ~ "("REL_OFF_REGEX"|"OFF_REGEX")[:,|]?" && in_parser_context(SromEntry) { + parse_srom_entry_segments() +} + +# Skip comments and blank lines +/^[ \t]*#/ || /^$/ { + next +} + +# Close blocks +/}/ && !in_parser_context(NVRAM) { + while (!in_parser_context(NVRAM) && $0 ~ "}") { + parser_state_close_block(); + } + next +} + +# Report unbalanced '}' +/}/ && in_parser_context(NVRAM) { + error("extra '}'") +} + +# Invalid variable type +$1 && in_parser_context(SymbolContext) { + error("unknown type '" $1 "'") +} + +# Generic parse failure +{ + error("unrecognized statement") +} + +# Create a class instance with the given name +function class_new(name, superclass, _class) { + if (_class != null) + errorx("class_get() must be called with one or two arguments") + + # Look for an existing class instance + if (name in _g_class_names) + errorx("redefining class: " name) + + # Create and register the class object + _class = obj_new(superclass) + _g_class_names[name] = _class + _g_obj[_class,OBJ_IS_CLS] = 1 + _g_obj[_class,CLS_NAME] = name + + return (_class) +} + +# Return the class instance with the given name +function class_get(name) { + if (name in _g_class_names) + return (_g_class_names[name]) + + errorx("no such class " name) +} + +# Return the name of cls +function class_get_name(cls) { + if (cls == null) { + warnx("class_get_name() called with null class") + return "" + } + + if (!obj_is_class(cls)) + errorx(cls " is not a class object") + + return (_g_obj[cls,CLS_NAME]) +} + +# Return true if the given property property ID is defined on class +function class_has_prop_id(class, prop_id, _super) { + if (_super != null) + errorx("class_has_prop_id() must be called with two arguments") + + if (class == null) + return (0) + + # Check class<->prop cache + if ((class, prop_id) in _g_class_prop_cache) + return (1) + + # Otherwise, slow path + if (!obj_is_class(class)) + errorx(class " is not a class object") + + if (_super != null) + errorx("class_has_prop_id() must be called with two arguments") + + for (_super = class; _super != null; _super = obj_get_class(_super)) { + if (!((_super,CLS_PROP,prop_id) in _g_obj)) + continue + + # Found; add to class<->prop cache + _g_class_prop_cache[class,prop_id] = 1 + return (1) + } + + return (0) +} + +# Return true if the given property prop is defined on class +function class_has_property(class, prop) { + if (!(PROP_ID in prop)) + return (0) + + return (class_has_prop_id(class, prop[PROP_ID])) +} + +# Define a `prop` on `class` with the given `name` string +function class_add_prop(class, prop, name, _prop_id) { + if (_prop_id != null) + errorx("class_add_prop() must be called with three arguments") + + # Check for duplicate property definition + if (class_has_property(class, prop)) + errorx("property " prop[PROP_NAME] " already defined on " \ + class_get_name(class)) + + # Init property IDs + if (_g_prop_ids == null) + _g_prop_ids = 1 + + # Get (or create) new property entry + if (name in _g_prop_names) { + _prop_id = _g_prop_names[name] + } else { + _prop_id = _g_prop_ids++ + _g_prop_names[name] = _prop_id + _g_props[_prop_id] = name + + prop[PROP_NAME] = name + prop[PROP_ID] = _prop_id + } + + # Add to class definition + _g_obj[class,CLS_PROP,prop[PROP_ID]] = name + return (name) +} + +# Return the property ID for a given class-defined property +function class_get_prop_id(class, prop) { + if (class == null) + errorx("class_get_prop_id() on null class") + + if (!class_has_property(class, prop)) { + errorx("requested undefined property '" prop[PROP_NAME] "on " \ + class_get_name(class)) + } + + return (prop[PROP_ID]) +} + +# Return the property ID for a given class-defined property name +function class_get_named_prop_id(class, name, _prop_id) { + if (class == null) + errorx("class_get_prop_id() on null class") + + if (!(name in _g_prop_names)) + errorx("requested undefined property '" name "'") + + _prop_id = _g_prop_names[name] + + if (!class_has_prop_id(class, _prop_id)) { + errorx("requested undefined property '" _g_props[_prop_id] \ + "' on " class_get_name(class)) + } + + return (_prop_id) +} + +# Create a new instance of the given class +function obj_new(class, _obj) { + if (_obj != null) + errorx("obj_new() must be called with one argument") + + if (_g_obj_ids == null) + _g_obj_ids = 1 + + # Assign ID and set superclass + _obj = _g_obj_ids++ + _g_obj[_obj,OBJ_SUPER] = class + + return (_obj) +} + +# obj_delete() support for Map instances +function _obj_delete_map(obj, _prefix, _key) { + obj_assert_class(obj, Map) + _prefix = "^" obj SUBSEP + for (_key in _g_maps) { + if (!match(_key, _prefix) && _key != obj) + continue + delete _g_maps[_key] + } +} + +# obj_delete() support for Array instances +function _obj_delete_array(obj, _size, _i) { + obj_assert_class(obj, Array) + _size = array_size(obj) + + for (_i = 0; _i < _size; _i++) + delete _g_arrays[obj,OBJ_PROP,_i] +} + +# Destroy all metadata associated with the given object +function obj_delete(obj, _prop_id, _prop_name, _prefix, _key, _size, _i) { + if (obj_is_class(obj)) + errorx("cannot delete class objects") + + # Handle classes that use external global array storage + # for effeciency + if (obj_is_instanceof(obj, Map)) { + _obj_delete_map(obj) + } else if (obj_is_instanceof(obj, Array)) { + _obj_delete_array(obj) + } + + # Delete all object properties + for (_prop_name in _g_prop_names) { + if (!obj_has_prop_id(obj, _prop_id)) + continue + + _prop_id = _g_prop_names[_prop_name] + delete _g_obj[obj,OBJ_PROP,_prop_id] + delete _g_obj_nr[obj,OBJ_PROP,_prop_id] + } + + # Delete instance state + delete _g_obj[obj,OBJ_IS_CLS] + delete _g_obj[obj,OBJ_SUPER] +} + +# Print an object's unique ID, class, and properties to +# stdout +function obj_dump(obj, _pname, _prop_id, _prop_val) { + print(class_get_name(obj_get_class(obj)) "<" obj ">:") + + # Dump all properties + for (_pname in _g_prop_names) { + _prop_id = _g_prop_names[_pname] + + if (!obj_has_prop_id(obj, _prop_id)) + continue + + _prop_val = prop_get(obj, _prop_id) + printf("\t%s: %s\n", _pname, _prop_val) + } +} + +# Return true if obj is a class object +function obj_is_class(obj) { + return (_g_obj[obj,OBJ_IS_CLS] == 1) +} + +# Return the class of obj, if any. +function obj_get_class(obj) { + if (obj == null) + errorx("obj_get_class() on null object") + return (_g_obj[obj,OBJ_SUPER]) +} + +# Return true if obj is an instance of the given class +function obj_is_instanceof(obj, class, _super) { + if (_super != null) + errorx("obj_is_instanceof() must be called with two arguments") + + if (!obj_is_class(class)) + errorx(class " is not a class object") + + if (obj == null) { + errorx("obj_is_instanceof() called with null obj (class " \ + class_get_name(class) ")") + } + + for (_super = obj_get_class(obj); _super != null; + _super = obj_get_class(_super)) + { + if (_super == class) + return (1) + } + + return (0) +} + +# Default object shallow equality implementation. Returns true if the two +# objects share a common superclass and have identity equality across all defined +# properties. +function obj_trivially_equal(lhs, rhs, _class, _pname, _prop_id) { + # Simple case + if (lhs == rhs) + return (1) + + # Must share a common superclass + _class = obj_get_class(lhs) + if (_class != obj_get_class(rhs)) + return (0) + + # Compare all properties + _prop_count = 0 + for (_pname in _g_prop_names) { + _prop_id = _g_prop_names[_pname] + + if (!class_has_prop_id(_class, _prop_id)) + continue + + if (prop_get(lhs, _prop_id) != prop_get(rhs, _prop_id)) + return (0) + } + + # All properties are trivially equal + return (1) +} + + +# Return a debug string representation of an object's unique ID, class, and +# properties +function obj_to_string(obj, _pname, _prop_id, _prop_val, _prop_count, _result) { + _result = class_get_name(obj_get_class(obj)) "<" obj ">: { " + + # Fetch all properties + _prop_count = 0 + for (_pname in _g_prop_names) { + _prop_id = _g_prop_names[_pname] + + if (!obj_has_prop_id(obj, _prop_id)) + continue + + if (_prop_count >= 0) + _result = _result ", " + + _result = _result sprintf("\t%s: %s\n", _pname, _prop_val) + _prop_count++ + } + + return (_result " }") +} + +# Assert that obj is an instance of the given class +function obj_assert_class(obj, class) { + if (!obj_is_instanceof(obj, class)) { + errorx(class_get_name(obj_get_class(obj)) "<" obj "> is not " \ + "an instance of " class_get_name(class)) + } +} + +# Return true if the given property prop is defined by the object's superclass +function obj_has_property(obj, prop, _class) { + if (obj == null) + errorx("obj_has_property() on null object") + + _class = obj_get_class(obj) + return (class_has_property(_class, prop)) +} + +# Return true if the given property ID is defined by the object's superclass +function obj_has_prop_id(obj, prop_id, _class) { + if (obj == null) + errorx("obj_has_prop_id() on null object") + + _class = obj_get_class(obj) + return (class_has_prop_id(_class, prop_id)) +} + +# Return the line (NR) at which a given property ID was set on the object +# Will throw an error if the property has not been set on obj +function obj_get_prop_id_nr(obj, prop_id) { + if (obj == null) + errorx("obj_get_prop_id_nr() on null object") + + if (!obj_has_prop_id(obj, prop_id)) { + errorx("requested undefined property '" _g_props[prop_id] \ + "' (" prop_id ") on " obj_to_string(obj)) + } + + # Fetch NR + if ((obj,OBJ_PROP,prop_id) in _g_obj_nr) + return (_g_obj_nr[obj,OBJ_PROP,prop_id]) + + errorx("property '" _g_props[prop_id] "' (" prop_id ") not " \ + "previously set on " obj_to_string(obj)) +} + +# Return the line (NR) at which a given property was set on the object +# Will throw an error if the property has not been set on obj +function obj_get_prop_nr(obj, prop) { + return (obj_get_prop_id_nr(obj, prop[PROP_ID])) +} + +# Return an abstract property ID for a given property +function obj_get_prop_id(obj, prop) { + if (obj == null) + errorx("obj_get_prop_id() on null object") + + return (class_get_prop_id(obj_get_class(obj), prop)) +} + + +# Return the property ID for a given property name +function obj_get_named_prop_id(obj, name) { + if (obj == null) + errorx("obj_get_named_prop_id() on null object") + + return (class_get_named_prop_id(obj_get_class(obj), name)) +} + +# Set a property on obj +function set(obj, prop, value, _class) { + return (prop_set(obj, prop[PROP_ID], value)) +} + +# Get a property value defined on obj +function get(obj, prop, _class) { + return (prop_get(obj, prop[PROP_ID])) +} + +# Set a property on obj, using a property ID returned by obj_get_prop_id() or +# class_get_prop_id() +function prop_set(obj, prop_id, value, _class) { + if (obj == null) { + errorx("setting property '" _g_props[prop_id] \ + "' on null object") + } + + _class = obj_get_class(obj) + if (_class == null) + errorx(obj " has no superclass") + + if (!class_has_prop_id(_class, prop_id)) { + errorx("requested undefined property '" _g_props[prop_id] \ + "' (" prop_id ") on " class_get_name(_class)) + } + + # Track the line on which the property was set + _g_obj_nr[obj,OBJ_PROP,prop_id] = NR + _g_obj[obj,OBJ_PROP,prop_id] = value +} + +# Convert a property ID to a property path. +function prop_id_to_path(prop_id) { + if (!(prop_id in _g_props)) + errorx("'" prop_id "' is not a property ID") + + # Convert to path string representation + return (""prop_id) +} + +# Convert a property to a property path. +function prop_to_path(prop) { + if (!(PROP_ID in prop)) + errorx("prop_to_path() called with non-property head") + + return (prop_id_to_path(prop[PROP_ID])) +} + +# Create a property path from head and tail properties +# Additional properties may be appended via prop_path_append() or +# prop_path_append_id() +function prop_path_create(head, tail) { + if (!(PROP_ID in head)) + errorx("prop_path() called with non-property head") + + if (!(PROP_ID in tail)) + errorx("prop_path() called with non-property tail") + + return (head[PROP_ID] SUBSEP tail[PROP_ID]) +} + +# Append a property to the given property path +function prop_path_append(path, tail) { + if (!(PROP_ID in tail)) + errorx("prop_path_append() called with non-property tail") + + return (prop_path_append_id(path, tail[PROP_ID])) +} + +# Append a property ID to the given property path +function prop_path_append_id(path, tail_id) { + if (!(tail_id in _g_props)) + errorx("'" tail_id "' is not a property ID") + + return (path SUBSEP tail_id) +} + +# Fetch a value from obj using a property path previously returned by +# prop_path_create(), prop_to_path(), etc. +function prop_get_path(obj, prop_path, _class, _prop_ids, _nprop_ids, _next, + _prop_head, _prop_len, _prop_tail) +{ + if (obj == null) { + errorx("requested property path '" \ + gsub(SUBSEP, ".", prop_path) "' on null object") + } + + # Try the cache first + _class = obj_get_class(obj) + if ((_class,prop_path,PPATH_HEAD) in _g_ppath_cache) { + _prop_head = _g_ppath_cache[_class,prop_path,PPATH_HEAD] + _next = prop_get(obj, _prop_head) + + if ((_class,prop_path,PPATH_TAIL) in _g_ppath_cache) { + _prop_tail = _g_ppath_cache[_class,prop_path,PPATH_TAIL] + return (prop_get_path(_next, _prop_tail)) + } + + return (_next) + } + + # Parse the head/tail of the property path and add to cache + _nprop_ids = split(prop_path, _prop_ids, SUBSEP) + if (_nprop_ids == 0) + errorx("empty property path") + _prop_head = _prop_ids[1] + _g_ppath_cache[_class,prop_path,PPATH_HEAD] = _prop_head + + if (_nprop_ids > 1) { + _prop_len = length(_prop_head) + _prop_tail = substr(prop_path, _prop_len+2) + + # Add to cache + _g_ppath_cache[_class,prop_path,PPATH_TAIL] = _prop_tail + } + + # Recursively call out implementation, this time fetching from + # cache + return (prop_get_path(obj, prop_path)) +} + +# Fetch a value property value from obj, using a property ID returned by +# obj_get_prop_id() or class_get_prop_id() +function prop_get(obj, prop_id, _class) { + if (obj == null) { + errorx("requested property '" _g_props[prop_id] \ + "' on null object") + } + + _class = obj_get_class(obj) + if (_class == null) + errorx(obj " has no superclass") + + if (!class_has_prop_id(_class, prop_id)) { + errorx("requested undefined property '" _g_props[prop_id] \ + "' (" prop_id ") on " class_get_name(_class)) + } + + return (_g_obj[obj,OBJ_PROP,prop_id]) +} + +# Create a new MacroType instance +function macro_type_new(name, const_suffix, _obj) { + _obj = obj_new(MacroType) + + set(_obj, p_name, name) + set(_obj, p_const_suffix, const_suffix) + + return (_obj) +} + +# Create a new MacroDefine instance +function macro_new(name, value, _obj) { + _obj = obj_new(MacroDefine) + set(_obj, p_name, name) + set(_obj, p_value, value) + + return (_obj) +} + +# Create an empty array; this uses _g_arrays to store integer +# keys/values under the object's property prefix. +function array_new(_obj) { + _obj = obj_new(Array) + set(_obj, p_count, 0) + + return (_obj) +} + +# Return the number of elements in the array +function array_size(array) { + obj_assert_class(array, Array) + return (get(array, p_count)) +} + +# Return true if the array is empty +function array_empty(array) { + return (array_size(array) == 0) +} + +# Append a value to the array +function array_append(array, value, _i) { + obj_assert_class(array, Array) + + _i = get(array, p_count) + _g_arrays[array,OBJ_PROP,_i] = value + set(array, p_count, _i+1) +} + +# Set an array value +# An error will be thrown if the idx is outside array bounds +function array_set(array, idx, value) { + obj_assert_class(array, Array) + + if (!((array,OBJ_PROP,idx) in _g_arrays)) + errorx(idx " out of range of array " obj_to_string(array)) + + _g_arrays[array,OBJ_PROP,idx] = value +} + +# Return value at the given index from the array +# An error will be thrown if 'idx' is outside the array bounds +function array_get(array, idx) { + obj_assert_class(array, Array) + + if (!((array,OBJ_PROP,idx) in _g_arrays)) + errorx(idx " out of range of array " obj_to_string(array)) + + return (_g_arrays[array,OBJ_PROP,idx]) +} + + +# +# Sort an array, using standard awk comparison operators over its values. +# +# If `prop_path` is non-NULL, the corresponding property path (or property ID) +# will be fetched from each array element and used as the sorting value. +# +function array_sort(array, prop_path, _size) { + obj_assert_class(array, Array) + + _size = array_size(array) + if (_size <= 1) + return + + _qsort(array, prop_path, 0, _size-1) +} + +function _qsort_get_key(array, idx, prop_path, _v) { + _v = array_get(array, idx) + + if (prop_path == null) + return (_v) + + return (prop_get_path(_v, prop_path)) +} + +function _qsort(array, prop_path, first, last, _qpivot, _qpivot_val, _qleft, + _qleft_val, _qright, _qright_val) { if (first >= last) return @@ -501,23 +2319,23 @@ function _qsort (array, first, last) _qleft = first _qright = last - _qpivot_val = array[_qpivot] + _qpivot_val = _qsort_get_key(array, _qpivot, prop_path) # partition while (_qleft <= _qright) { - while (array[_qleft] < _qpivot_val) + while (_qsort_get_key(array, _qleft, prop_path) < _qpivot_val) _qleft++ - while (array[_qright] > _qpivot_val) + while (_qsort_get_key(array, _qright, prop_path) > _qpivot_val) _qright-- # swap if (_qleft <= _qright) { - _qleft_val = array[_qleft] - _qright_val = array[_qright] + _qleft_val = array_get(array, _qleft) + _qright_val = array_get(array, _qright) - array[_qleft] = _qright_val - array[_qright] = _qleft_val + array_set(array, _qleft, _qright_val) + array_set(array, _qright, _qleft_val) _qleft++ _qright-- @@ -525,23 +2343,1111 @@ function _qsort (array, first, last) } # sort the partitions - _qsort(array, first, _qright) - _qsort(array, _qleft, last) + _qsort(array, prop_path, first, _qright) + _qsort(array, prop_path, _qleft, last) +} + + +# +# Join all array values with the given separator +# +# If `prop_path` is non-NULL, the corresponding property path (or property ID) +# will be fetched from each array value and included in the result, rather than +# immediate array value +# +function array_join(array, sep, prop_path, _i, _size, _value, _result) { + obj_assert_class(array, Array) + + _result = "" + _size = array_size(array) + for (_i = 0; _i < _size; _i++) { + # Fetch the value (and optionally, a target property) + _value = array_get(array, _i) + if (prop_path != null) + _value = prop_get_path(_value, prop_path) + + if (_i+1 < _size) + _result = _result _value sep + else + _result = _result _value + } + + return (_result) +} + +# Return the first value in the array, or null if empty +function array_first(array) { + obj_assert_class(array, Array) + + if (array_size(array) == 0) + return (null) + else + return (array_get(array, 0)) +} + +# Return the last value in the array, or null if empty +function array_tail(list, _size) { + obj_assert_class(array, Array) + + _size = array_size(array) + if (_size == 0) + return (null) + else + return (array_get(array, _size-1)) +} + +# Create an empty hash table; this uses the _g_maps array to store arbitrary +# keys/values under the object's property prefix. +function map_new(_obj) { + _obj = obj_new(Map) + return (_obj) +} + +# Add `key` with `value` to `map` +function map_set(map, key, value) { + obj_assert_class(map, Map) + _g_maps[map,OBJ_PROP,key] = value +} + +# Remove `key` from the map +function map_remove(map, key) { + obj_assert_class(map, Map) + delete _g_maps[map,OBJ_PROP,key] +} + +# Return true if `key` is found in `map`, false otherwise +function map_contains(map, key) { + obj_assert_class(map, Map) + return ((map,OBJ_PROP,key) in _g_maps) +} + +# Fetch the value of `key` from the map. Will throw an error if the +# key does not exist +function map_get(map, key) { + obj_assert_class(map, Map) + return _g_maps[map,OBJ_PROP,key] +} + +# Create and return a new list containing all defined values in `map` +function map_to_array(map, _key, _prefix, _values) { + obj_assert_class(map, Map) + + _values = array_new() + _prefix = "^" map SUBSEP OBJ_PROP SUBSEP + for (_key in _g_maps) { + if (!match(_key, _prefix)) + continue + + array_append(_values, _g_maps[_key]) + } + + return (_values) +} + +# Create a new Type instance +function type_new(name, width, signed, constant, array_constant, fmt, mask, + constant_value, array_constant_value, _obj) +{ + obj_assert_class(fmt, Fmt) + + _obj = obj_new(Type) + set(_obj, p_name, name) + set(_obj, p_width, width) + set(_obj, p_signed, signed) + set(_obj, p_const, constant) + set(_obj, p_const_val, constant_value) + set(_obj, p_array_const, array_constant) + set(_obj, p_array_const_val, array_constant_value) + set(_obj, p_default_fmt, fmt) + set(_obj, p_mask, mask) + + return (_obj) +} + +# Return true if two types are equal +function type_equal(lhs, rhs) { + # Simple case + if (lhs == rhs) + return (1) + + # Must share a common class + if (obj_get_class(lhs) != obj_get_class(rhs)) + return (0) + + # Handle ArrayType equality + if (obj_is_instanceof(lhs, ArrayType)) { + # Size must be equal + if (get(lhs, p_count) != get(rhs, p_count)) + return (0) + + # The base types must be equal + return (type_equal(type_get_base(lhs), type_get_base(rhs))) + } + + # Handle Type equality -- we just check for trivial identity + # equality of all members + obj_assert_class(lhs, Type) + return (obj_trivially_equal(lhs, rhs)) +} + +# Return the type's default value mask. If the type is an array type, +# the default mask of the base type will be returned. +function type_get_default_mask(type) { + if (obj_is_instanceof(type, ArrayType)) + return (type_get_default_mask(type_get_base(type))) + + obj_assert_class(type, Type) + return (get(type, p_mask)) +} + +# Return the type's C constant representation +function type_get_const(type) { + if (obj_is_instanceof(type, ArrayType)) + return (get(type_get_base(type), p_array_const)) + + obj_assert_class(type, Type) + return (get(type, p_const)) +} + +# Return the type's C constant integer value +function type_get_const_val(type) { + if (obj_is_instanceof(type, ArrayType)) + return (get(type_get_base(type), p_array_const_val)) + + obj_assert_class(type, Type) + return (get(type, p_const_val)) +} + +# Return an array type's element count, or 1 if the type is not +# an array type +function type_get_nelem(type) { + if (obj_is_instanceof(type, ArrayType)) + return (get(type, p_count)) + + obj_assert_class(type, Type) + return (1) +} + +# Return the base type for a given type instance. +function type_get_base(type) { + if (obj_is_instanceof(type, ArrayType)) + return (type_get_base(get(type, p_type))) + + obj_assert_class(type, Type) + return (type) +} + +# Return the default fmt for a given type instance +function type_get_default_fmt(type, _base) { + _base = type_get_base(type) + return (get(_base, p_default_fmt)) +} + +# Return a string representation of the given type +function type_to_string(type, _base_type) { + if (obj_is_instanceof(type, ArrayType)) { + _base_type = type_get_base(type) + return (type_to_string(_base_type) "[" get(type, p_count) "]") + } + return get(type, p_name) +} + +# Return true if type `rhs` is can be coerced to type `lhs` without data +# loss +function type_can_represent(lhs, rhs) { + # Must be of the same class (Type or ArrayType) + if (obj_get_class(lhs) != obj_get_class(rhs)) + return (0) + + if (obj_is_instanceof(lhs, ArrayType)) { + # The base types must have a representable relationship + if (!type_can_represent(type_get_base(lhs), type_get_base(rhs))) + return (0) + + # The lhs type must be able to represent -at least- as + # many elements as the RHS type + if (get(lhs, p_count) < get(rhs, p_count)) + return (0) + + return (1) + } + + # A signed type could represent the full range of a smaller unsigned + # type, but we don't bother; the two should agree when used in a SROM + # layout. Instead simply assert that both are signed or unsigned. + if (get(lhs, p_signed) != get(rhs, p_signed)) + return (0) + + # The `rhs` type must be equal or smaller in width to the `lhs` type + if (get(lhs, p_width) < get(rhs, p_width)) + return (0) + + return (1) +} + +# Create a new ArrayType instance +function array_type_new(type, count, _obj) { + _obj = obj_new(ArrayType) + set(_obj, p_type, type) + set(_obj, p_count, count) + + return (_obj) +} + +# +# Parse a type string to either the Type, ArrayType, or null if +# the type is not recognized. +# +function parse_type_string(str, _base, _count) { + if (match(str, ARRAY_REGEX"$") > 0) { + # Extract count and base type + _count = substr(str, RSTART+1, RLENGTH-2) + sub(ARRAY_REGEX"$", "", str) + + # Look for base type + if ((_base = type_named(str)) == null) + return (null) + + return (array_type_new(_base, int(_count))) + } else { + return (type_named(str)) + } +} + +# +# Parse a variable name in the form of 'name' or 'name[len]', returning +# either the provided base_type if no array specifiers are found, or +# the fully parsed ArrayType. +# +function parse_array_type_specifier(str, base_type, _count) { + if (match(str, ARRAY_REGEX"$") > 0) { + # Extract count + _count = substr(str, RSTART+1, RLENGTH-2) + return (array_type_new(base_type, int(_count))) + } else { + return (base_type) + } +} + +# Return the type constant for `name`, if any +function type_named(name, _n, _type) { + if (name == null) + errorx("called type_named() with null name") + + if (map_contains(BaseTypes, name)) + return (map_get(BaseTypes, name)) + + return (null) +} + +# Create a new Fmt instance +function fmt_new(name, symbol, _obj) { + _obj = obj_new(Fmt) + set(_obj, p_name, name) + set(_obj, p_symbol, symbol) + + return (_obj) +} + + +# Return the Fmt constant for `name`, if any +function fmt_named(name, _n, _fmt) { + if (map_contains(ValueFormats, name)) + return (map_get(ValueFormats, name)) + + return (null) +} + +# Create a new VFlag instance +function vflag_new(name, constant, _obj) { + _obj = obj_new(VFlag) + set(_obj, p_name, name) + set(_obj, p_const, constant) + + return (_obj) +} + +# Create a new StringConstant AST node +function stringconstant_new(value, continued, _obj) { + _obj = obj_new(StringConstant) + set(_obj, p_value, value) + set(_obj, p_continued, continued) + set(_obj, p_line, NR) + + return (_obj) +} + +# Create an empty StringConstant AST node to which additional lines +# may be appended +function stringconstant_empty(_obj) { + return (stringconstant_new("", 1)) +} + +# Parse an input string and return a new string constant +# instance +function stringconstant_parse_line(line, _obj) { + _obj = stringconstant_empty() + stringconstant_append_line(_obj, line) + return (_obj) +} + +# Parse and apend an additional line to this string constant +function stringconstant_append_line(str, line, _cont, _strbuf, _regex, _eol) { + obj_assert_class(str, StringConstant) + + # Must be marked for continuation + if (!get(str, p_continued)) { + errorx("can't append to non-continuation string '" \ + get(str, p_value) "'") + } + + _strbuf = get(str, p_value) + + # If start of string, look for (and remove) initial double quote + if (_strbuf == null) { + _regex = "^[ \t]*\"" + if (!sub(_regex, "", line)) { + error("expected quoted string") + } + } + + # Look for a terminating double quote + _regex = "([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\"" + + _eol = match(line, _regex) + if (_eol > 0) { + # Drop everything following the terminating quote + line = substr(line, 1, RLENGTH-1) + _cont = 0 + } else { + # No terminating quote found, continues on next line + _cont = 1 + } + + # Trim leading and trailing whitespace + sub(/(^[ \t]+|[ \t]+$)/, "", line) + + # Append to existing buffer + if ((_strbuf = get(str, p_value)) == NULL) + set(str, p_value, line) + else + set(str, p_value, _strbuf " " line) + + # Update line continuation setting + set(str, p_continued, _cont) +} + +# Create a new RevRange instance +function revrange_new(start, end, _obj) { + _obj = obj_new(RevRange) + set(_obj, p_start, start) + set(_obj, p_end, end) + set(_obj, p_line, NR) + + return (_obj) +} + +# Return true if the two revision ranges are equal +function revrange_equal(lhs, rhs) { + if (get(lhs, p_start) != get(rhs, p_start)) + return (0) + + if (get(lhs, p_end) != get(rhs, p_end)) + return (0) + + return (1) +} + +# Return true if the requested rev is covered by revrange, false otherwise +function revrange_contains(range, rev) { + obj_assert_class(range, RevRange) + + if (rev < get(range, p_start)) + return (0) + else if (rev > get(range, p_end)) { + return (0) + } else { + return (1) + } +} + +# +# Return a string representation of the given revision range +# +function revrange_to_string(revs, _start, _end) { + obj_assert_class(revs, RevRange) + + _start = get(revs, p_start) + _end = get(revs, p_end) + + if (_start == 0) + return ("<= " _end) + else if (_end == REV_MAX) + return (">= " _start) + else + return (_start "-" _end) +} + +# Create a new VarGroup instance +function var_group_new(name, _obj) { + _obj = obj_new(VarGroup) + set(_obj, p_name, name) + set(_obj, p_vars, array_new()) + set(_obj, p_line, NR) + + return (_obj) +} + +# Create a new NVRAM instance +function nvram_new(_obj, _vars, _v) { + _obj = obj_new(NVRAM) + _vars = array_new() + set(_obj, p_vars, _vars) + set(_obj, p_var_groups, array_new()) + set(_obj, p_srom_layouts, array_new()) + set(_obj, p_srom_table, map_new()) + + # + # Register our implicit variable definitions + # + + # SROM signature offset + _v = var_new(VAccessInternal, "", UInt16) + array_append(_vars, _v) + _g_var_names[get(_v, p_name)] = _v + + # SROM CRC8 offset + _v = var_new(VAccessInternal, "", UInt8) + array_append(_vars, _v) + _g_var_names[get(_v, p_name)] = _v + + return (_obj) +} + +# Register a new SROM layout instance +# An error will be thrown if the layout overlaps any revisions covered +# by an existing instance. +function nvram_add_srom_layout(nvram, layout, _table, _revs, _start, _end, _i) { + obj_assert_class(nvram, NVRAM) + obj_assert_class(layout, SromLayout) + + # revision:layout hash table + _table = get(nvram, p_srom_table) + + # register the layout's revisions + _revs = get(layout, p_revisions) + _start = get(_revs, p_start) + _end = get(_revs, p_end) + + for (_i = _start; _i <= _end; _i++) { + if (map_contains(_table, _i)) { + error("SROM layout redeclares layout for revision '" \ + _i "' (originally declared on line " \ + get(map_get(_table, _i), p_line) ")") + } + + map_set(_table, _i, layout) + } + + # append to srom_layouts + array_append(get(nvram, p_srom_layouts), layout) +} + +# Return the first SROM layout registered for a given SROM revision, +# or null if no matching layout is found +function nvram_get_srom_layout(nvram, revision, _layouts, _nlayouts, _layout, + _i) +{ + obj_assert_class(nvram, NVRAM) + + _layouts = get(nvram, p_srom_layouts) + _nlayouts = array_size(_layouts) + for (_i = 0; _i < _nlayouts; _i++) { + _layout = array_get(_layouts, _i) + + if (srom_layout_has_rev(_layout, revision)) + return (_layout) + } + + # Not found + return (null) +} + +# Create a new Var instance +function var_new(access, name, type, _obj) { + obj_assert_class(access, VAccess) + + # Validate the variable identifier + # + # The access modifier dictates the permitted identifier format. + # VAccessInternal: + # VAccess(Public|Private): ident + if (access != VAccessInternal && name ~ SVAR_IDENT_REGEX) { + error("invalid identifier '"name"'; did you mean to " \ + "mark this variable as internal?") + } else if (access == VAccessInternal) { + if (name !~ SVAR_IDENT_REGEX) + error("invalid identifier '"name"' for internal " \ + "variable; did you mean '<" name ">'?") + } else if (name !~ VAR_IDENT_REGEX) { + error("invalid identifier '"name"'") + } + + _obj = obj_new(Var) + set(_obj, p_access, access) + set(_obj, p_name, name) + set(_obj, p_type, type) + set(_obj, p_line, NR) + + return (_obj) +} + +# Return true if var is internal-only, and should not be included +# in any output (e.g. has an access specifier of VAccessInternal). +function var_is_internal(var) { + return (get(var, p_access) == VAccessInternal) +} + +# Return true if `var` has an array type +function var_has_array_type(var, _vtype) { + obj_assert_class(var, Var) + _vtype = get(var, p_type) + return (obj_is_instanceof(_vtype, ArrayType)) +} + +# Return the number of array elements defined by this variable's type, +# or 1 if the variable does not have an array type. +function var_get_array_len(var) { + obj_assert_class(var, Var) + return (type_get_nelem(get(var, p_type))) +} + +# Return the fmt for var. If not explicitly set on var, will return then +# return of calling type_get_default_fmt() with the variable's type +function var_get_fmt(var, _fmt) { + obj_assert_class(var, Var) + + # If defined on var, return it + if ((_fmt = get(var, p_fmt)) != null) + return (_fmt) + + # Fall back on the type's default + return (type_get_default_fmt(get(var, p_type))) +} + +# Return a new MacroDefine instance for the given variable, macro type, +# and value +function var_get_macro(var, macro_type, value, _macro) { + obj_assert_class(var, Var) + obj_assert_class(macro_type, MacroType) + + return (macro_new(var_get_macro_name(var, macro_type), value)) +} + +# Return the preprocessor constant name to be used with `var` for the given +# macro_type +function var_get_macro_name(var, macro_type, _var_name, _suffix) { + obj_assert_class(var, Var) + obj_assert_class(macro_type, MacroType) + + _var_name = get(var, p_name) + _suffix = get(macro_type, p_const_suffix) + + return("BHND_NVAR_" toupper(_var_name) _suffix) +} + +# Create a new SromLayout instance +function srom_layout_new(rev_desc, _obj) +{ + _obj = obj_new(SromLayout) + set(_obj, p_revisions, rev_desc) + set(_obj, p_entries, array_new()) + set(_obj, p_revmap, map_new()) + set(_obj, p_output_var_counts, map_new()) + set(_obj, p_line, NR) + + return (_obj) +} + +# Register a new entry with the srom layout +function srom_layout_add_entry(layout, entry, _revmap, _name, _rev_start, + _rev_end, _var, _prev_entry, _count, _i) +{ + obj_assert_class(layout, SromLayout) + obj_assert_class(entry, SromEntry) + + _layout_revmap = get(layout, p_revmap) + _layout_var_count = get(layout, p_output_var_counts) + + _var = get(entry, p_var) + _name = get(_var, p_name) + + # Add to revision array + array_append(get(layout, p_entries), entry) + + # Add to the revision map tables + _rev_start = get(get(entry, p_revisions), p_start) + _rev_end = get(get(entry, p_revisions), p_end) + + for (_i = _rev_start; _i <= _rev_end; _i++) { + # Check for existing entry + _prev_entry = srom_layout_find_entry(layout, _name, _i) + if (_prev_entry != null) { + error("redefinition of variable '" _name "' for SROM " \ + "revision " _i " (previously defined on line " \ + get(_prev_entry, p_line) ")") + } + + # Add to the (varname,revision) map + map_set(_layout_revmap, (_name SUBSEP _i), entry) + + # If an output variable, set or increment the output variable + # count + if (!srom_entry_should_output(entry, _i)) + continue + + if (!map_contains(_layout_var_count, _i)) { + map_set(_layout_var_count, _i, 1) + } else { + _count = map_get(_layout_var_count, _i) + map_set(_layout_var_count, _i, _count + 1) + } + } +} + + +# Return the variable name to be used when emitting C declarations +# for this SROM layout +# +# The name is gauranteed to be unique across SROM layouts with non-overlapping +# revision ranges +function srom_layout_get_variable_name(layout, _revs) { + obj_assert_class(layout, SromLayout) + + _revs = get(layout, p_revisions) + + return ("bhnd_sprom_layout_r" get(_revs, p_start) \ + "_r" get(_revs, p_end)) +} + +# Return true if the given SROM revision is defined by the layout, false +# otherwise +function srom_layout_has_rev(layout, rev) { + obj_assert_class(layout, SromLayout) + return (revrange_contains(get(layout, p_revisions), rev)) +} + + +# Return the total number of output variables (variables to be included +# in the SROM layout bindings) for the given SROM revision +function srom_layout_num_output_vars(layout, rev, _counts) +{ + obj_assert_class(layout, SromLayout) + + _counts = get(layout, p_output_var_counts) + if (!map_contains(_counts, rev)) + return (0) + + return (map_get(_counts, rev)) +} + +# Return the SromEntry defined for the given variable name and SROM revision, +# or null if none +function srom_layout_find_entry(layout, vname, revision, _key, _srom_revmap) { + obj_assert_class(layout, SromLayout) + + _srom_revmap = get(layout, p_revmap) + + # SromEntry are mapped by name,revision composite keys + _key = vname SUBSEP revision + if (!map_contains(_srom_revmap, _key)) + return (null) + + return (map_get(_srom_revmap, _key)) + +} + +# Create a new SromLayoutFilter instance, checking that `revs` +# falls within the parent's revision range +function srom_layout_filter_new(parent, revs, _obj, _start, _end, _parent_revs) { + obj_assert_class(parent, SromLayout) + obj_assert_class(revs, RevRange) + + # Fetch our parent's revision range, confirm that we're + # a strict subset + _start = get(revs, p_start) + _end = get(revs, p_end) + _parent_revs = get(parent, p_revisions) + + if (!revrange_contains(_parent_revs, _start)) + error("'" _start "' is outside of parent range") + + if (!revrange_contains(_parent_revs, _end)) + error("'" _end "' is outside of parent range") + + if (revrange_equal(revs, _parent_revs)) { + error("srom range '" revrange_to_string(revs) "' is " \ + "identical to parent range of '" \ + revrange_to_string(_parent_revs) "'") + } + + # Construct and return new filter instance + _obj = obj_new(SromLayoutFilter) + set(_obj, p_parent, parent) + set(_obj, p_revisions, revs) + set(_obj, p_line, NR) + + return (_obj) +} + +# +# Create a new SromEntry instance +# +# var: The variable referenced by this entry +# revisions: The SROM revisions to which this entry applies +# base_offset: The SROM entry offset; any relative segment offsets will be +# calculated relative to the base offset +# type: The SROM's value type; this may be a subtype of the variable +# type, and defines the data (width, sign, etc) to be read from +# SROM. +# +function srom_entry_new(var, revisions, base_offset, type, _obj) { + obj_assert_class(var, Var) + if (revisions != null) + obj_assert_class(revisions, RevRange) + + _obj = obj_new(SromEntry) + set(_obj, p_var, var) + set(_obj, p_revisions, revisions) + set(_obj, p_base_offset, base_offset) + set(_obj, p_type, type) + set(_obj, p_offsets, array_new()) + set(_obj, p_line, NR) + + return (_obj) +} + +# Return true if the SromEntry has an array type +function srom_entry_has_array_type(entry) { + obj_assert_class(entry, SromEntry) + + return (obj_is_instanceof(get(entry, p_type), ArrayType)) +} + +# Return the number of array elements defined by this SromEntry's type, +# or 1 if the entry does not have an array type. +function srom_entry_get_array_len(entry, _type) { + obj_assert_class(entry, SromEntry) + + return (type_get_nelem(get(entry, p_type))) +} + +# +# Return true if the given entry should be included in the output bindings +# generated for the given revision, false otherwise. +# +function srom_entry_should_output(entry, rev, _var, _revs) +{ + obj_assert_class(entry, SromEntry) + + _var = get(entry, p_var) + _revs = get(entry, p_revisions) + + # Exclude internal variables + if (var_is_internal(_var)) + return (0) + + # Exclude inapplicable entry revisions + if (!revrange_contains(_revs, rev)) + return (0) + + return (1) +} + +# +# Return the single, non-shifted, non-masked offset/segment for the given +# SromEntry, or throw an error if the entry contains multiple offsets/segments. +# +# This is used to fetch special-cased variable definitions that are required +# to present a single simple offset. +# +function srom_entry_get_single_segment(entry, _offsets, _segments, _seg, + _base_type, _default_mask) +{ + obj_assert_class(entry, SromEntry) + + # Fetch the single offset's segment list + _offsets = get(entry, p_offsets) + if (array_size(_offsets) != 1) + errorc(get(entry, p_line), "unsupported offset count") + + _segments = get(array_first(_offsets), p_segments) + if (array_size(_segments) != 1) + errorc(get(entry, p_line), "unsupported segment count") + + # Fetch the single segment + _seg = array_first(_segments) + _base_type = srom_segment_get_base_type(_seg) + _default_mask = get(_base_type, p_mask) + + # Must not be shifted/masked + if (get(_seg, p_shift) != 0) + errorc(obj_get_prop_nr(_seg, p_mask), "shift unsupported") + + if (get(_seg, p_mask) != _default_mask) + errorc(obj_get_prop_nr(_seg, p_mask), "mask unsupported") + + return (_seg) +} + +# Create a new SromOffset instance +function srom_offset_new(_obj) { + _obj = obj_new(SromOffset) + set(_obj, p_segments, array_new()) + set(_obj, p_line, NR) + + return (_obj) +} + +# Return the number of SromSegment instances defined by this offset. +function srom_offset_segment_count(offset) { + obj_assert_class(offset, SromOffset) + return (array_size(get(offset, p_segments))) +} + +# Return the idx'th segment. Will throw an error if idx falls outside +# the number of available segments. +function srom_offset_get_segment(offset, idx, _segments, _seg) { + obj_assert_class(offset, SromOffset) + + return (array_get(get(offset, p_segments), idx)) +} + +# Create a new SromSegment instance +function srom_segment_new(offset, type, mask, shift, value, _obj) { + _obj = obj_new(SromSegment) + set(_obj, p_offset, offset) + set(_obj, p_type, type) + set(_obj, p_mask, mask) + set(_obj, p_shift, shift) + set(_obj, p_value, value) + set(_obj, p_line, NR) + + return (_obj) +} + +# Return true if the segment has an array type +function srom_segment_has_array_type(seg, _type) { + _type = srom_segment_get_type(seg) + return (obj_is_instanceof(_type, ArrayType)) +} + +# Return the array count of the segment, or 1 if the segment does not have +# an array type +function srom_segment_get_array_len(seg, _type) { + if (!srom_segment_has_array_type(seg)) + return (1) + + _type = srom_segment_get_type(seg) + return (get(_type, p_count)) +} + +# Return the type of the segment +function srom_segment_get_type(seg) { + obj_assert_class(seg, SromSegment) + return (get(seg, p_type)) + +} + +# Return the base type of the segment +function srom_segment_get_base_type(seg) { + return (type_get_base(srom_segment_get_type(seg))) +} + +# Return true if the two segments have identical types and attributes (i.e. +# differing only by offset) +function srom_segment_attributes_equal(lhs, rhs) { + obj_assert_class(lhs, SromSegment) + obj_assert_class(rhs, SromSegment) + + # type + if (!type_equal(get(lhs, p_type), get(rhs, p_type))) + return (0) + + # mask + if (get(lhs, p_mask) != get(rhs, p_mask)) + return (0) + + # shift + if (get(lhs, p_shift) != get(rhs, p_shift)) + return (0) + + # value + if (get(lhs, p_value) != get(rhs, p_value)) + return (0) + + return (1) +} + +# Return a human-readable representation of a Segment instance +function segment_to_string(seg, _str, _t, _m, _s, _attrs, _attr_str) { + _attrs = array_new() + + # include type (if specified) + if ((_t = get(seg, p_type)) != null) + _str = (type_to_string(_t) " ") + + # include offset + _str = (_str sprintf("0x%X", get(seg, p_offset))) + + # append list of attributes + if ((_m = get(seg, p_mask)) != null) + array_append(_attrs, ("&" _m)) + + if ((_s = get(seg, p_shift)) != null) { + if (_s > 0) + _s = ">>" _s + else + _s = "<<" _s + array_append(_attrs, _s) + } + + _attr_str = array_join(_attrs, ", ") + obj_delete(_attrs) + + if (_attr_str == "") + return (_str) + else + return (_str " (" _attr_str ")") +} + +# return the flag definition for variable `v` +function gen_var_flags(v, _type, _flags, _flag, _str) +{ + _num_flags = 0; + _type = get(v, p_type) + _flags = array_new() + + # VF_PRIVATE + if (get(v, p_access) == VAccessPrivate) + array_append(_flags, VFlagPrivate) + + # VF_IGNALL1 + if (get(v, p_ignall1)) + array_append(_flags, VFlagIgnoreAll1) + + # If empty, return empty flag value + if (array_size(_flags) == 0) { + obj_delete(_flags) + return ("0") + } + + # Join all flag constants with | + _str = array_join(_flags, "|", class_get_prop_id(VFlag, p_const)) + + # Clean up + obj_delete(_flags) + + return (_str) +} + +# +# Return the absolute value +# +function abs(i) { + return (i < 0 ? -i : i) +} + +# +# Return the minimum of two values +# +function min(lhs, rhs) { + return (lhs < rhs ? lhs : rhs) +} + +# +# Return the maximum of two values +# +function max(lhs, rhs) { + return (lhs > rhs ? lhs : rhs) +} + +# +# Parse a hex string +# +function parse_hex_string(str, _hex_pstate, _out, _p, _count) { + if (!AWK_REQ_HEX_PARSING) + return (str + 0) + + # Populate hex parsing lookup table on-demand + if (!("F" in _g_hex_table)) { + for (_p = 0; _p < 16; _p++) { + _g_hex_table[sprintf("%X", _p)] = _p + _g_hex_table[sprintf("%x", _p)] = _p + } + } + + # Split input into an array + _count = split(toupper(str), _hex_pstate, "") + _p = 1 + + # Skip leading '0x' + if (_count >= 2 && _hex_pstate[1] == "0") { + if (_hex_pstate[2] == "x" || _hex_pstate[2] == "X") + _p += 2 + } + + # Parse the hex_digits + _out = 0 + for (; _p <= _count; _p++) + _out = (_out * 16) + _g_hex_table[_hex_pstate[_p]] + + return (_out) +} + +# +# Return the integer representation of an unsigned decimal, hexadecimal, or +# octal string +# +function parse_uint_string(str) { + if (str ~ UINT_REGEX) + return (int(str)) + else if (str ~ HEX_REGEX) + return (parse_hex_string(str)) + else + error("invalid integer value: '" str "'") +} + +# +# Parse an offset string, stripping any leading '+' or trailing ':' or ',' +# characters +# +# +0x0: +# 0x0, +# ... +# +function parse_uint_offset(str) { + # Drop any leading '+' + sub(/^\+/, "", str) + + # Drop any trailing ':', ',', or '|' + sub("[,|:]$", "", str) + + # Parse the cleaned up string + return (parse_uint_string(str)) } # # Print msg to output file, without indentation # -function emit_ni (msg) -{ +function emit_ni(msg) { printf("%s", msg) >> OUTPUT_FILE } # # Print msg to output file, indented for the current `output_depth` # -function emit (msg) -{ +function emit(msg, _ind) { for (_ind = 0; _ind < output_depth; _ind++) emit_ni("\t") @@ -551,24 +3457,36 @@ function emit (msg) # # Print a warning to stderr # -function warn (msg) -{ +function warn(msg) { print "warning:", msg, "at", FILENAME, "line", NR > "/dev/stderr" } +# +# Print an warning message without including the source line information +# +function warnx(msg) { + print "warning:", msg > "/dev/stderr" +} + +# +# Print a compiler error to stderr with a caller supplied +# line number +# +function errorc(line, msg) { + errorx(msg " at " FILENAME " line " line) +} + # # Print a compiler error to stderr # -function error (msg) -{ +function error(msg) { errorx(msg " at " FILENAME " line " NR ":\n\t" $0) } # # Print an error message without including the source line information # -function errorx (msg) -{ +function errorx(msg) { print "error:", msg > "/dev/stderr" _EARLY_EXIT=1 exit 1 @@ -577,33 +3495,18 @@ function errorx (msg) # # Print a debug output message # -function debug (msg) -{ +function debug(msg, _i) { if (!DEBUG) return - for (_di = 0; _di < depth; _di++) + for (_i = 1; _i < _g_parse_stack_depth; _i++) printf("\t") > "/dev/stderr" print msg > "/dev/stderr" } -# -# Return an array key composed of the given (parent, selector, child) -# tuple. -# The child argument is optional and may be omitted. -# -function subkey (parent, selector, child) -{ - if (child != null) - return (parent SUBSEP selector SUBSEP child) - else - return (parent SUBSEP selector) -} - # # Advance to the next non-comment input record # -function next_line () -{ +function next_line(_result) { do { _result = getline } while (_result > 0 && $0 ~ /^[ \t]*#.*/) # skip comment lines @@ -613,8 +3516,7 @@ function next_line () # # Advance to the next input record and verify that it matches @p regex # -function getline_matching (regex) -{ +function getline_matching(regex, _result) { _result = next_line() if (_result <= 0) return (_result) @@ -631,532 +3533,668 @@ function getline_matching (regex) # If all fields are consumed and the optional do_getline argument is true, # read the next line. # -function shiftf (n, do_getline) -{ - if (n > NF) error("shift past end of line") - for (_si = 1; _si <= NF-n; _si++) { - $(_si) = $(_si+n) +function shiftf(n, do_getline, _i) { + if (n > NF) + error("shift past end of line") + + if (n == NF) { + # If shifting the entire line, just reset the line value + $0 = "" + } else { + for (_i = 1; _i <= NF-n; _i++) { + $(_i) = $(_i+n) + } + NF = NF - n } - NF = NF - n if (NF == 0 && do_getline) next_line() } -# -# Parse a revision descriptor from the current line. -# -function parse_revdesc (result) -{ - _rstart = 0 - _rend = 0 - - if ($2 ~ "[0-9]*-[0-9*]") { - split($2, _revrange, "[ \t]*-[ \t]*") - _rstart = _revrange[1] - _rend = _revrange[2] - } else if ($2 ~ "(>|>=|<|<=)" && $3 ~ "[1-9][0-9]*") { - if ($2 == ">") { - _rstart = int($3)+1 - _rend = REV_MAX - } else if ($2 == ">=") { - _rstart = int($3) - _rend = REV_MAX - } else if ($2 == "<" && int($3) > 0) { - _rstart = 0 - _rend = int($3)-1 - } else if ($2 == "<=") { - _rstart = 0 - _rend = int($3)-1 - } else { - error("invalid revision descriptor") - } - } else if ($2 ~ "[1-9][0-9]*") { - _rstart = int($2) - _rend = int($2) - } else { - error("invalid revision descriptor") - } - - result[REV_START] = _rstart - result[REV_END] = _rend -} - -# # Push a new parser state. -# -# The name may be null, in which case the STATE_IDENT variable will not be -# defined in this scope -# -function push_state (type, name, block) { - depth++ - push(STATE_LINENO, NR) - if (name != null) - push(STATE_IDENT, name) - push(STATE_TYPE, type) - push(STATE_ISBLOCK, block) +function parser_state_push(ctx, is_block, _state) { + _state = obj_new(ParseState) + set(_state, p_ctx, ctx) + set(_state, p_is_block, is_block) + set(_state, p_line, NR) + + _g_parse_stack_depth++ + _g_parse_stack[_g_parse_stack_depth] = _state } -# -# Pop the top of the parser state stack. -# -function pop_state () { - # drop all symbols defined at this depth - for (s in symbols) { - if (s ~ "^"depth"[^0-9]") - delete symbols[s] +# Fetch the current parser state +function parser_state_get() { + if (_g_parse_stack_depth == 0) + errorx("parser_state_get() called with empty parse stack") + + return (_g_parse_stack[_g_parse_stack_depth]) +} + +# Pop the current parser state +function parser_state_pop(_block_state, _closes_block) { + if (_g_parse_stack_depth == 0) + errorx("parser_state_pop() called with empty parse stack") + + _closes_block = get(parser_state_get(), p_is_block) + + delete _g_parse_stack[_g_parse_stack_depth] + _g_parse_stack_depth-- + + if (_closes_block) + debug("}") +} + +# Fetch the current context object associated with this parser state +# The object will be asserted as being an instance of the given class. +function parser_state_get_context(class, _ctx_obj) { + _ctx_obj = get(parser_state_get(), p_ctx) + obj_assert_class(_ctx_obj, class) + + return (_ctx_obj) +} + +# Walk the parser state stack until a context object of the given class +# is found. If the top of the stack is reached without finding a context object +# of the requested type, an error will be thrown. +function parser_state_find_context(class, _state, _ctx, _i) { + if (class == null) + errorx("parser_state_find_context() called with null class") + + # Find the first context instance inheriting from `class` + for (_i = 0; _i < _g_parse_stack_depth; _i++) { + _state = _g_parse_stack[_g_parse_stack_depth - _i] + _ctx = get(_state, p_ctx) + + # Check for match + if (obj_is_instanceof(_ctx, class)) + return (_ctx) } - depth-- + + # Not found + errorx("no context instance of type '" class_get_name(class) "' " \ + "found in parse stack") } # # Find opening brace and push a new parser state for a brace-delimited block. # -# The name may be null, in which case the STATE_IDENT variable will not be -# defined in this scope -# -function open_block (type, name) -{ +function parser_state_open_block(ctx) { if ($0 ~ "{" || getline_matching("^[ \t]*{") > 0) { - push_state(type, name, 1) - sub("^[^{]+{", "", $0) + parser_state_push(ctx, 1) + sub("^[^{]*{", "", $0) return } - error("found '"$1 "' instead of expected '{' for '" name "'") + error("found '"$1 "' instead of expected '{'") } # # Find closing brace and pop parser states until the first # brace-delimited block is discarded. # -function close_block () -{ +function parser_state_close_block(_next_state, _found_block) { if ($0 !~ "}") error("internal error - no closing brace") # pop states until we exit the first enclosing block do { - _closed_block = g(STATE_ISBLOCK) - pop_state() - } while (!_closed_block) + _next_state = parser_state_get() + _found_block = get(_next_state, p_is_block) + parser_state_pop() + } while (!_found_block) # strip everything prior to the block closure sub("^[^}]*}", "", $0) } -# Internal symbol table lookup function. Returns the symbol depth if -# name is found at or above scope; if scope is null, it defauls to 0 -function _find_sym (name, scope) -{ - if (scope == null) - scope = 0; +# Evaluates to true if the current parser state is defined with a context of +# the given class +function in_parser_context(class, _ctx) { + if (class == null) + errorx("called in_parser_context() with null class") - for (i = scope; i < depth; i++) { - if ((depth-i,name) in symbols) - return (depth-i) - } - - return (-1) + _ctx = get(parser_state_get(), p_ctx) + return (obj_is_instanceof(_ctx, class)) } # -# Look up a variable in the symbol table with `name` and return its value. +# Parse and return a revision range from the current line. # -# If `scope` is not null, the variable search will start at the provided -# scope level -- 0 is the current scope, 1 is the parent's scope, etc. +# 4 +# 4-10 # revisions 4-10, inclusive +# > 4 +# < 4 +# >= 4 +# <= 4 # -function g (name, scope) -{ - _g_depth = _find_sym(name, scope) - if (_g_depth < 0) - error("'" name "' is undefined") +function parse_revrange(_start, _end, _robj) { + _start = 0 + _end = 0 - return (symbols[_g_depth,name]) -} - -function is_defined (name, scope) -{ - return (_find_sym(name, scope) >= 0) -} - -# Define a new variable in the symbol table's current scope, -# with the given value -function push (name, value) -{ - symbols[depth,name] = value -} - -# Set an existing variable's value in the symbol table; if not yet defined, -# will trigger an error -function set (name, value, scope) -{ - for (i = 0; i < depth; i++) { - if ((depth-i,name) in symbols) { - symbols[depth-i,name] = value - return + if ($2 ~ "[0-9]*-[0-9*]") { + split($2, _g_rev_range, "[ \t]*-[ \t]*") + _start = int(_g_rev_range[1]) + _end = int(_g_rev_range[2]) + } else if ($2 ~ "(>|>=|<|<=)" && $3 ~ "[1-9][0-9]*") { + if ($2 == ">") { + _start = int($3)+1 + _end = REV_MAX + } else if ($2 == ">=") { + _start = int($3) + _end = REV_MAX + } else if ($2 == "<" && int($3) > 0) { + _start = 0 + _end = int($3)-1 + } else if ($2 == "<=") { + _start = 0 + _end = int($3)-1 + } else { + error("invalid revision descriptor") } - } - # No existing value, cannot define - error("'" name "' is undefined") -} - -# Evaluates to true if immediately within a block scope of the given type -function in_state (type) -{ - if (!is_defined(STATE_TYPE)) - return (type == ST_NONE) - - return (type == g(STATE_TYPE)) -} - -# Evaluates to true if within an immediate or non-immediate block scope of the -# given type -function in_nested_state (type) -{ - for (i = 0; i < depth; i++) { - if ((depth-i,STATE_TYPE) in symbols) { - if (symbols[depth-i,STATE_TYPE] == type) - return (1) - } - } - return (0) -} - -# Evaluates to true if definitions of the given type are permitted within -# the current scope -function allow_def (type) -{ - if (type == ST_VAR_BLOCK) { - return (in_state(ST_NONE) || in_state(ST_STRUCT_BLOCK)) - } else if (type == ST_STRUCT_BLOCK) { - return (in_state(ST_NONE)) - } else if (type == ST_SROM_DEFN) { - return (in_state(ST_VAR_BLOCK) || in_state(ST_STRUCT_BLOCK)) - } - - error("unknown type '" type "'") -} - -# struct definition -$1 == ST_STRUCT_BLOCK && allow_def($1) { - name = $2 - - # Remove array[] specifier - if (sub(/\[\]$/, "", name) == 0) - error("expected '" name "[]', not '" name "'") - - if (name !~ IDENT_REGEX || name ~ TYPES_REGEX) - error("invalid identifier '" name "'") - - # Add top-level struct entry - if ((name,DEF_LINE) in structs) - error("struct identifier '" name "' previously defined on " \ - "line " structs[name,DEF_LINE]) - structs[name,DEF_LINE] = NR - structs[name,NUM_REVS] = 0 - - # Open the block - debug("struct " name " {") - open_block(ST_STRUCT_BLOCK, name) -} - -# struct srom descriptor -$1 == ST_SROM_DEFN && allow_def(ST_SROM_DEFN) && in_state(ST_STRUCT_BLOCK) { - sid = g(STATE_IDENT) - - # parse revision descriptor - rev_desc[REV_START] = 0 - parse_revdesc(rev_desc) - - # assign revision id - rev = structs[sid,NUM_REVS] "" - revk = subkey(sid, REV, rev) - structs[sid,NUM_REVS]++ - - # init basic revision state - structs[revk,REV_START] = rev_desc[REV_START] - structs[revk,REV_END] = rev_desc[REV_END] - - if (match($0, "\\[[^]]*\\]") <= 0) - error("expected base address array") - - addrs_str = substr($0, RSTART+1, RLENGTH-2) - num_offs = split(addrs_str, addrs, ",[ \t]*") - structs[revk, REV_NUM_OFFS] = num_offs - for (i = 1; i <= num_offs; i++) { - offk = subkey(revk, OFF, (i-1) "") - - if (addrs[i] !~ HEX_REGEX) - error("invalid base address '" addrs[i] "'") - - structs[offk,SEG_ADDR] = addrs[i] - } - - debug("struct_srom " structs[revk,REV_START] "... [" addrs_str "]") - next -} - -# close any previous srom revision descriptor -$1 == ST_SROM_DEFN && in_state(ST_SROM_DEFN) { - pop_state() -} - -# open a new srom revision descriptor -$1 == ST_SROM_DEFN && allow_def(ST_SROM_DEFN) { - # parse revision descriptor - parse_revdesc(rev_desc) - - # assign revision id - vid = g(STATE_IDENT) - rev = vars[vid,NUM_REVS] "" - revk = subkey(vid, REV, rev) - vars[vid,NUM_REVS]++ - - # vend scoped rev/revk variables for use in the - # revision offset block - push("rev_id", rev) - push("rev_key", revk) - - # init basic revision state - vars[revk,DEF_LINE] = NR - vars[revk,REV_START] = rev_desc[REV_START] - vars[revk,REV_END] = rev_desc[REV_END] - vars[revk,REV_NUM_OFFS] = 0 - - debug("srom " rev_desc[REV_START] "-" rev_desc[REV_END] " {") - push_state(ST_SROM_DEFN, null, 0) - - # seek to the first offset definition - do { - shiftf(1) - } while ($1 !~ SROM_OFF_REGEX && NF > 0) -} - -# -# Extract and return the array length from the given type string. -# Returns -1 if the type is not an array. -# -function type_array_len (type) -{ - # extract byte count[] and width - if (match(type, ARRAY_REGEX"$") > 0) { - return (substr(type, RSTART+1, RLENGTH-2)) + } else if ($2 ~ "[1-9][0-9]*") { + _start = int($2) + _end = int($2) } else { - return (-1) + error("invalid revision descriptor") } + + return (revrange_new(_start, _end)) } +# +# Parse a variable group block starting at the current line # -# Parse an offset declaration from the current line. +# group "Group Name" { +# u8 var_name[10] { +# ... +# } +# ... +# } # -function parse_offset_segment (revk, offk) +function parse_variable_group(_ctx, _groups, _group, _group_name) { + _ctx = parser_state_get_context(NVRAM) + + # Seek to the start of the name string + shiftf(1) + + # Parse the first line + _group_name = stringconstant_parse_line($0) + + # Incrementally parse line continuations + while (get(_group_name, p_continued)) { + getline + stringconstant_append_line(_group_name, $0) + } + + debug("group \"" get(_group_name, p_value) "\" {") + + # Register the new variable group + _groups = get(_ctx, p_var_groups) + _group = var_group_new(_group_name) + array_append(_groups, _group) + + # Push our variable group block + parser_state_open_block(_group) +} + + +# +# Parse a variable definition block starting at the current line +# +# u8 var_name[10] { +# all1 ignore +# desc ... +# } +# +function parse_variable_defn(_ctx, _vaccess, _type, _name, _fmt, _var, + _var_list) { - vid = g(STATE_IDENT) - - # use explicit type if specified, otherwise use the variable's - # common type - if ($1 !~ HEX_REGEX) { - type = $1 - if (type !~ TYPES_REGEX) - error("unknown field type '" type "'") + _ctx = parser_state_get_context(SymbolContext) + # Check for access modifier + if ($1 == "private") { + _vaccess = VAccessPrivate + shiftf(1) + } else if ($1 == "internal") { + _vaccess = VAccessInternal shiftf(1) } else { - type = vars[vid,VAR_TYPE] + _vaccess = VAccessPublic } - # read offset value - offset = $1 - if (offset !~ HEX_REGEX) - error("invalid offset value '" offset "'") + # Find the base type + if ((_type = type_named($1)) == null) + error("unknown type '" $1 "'") - # extract byte count[], base type, and width - if (match(type, ARRAY_REGEX"$") > 0) { - count = int(substr(type, RSTART+1, RLENGTH-2)) - type = substr(type, 1, RSTART-1) + # Parse (and trim) any array specifier from the variable name + _name = $2 + _type = parse_array_type_specifier(_name, _type) + sub(ARRAY_REGEX"$", "", _name) + + # Look for an existing variable definition + if (_name in _g_var_names) { + error("variable identifier '" _name "' previously defined at " \ + "line " get(_g_var_names[_name], p_line)) + } + + # Construct new variable instance + _var = var_new(_vaccess, _name, _type) + debug((_private ? "private " : "") type_to_string(_type) " " _name " {") + + # Register in global name table + _g_var_names[_name] = _var + + # Add to our parent context + _var_list = get(_ctx, p_vars) + array_append(_var_list, _var) + + # Push our variable definition block + parser_state_open_block(_var) +} + + +# +# Return a string containing the human-readable list of valid Fmt names +# +function fmt_get_human_readable_list(_result, _fmts, _fmt, _nfmts, _i) +{ + # Build up a string listing the valid formats + _fmts = map_to_array(ValueFormats) + _result = "" + + _nfmts = array_size(_fmts) + for (_i = 0; _i < _nfmts; _i++) { + _fmt = array_get(_fmts, _i) + if (_i+1 == _nfmts) + _result = _result "or " + + _result = _name_str \ + "'" get(_fmt, p_name) "'" + + if (_i+1 < _nfmts) + _result = _result ", " + } + + obj_delete(_fmts) + return (_result) +} + +# +# Parse a variable parameter from the current line +# +# fmt (decimal|hex|macaddr|...) +# all1 ignore +# desc "quoted string" +# help "quoted string" +# +function parse_variable_param(param_name, _var, _vprops, _prop_id, _pval) { + _var = parser_state_get_context(Var) + + if (param_name == "fmt") { + debug($1 " " $2) + + # Check for an existing definition + if ((_pval = get(_var, p_fmt)) != null) { + error("fmt previously specified on line " \ + obj_get_prop_nr(_var, p_fmt)) + } + + # Validate arguments + if (NF != 2) { + error("'" $1 "' requires a single parameter value of " \ + fmt_get_human_readable_list()) + } + + if ((_pval = fmt_named($2)) == null) { + error("'" $1 "' value '" $2 "' unrecognized. Must be " \ + "one of " fmt_get_human_readable_list()) + } + + # Set fmt reference + set(_var, p_fmt, _pval) + } else if (param_name == "all1") { + debug($1 " " $2) + + # Check for an existing definition + if ((_pval = get(_var, p_ignall1)) != null) { + error("all1 previously specified on line " \ + obj_get_prop_nr(_var, p_ignall1)) + } + + # Check argument + if (NF != 2) + error("'" $1 "'requires a single 'ignore' argument") + else if ($2 != "ignore") + error("unknown "$1" value '"$2"', expected 'ignore'") + + # Set variable property + set(_var, p_ignall1, 1) + } else if (param_name == "desc" || param_name == "help") { + # Fetch an indirect property reference for either the 'desc' + # or 'help' property + _prop_id = obj_get_named_prop_id(_var, param_name) + + # Check for an existing definition + if ((_pval = prop_get(_var, _prop_id)) != null) { + error(get(_var, p_name) " '" $1 "' redefined " \ + "(previously defined on line " \ + obj_get_prop_id_nr(_var, _prop_id) ")") + } + + # Seek to the start of the desc/help string + shiftf(1) + + # Parse the first line + _pval = stringconstant_parse_line($0) + + # Incrementally parse line continuations + while (get(_pval, p_continued)) { + getline + stringconstant_append_line(_pval, $0) + } + + debug(param_name " \"" get(_pval, p_value) "\"") + + # Add to the var object + prop_set(_var, _prop_id, _pval) } else { - count = 1 + error("unknown variable property type: '" param_name "'") } - width = TSIZE[type] +} - # seek to attributes or end of the offset expr + +# +# Parse a top-level SROM layout block starting at the current line +# +# srom 4-7 { +# 0x000: ... +# } +# +function parse_srom_layout(_nvram, _srom_layouts, _revs, _layout) { + _nvram = parser_state_get_context(NVRAM) + _srom_layouts = get(_nvram, p_srom_layouts) + + # Parse revision descriptor and register SROM + # instance + _revs = parse_revrange() + _layout = srom_layout_new(_revs) + nvram_add_srom_layout(_nvram, _layout) + + debug("srom " revrange_to_string(_revs) " {") + + # Push new SROM parser state + parser_state_open_block(_layout) +} + + +# +# Parse a nested srom range filter block starting at the current line +# srom 4-7 { +# # Filter block +# srom 5 { +# 0x000: ... +# } +# } +# +function parse_srom_layout_filter(_parent, _revs, _filter) { + _parent = parser_state_get_context(SromLayout) + + # Parse revision descriptor + _revs = parse_revrange() + + # Construct the filter (which also validates the revision range) + _filter = srom_layout_filter_new(_parent, _revs) + + debug("srom " revrange_to_string(_revs) " {") + + # Push new SROM parser state + parser_state_open_block(_filter) +} + + +# +# Parse a SROM offset segment's attribute list from the current line +# +# +# (&0xF0, >>4, =0x5340) +# () +# +# Attribute designators: +# &0xF Mask value with 0xF +# <<4 Shift left 4 bits +# >>4 Shift right 4 bits +# =0x53 The parsed value must be equal to this constant value +# +# May be followed by a | indicating that this segment should be OR'd with the +# segment that follows, or a terminating , indicating that a new offset's +# list of segments may follow. +# +function parse_srom_segment_attributes(offset, type, _attrs, _num_attr, _attr, + _mask, _shift, _value, _i) +{ + # seek to offset (attributes...) or end of the offset expr (|,) sub("^[^,(|){}]+", "", $0) - # parse attributes - mask=TMASK[type] - shift=0 + # defaults + _mask = type_get_default_mask(type) + _shift = 0 + # parse attributes if ($1 ~ "^\\(") { # extract attribute list - if (match($0, "\\([^|\(\)]*\\)") <= 0) + if (match($0, /\([^|\(\)]*\)/) <= 0) error("expected attribute list") - attr_str = substr($0, RSTART+1, RLENGTH-2) - # drop from input line + _attrs = substr($0, RSTART+1, RLENGTH-2) + + # drop attribute list from the input line $0 = substr($0, RSTART+RLENGTH, length($0) - RSTART+RLENGTH) # parse attributes - num_attr = split(attr_str, attrs, ",[ \t]*") - for (i = 1; i <= num_attr; i++) { - attr = attrs[i] - if (sub("^&[ \t]*", "", attr) > 0) { - mask = attr - } else if (sub("^<<[ \t]*", "", attr) > 0) { - shift = "-"attr - } else if (sub("^>>[ \t]*", "", attr) > 0) { - shift = attr + _num_attr = split(_attrs, _g_attrs, ",[ \t]*") + for (_i = 1; _i <= _num_attr; _i++) { + _attr = _g_attrs[_i] + + if (sub("^&[ \t]*", "", _attr) > 0) { + _mask = parse_uint_string(_attr) + } else if (sub("^<<[ \t]*", "", _attr) > 0) { + _shift = - parse_uint_string(_attr) + } else if (sub("^>>[ \t]*", "", _attr) > 0) { + _shift = parse_uint_string(_attr) + } else if (sub("^=[ \t]*", "", _attr) > 0) { + _value = _attr } else { - error("unknown attribute '" attr "'") + error("unknown attribute '" _attr "'") } } } - # assign segment id - seg = vars[offk,OFF_NUM_SEGS] "" - segk = subkey(offk, OFF_SEG, seg) - vars[offk,OFF_NUM_SEGS]++ - - vars[segk,SEG_ADDR] = offset + (width * _oi) - vars[segk,SEG_COUNT] = count - vars[segk,SEG_TYPE] = type - vars[segk,SEG_MASK] = mask - vars[segk,SEG_SHIFT] = shift - - debug("{"vars[segk,SEG_ADDR]", "type", "mask", "shift"}" \ - _comma) + return (srom_segment_new(offset, type, _mask, _shift, _value)) } -# revision offset definition -$1 ~ SROM_OFF_REGEX && in_state(ST_SROM_DEFN) { - vid = g(STATE_IDENT) +# +# Parse a SROM offset's segment declaration from the current line +# +# +0x0: u8 (&0xF0, >>4) # read 8 bits at +0x0 (relative to srom entry +# # offset, apply 0xF0 mask, shift >> 4 +# 0x10: u8 (&0xF0, >>4) # identical to above, but perform the read at +# # absolute offset 0x10 +# +# +0x0: u8 # no attributes +# 0x10: u8 +# +# +0x0 # simplified forms denoted by lack of ':'; the +# 0x0 # type is inherited from the parent SromEntry +# +# +function parse_srom_segment(base_offset, base_type, _simple, _type, _type_str, + _offset, _attrs, _num_attr, _attr, _mask, _shift, _off_desc) +{ + # Fetch the offset value + _offset = $1 - # fetch rev id/key defined by our parent block - rev = g("rev_id") - revk = g("rev_key") + # Offset string must be one of: + # simplified entry: + # Provides only the offset, with the type inherited + # from the original variable definition + # standard entry: : + # Provides the offset, followed by a type + # + # We differentiate the two by looking for (and simultaneously removing) + # the trailing ':' + if (!sub(/:$/, "", _offset)) + _simple = 1 - # parse all offsets + # The offset may either be absolute (e.g. 0x180) or relative (e.g. + # +0x01). + # + # If we find a relative offset definition, we must trim the leading '+' + # and then add the base offset + if (sub(/^\+/, "", _offset)) { + _offset = base_offset + parse_uint_offset(_offset) + } else { + + _offset = parse_uint_offset(_offset) + } + + # If simplified form, use the base type of the SROM entry. Otherwise, + # we need to parse the type. + if (_simple) { + _type = base_type + } else { + _type_str = $2 + sub(/,$/, "", _type_str) # trim trailing ',', if any + + if ((_type = parse_type_string(_type_str)) == null) + error("unknown type '" _type_str "'") + } + + # Parse the trailing (... attributes ...), if any + return (parse_srom_segment_attributes(_offset, _type)) +} + +# +# Parse a SROM variable entry from the current line +# : ... +# +function parse_srom_variable_entry(_srom, _srom_revs, _rev_start, _rev_end, + _srom_entries, _srom_revmap, _prev_entry, _ctx, _base_offset, _name, + _stype, _var, _entry, _offset, _seg, _i) +{ + # Fetch our parent context + _ctx = parser_state_get_context(SromContext) + _srom_revs = get(_ctx, p_revisions) + _rev_start = get(_srom_revs, p_start) + _rev_end = get(_srom_revs, p_end) + + # Locate our enclosing layout + _srom = parser_state_find_context(SromLayout) + _srom_entries = get(_srom, p_entries) + _srom_revmap = get(_srom, p_revmap) + + # Verify argument count + if (NF < 3) { + error("unrecognized srom entry syntax; must specify at " \ + "least \": \"") + } + + # Parse the base offset + _base_offset = parse_uint_offset($1) + + # Parse the base type + if ((_stype = type_named($2)) == null) + error("unknown type '" $2 "'") + + # Parse (and trim) any array specifier from the variable name + _name = $3 + _stype = parse_array_type_specifier(_name, _stype) + sub(ARRAY_REGEX"$", "", _name) + + # Locate the variable definition + if (!(_name in _g_var_names)) + error("no definition found for variable '" _name "'") + _var = _g_var_names[_name] + + # The SROM entry type must be a subtype of the variable's declared + # type + if (!type_can_represent(get(_var, p_type), _stype)) { + error("'" type_to_string(_stype) "' SROM value cannot be " \ + "coerced to '" type_to_string(get(_var, p_type)) " " _name \ + "' variable") + } + + # Create and register our new offset entry + _entry = srom_entry_new(_var, _srom_revs, _base_offset, _stype) + srom_layout_add_entry(_srom, _entry) + + # Seek to either the block start ('{'), or the attributes to be + # used for a single offset/segment entry at `offset` + shiftf(3) + + # Using the block syntax? */ + if ($1 == "{") { + debug(sprintf("0x%03x: %s %s {", _base_offset, + type_to_string(_stype), _name)) + parser_state_open_block(_entry) + } else { + # Otherwise, we're using the simplified syntax -- create and + # register our implicit SromOffset + _offset = srom_offset_new() + array_append(get(_entry, p_offsets), _offset) + + # Parse and register simplified segment syntax + _seg = parse_srom_segment_attributes(_base_offset, _stype) + array_append(get(_offset, p_segments), _seg) + + debug(sprintf("0x%03x: %s %s { %s }", _base_offset, + type_to_string(_stype), _name, segment_to_string(_seg))) + } +} + +# +# Parse all SromSegment entry segments readable starting at the current line +# +# [,|]? +# : [,|]? +# : ()[,|]? +# +function parse_srom_entry_segments(_entry, _base_off, _base_type, _offs, + _offset, _segs, _seg, _more_seg, _more_vals) +{ + _entry = parser_state_get_context(SromEntry) + _base_off = get(_entry, p_base_offset) + _offs = get(_entry, p_offsets) + + _base_type = get(_entry, p_type) + _base_type = type_get_base(_base_type) + + # Parse all offsets do { - # assign offset id - off = vars[revk,REV_NUM_OFFS] "" - offk = subkey(revk, OFF, off) - vars[revk,REV_NUM_OFFS]++ + # Create a SromOffset + _offset = srom_offset_new() + _segs = get(_offset, p_segments) - # initialize segment count - vars[offk,DEF_LINE] = NR - vars[offk,OFF_NUM_SEGS] = 0 + array_append(_offs, _offset) - debug("[") - # parse all segments + # Parse all segments do { - parse_offset_segment(revk, offk) + _seg = parse_srom_segment(_base_off, _base_type) + array_append(_segs, _seg) + + # Do more segments follow? _more_seg = ($1 == "|") if (_more_seg) shiftf(1, 1) + + if (_more_seg) + debug(segment_to_string(_seg) " |") + else + debug(segment_to_string(_seg)) } while (_more_seg) - debug("],") + + # Do more offsets follow? _more_vals = ($1 == ",") if (_more_vals) shiftf(1, 1) } while (_more_vals) } - -# variable definition -(($1 == "private" && $2 ~ TYPES_REGEX) || $1 ~ TYPES_REGEX) && - allow_def(ST_VAR_BLOCK) \ -{ - # check for 'private' flag - if ($1 == "private") { - private = 1 - shiftf(1) - } else { - private = 0 - } - - type = $1 - name = $2 - array = 0 - debug(type " " name " {") - - # Check for and remove any array[] specifier - base_type = type - if (sub(ARRAY_REGEX"$", "", base_type) > 0) - array = 1 - - # verify type - if (!base_type in DTYPE) - error("unknown type '" $1 "'") - - # Add top-level variable entry - if (name in var_names) - error("variable identifier '" name "' previously defined on " \ - "line " vars[name,DEF_LINE]) - - var_names[name] = 0 - vars[name,VAR_NAME] = name - vars[name,DEF_LINE] = NR - vars[name,VAR_TYPE] = type - vars[name,VAR_BASE_TYPE] = base_type - vars[name,NUM_REVS] = 0 - vars[name,VAR_PRIVATE] = private - vars[name,VAR_ARRAY] = array - vars[name,VAR_FMT] = "hex" # default if not specified - - open_block(ST_VAR_BLOCK, name) - - debug("type=" DTYPE[base_type]) - - if (in_nested_state(ST_STRUCT_BLOCK)) { - # Fetch the enclosing struct's name - sid = g(STATE_IDENT, 1) - - # Mark as a struct-based variable - vars[name,VAR_STRUCT] = sid - } -} - -# variable parameters -$1 ~ IDENT_REGEX && $2 ~ IDENT_REGEX && in_state(ST_VAR_BLOCK) { - vid = g(STATE_IDENT) - if ($1 == PROP_T_SFMT) { - if (!$2 in FMT) - error("invalid fmt '" $2 "'") - - vars[vid,VAR_FMT] = $2 - debug($1 "=" FMT[$2]) - } else if ($1 == PROP_T_ALL1 && $2 == "ignore") { - vars[vid,VAR_IGNALL1] = 1 - } else { - error("unknown parameter " $1) - } - next -} - -# Skip comments and blank lines -/^[ \t]*#/ || /^$/ { - next -} - -# Close blocks -/}/ && !in_state(ST_NONE) { - while (!in_state(ST_NONE) && $0 ~ "}") { - close_block(); - debug("}") - } - next -} - -# Report unbalanced '}' -/}/ && in_state(ST_NONE) { - error("extra '}'") -} - -# Invalid variable type -$1 && allow_def(ST_VAR_BLOCK) { - error("unknown type '" $1 "'") -} - -# Generic parse failure -{ - error("unrecognized statement") -} diff --git a/sys/dev/bwn/bwn_mac.c b/sys/dev/bwn/bwn_mac.c index f6c7575d9f7..42452c356b0 100644 --- a/sys/dev/bwn/bwn_mac.c +++ b/sys/dev/bwn/bwn_mac.c @@ -111,7 +111,7 @@ bwn_attach(device_t dev) // TODO uint8_t macaddr[6]; error = bhnd_nvram_getvar_array(dev, BHND_NVAR_MACADDR, macaddr, - sizeof(macaddr), BHND_NVRAM_TYPE_UINT8); + sizeof(macaddr), BHND_NVRAM_TYPE_UINT8_ARRAY); if (error) device_printf(dev, "error fetching macaddr: %d\n", error); else diff --git a/sys/mips/broadcom/bcm_nvram_cfe.c b/sys/mips/broadcom/bcm_nvram_cfe.c new file mode 100644 index 00000000000..314d3868346 --- /dev/null +++ b/sys/mips/broadcom/bcm_nvram_cfe.c @@ -0,0 +1,527 @@ +/*- + * Copyright (c) 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$"); + +/* + * BHND CFE NVRAM driver. + * + * Provides access to device NVRAM via CFE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include "bhnd_nvram_if.h" + +#include "bcm_nvram_cfevar.h" + +/** + * CFE-backed bhnd_nvram_io implementation. + */ +struct bhnd_nvram_iocfe { + struct bhnd_nvram_io io; /**< common I/O instance state */ + + char *dname; /**< CFE device name (borrowed) */ + int fd; /**< CFE file descriptor */ + size_t offset; /**< base offset */ + size_t size; /**< device size */ + bool req_blk_erase; /**< flash blocks must be erased + before writing */ +}; + +BHND_NVRAM_IOPS_DEFN(iocfe) + +#define IOCFE_LOG(_io, _fmt, ...) \ + printf("%s/%s: " _fmt, __FUNCTION__, (_io)->dname, ##__VA_ARGS__) + +static int bhnd_nvram_iocfe_new(struct bhnd_nvram_io **io, + char *dname); + +static struct bhnd_nvram_io *bhnd_nvram_find_cfedev(device_t dev, + char **dname, + bhnd_nvram_data_class_t **cls); + +/** Known CFE NVRAM device names, in probe order. */ +static char *nvram_cfe_devs[] = { + "nflash0.nvram", /* NAND */ + "nflash1.nvram", + "flash0.nvram", + "flash1.nvram", +}; + +/** Supported CFE NVRAM formats, in probe order. */ +static bhnd_nvram_data_class_t * const nvram_cfe_fmts[] = { + &bhnd_nvram_bcm_class, + &bhnd_nvram_tlv_class +}; + + +static int +bhnd_nvram_cfe_probe(device_t dev) +{ + struct bhnd_nvram_io *io; + bhnd_nvram_data_class_t *cls; + const char *cls_desc; + char *dname; + char *desc; + + /* Locate a usable CFE device */ + io = bhnd_nvram_find_cfedev(dev, &dname, &cls); + if (io == NULL) + return (ENXIO); + bhnd_nvram_io_free(io); + + /* Format the device description */ + cls_desc = bhnd_nvram_data_class_desc(cls); + asprintf(&desc, M_DEVBUF, "%s CFE %s", cls_desc, dname); + if (desc != NULL) { + device_set_desc_copy(dev, desc); + free(desc, M_DEVBUF); + } else { + device_set_desc(dev, cls_desc); + } + + /* Refuse wildcard attachments */ + return (BUS_PROBE_NOWILDCARD); +} + + +static int +bhnd_nvram_cfe_attach(device_t dev) +{ + struct bhnd_nvram_cfe_softc *sc; + bhnd_nvram_data_class_t *cls; + struct bhnd_nvram_io *io; + char *dname; + int error; + + sc = device_get_softc(dev); + sc->dev = dev; + + /* Locate NVRAM device via CFE */ + io = bhnd_nvram_find_cfedev(dev, &dname, &cls); + if (io == NULL) { + device_printf(dev, "CFE NVRAM device not found\n"); + return (ENXIO); + } + + /* Initialize NVRAM store and free the I/O context */ + error = bhnd_nvram_store_parse_new(&sc->store, io, cls); + bhnd_nvram_io_free(io); + if (error) + return (error); + + return (error); +} + +static int +bhnd_nvram_cfe_resume(device_t dev) +{ + return (0); +} + +static int +bhnd_nvram_cfe_suspend(device_t dev) +{ + return (0); +} + +static int +bhnd_nvram_cfe_detach(device_t dev) +{ + struct bhnd_nvram_cfe_softc *sc; + + sc = device_get_softc(dev); + + bhnd_nvram_store_free(sc->store); + + return (0); +} + +static int +bhnd_nvram_cfe_getvar(device_t dev, const char *name, void *buf, size_t *len, + bhnd_nvram_type type) +{ + struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev); + + return (bhnd_nvram_store_getvar(sc->store, name, buf, len, type)); +} + +static int +bhnd_nvram_cfe_setvar(device_t dev, const char *name, const void *buf, + size_t len, bhnd_nvram_type type) +{ + struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev); + + return (bhnd_nvram_store_setvar(sc->store, name, buf, len, type)); +} + +/** + * Find, open, identify, and return an I/O context mapping our + * CFE NVRAM device. + * + * @param dev bhnd_nvram_cfe device. + * @param[out] dname On success, the CFE device name. + * @param[out] cls On success, the identified NVRAM data format + * class. + * + * @retval non-NULL success. the caller inherits ownership of the returned + * NVRAM I/O context. + * @retval NULL if no usable CFE NVRAM device could be found. + */ +static struct bhnd_nvram_io * +bhnd_nvram_find_cfedev(device_t dev, char **dname, + bhnd_nvram_data_class_t **cls) +{ + struct bhnd_nvram_io *io; + int devinfo; + int error, result; + + for (u_int i = 0; i < nitems(nvram_cfe_fmts); i++) { + *cls = nvram_cfe_fmts[i]; + + for (u_int j = 0; j < nitems(nvram_cfe_devs); j++) { + *dname = nvram_cfe_devs[j]; + + /* Does the device exist? */ + if ((devinfo = cfe_getdevinfo(*dname)) < 0) { + if (devinfo != CFE_ERR_DEVNOTFOUND) { + device_printf(dev, "cfe_getdevinfo(%s) " + "failed: %d\n", *dname, devinfo); + } + + continue; + } + + /* Open for reading */ + if ((error = bhnd_nvram_iocfe_new(&io, *dname))) + continue; + + /* Probe */ + result = bhnd_nvram_data_probe(*cls, io); + if (result <= 0) { + /* Found a supporting NVRAM data class */ + return (io); + } + + /* Keep searching */ + bhnd_nvram_io_free(io); + io = NULL; + } + } + + return (NULL); +} + + +/** + * Allocate and return a new I/O context backed by a CFE device. + * + * The caller is responsible for deallocating the returned I/O context via + * bhnd_nvram_io_free(). + * + * @param[out] io On success, a valid I/O context for @p dname. + * @param dname The name of the CFE device to be opened for reading. + * + * @retval 0 success. + * @retval non-zero if opening @p dname otherwise fails, a standard unix error + * will be returned. + */ +static int +bhnd_nvram_iocfe_new(struct bhnd_nvram_io **io, char *dname) +{ + struct bhnd_nvram_iocfe *iocfe; + nvram_info_t nvram_info; + int cerr, devinfo, dtype, rlen; + int64_t nv_offset; + u_int nv_size; + bool req_blk_erase; + int error; + + iocfe = malloc(sizeof(*iocfe), M_DEVBUF, M_WAITOK); + iocfe->io.iops = &bhnd_nvram_iocfe_ops; + iocfe->dname = dname; + + /* Try to open the device */ + iocfe->fd = cfe_open(dname); + if (iocfe->fd <= 0) { + IOCFE_LOG(iocfe, "cfe_open() failed: %d\n", iocfe->fd); + + error = ENXIO; + goto failed; + } + + /* Try to fetch device info */ + if ((devinfo = cfe_getdevinfo(iocfe->dname)) < 0) { + IOCFE_LOG(iocfe, "cfe_getdevinfo() failed: %d\n", devinfo); + error = ENXIO; + goto failed; + } + + /* Verify device type */ + dtype = devinfo & CFE_DEV_MASK; + switch (dtype) { + case CFE_DEV_FLASH: + case CFE_DEV_NVRAM: + /* Valid device type */ + break; + default: + IOCFE_LOG(iocfe, "unknown device type: %d\n", dtype); + error = ENXIO; + goto failed; + } + + /* Try to fetch nvram info from CFE */ + cerr = cfe_ioctl(iocfe->fd, IOCTL_NVRAM_GETINFO, + (unsigned char *)&nvram_info, sizeof(nvram_info), &rlen, 0); + if (cerr == CFE_OK) { + /* Sanity check the result; must not be a negative integer */ + if (nvram_info.nvram_size < 0 || + nvram_info.nvram_offset < 0) + { + IOCFE_LOG(iocfe, "invalid NVRAM layout (%d/%d)\n", + nvram_info.nvram_size, nvram_info.nvram_offset); + error = ENXIO; + goto failed; + } + + nv_offset = nvram_info.nvram_offset; + nv_size = nvram_info.nvram_size; + req_blk_erase = (nvram_info.nvram_eraseflg != 0); + } else if (cerr != CFE_OK && cerr != CFE_ERR_INV_COMMAND) { + IOCFE_LOG(iocfe, "IOCTL_NVRAM_GETINFO failed: %d\n", cerr); + error = ENXIO; + goto failed; + } + + /* Fall back on flash info. + * + * This is known to be required on the Asus RT-N53 (CFE 5.70.55.33, + * BBP 1.0.37, BCM5358UB0), where IOCTL_NVRAM_GETINFO returns + * CFE_ERR_INV_COMMAND. + */ + if (cerr == CFE_ERR_INV_COMMAND) { + flash_info_t fi; + + cerr = cfe_ioctl(iocfe->fd, IOCTL_FLASH_GETINFO, + (unsigned char *)&fi, sizeof(fi), &rlen, 0); + + if (cerr != CFE_OK) { + IOCFE_LOG(iocfe, "IOCTL_FLASH_GETINFO failed %d\n", + cerr); + error = ENXIO; + goto failed; + } + + nv_offset = 0x0; + nv_size = fi.flash_size; + req_blk_erase = !(fi.flash_flags & FLASH_FLAG_NOERASE); + } + + + /* Verify that the full NVRAM layout can be represented via size_t */ + if (nv_size > SIZE_MAX || SIZE_MAX - nv_size < nv_offset) { + IOCFE_LOG(iocfe, "invalid NVRAM layout (%#x/%#jx)\n", + nv_size, (intmax_t)nv_offset); + error = ENXIO; + goto failed; + } + + iocfe->offset = nv_offset; + iocfe->size = nv_size; + iocfe->req_blk_erase = req_blk_erase; + + *io = &iocfe->io; + return (CFE_OK); + +failed: + if (iocfe->fd >= 0) + cfe_close(iocfe->fd); + + free(iocfe, M_DEVBUF); + + *io = NULL; + return (error); +} + +static void +bhnd_nvram_iocfe_free(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_iocfe *iocfe = (struct bhnd_nvram_iocfe *)io; + + cfe_close(iocfe->fd); + free(io, M_DEVBUF); +} + +static size_t +bhnd_nvram_iocfe_getsize(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_iocfe *iocfe = (struct bhnd_nvram_iocfe *)io; + return (iocfe->size); +} + +static int +bhnd_nvram_iocfe_setsize(struct bhnd_nvram_io *io, size_t size) +{ + /* unsupported */ + return (ENODEV); +} + +static int +bhnd_nvram_iocfe_read_ptr(struct bhnd_nvram_io *io, size_t offset, + const void **ptr, size_t nbytes, size_t *navail) +{ + /* unsupported */ + return (ENODEV); +} + +static int +bhnd_nvram_iocfe_write_ptr(struct bhnd_nvram_io *io, size_t offset, + void **ptr, size_t nbytes, size_t *navail) +{ + /* unsupported */ + return (ENODEV); +} + +static int +bhnd_nvram_iocfe_write(struct bhnd_nvram_io *io, size_t offset, void *buffer, + size_t nbytes) +{ + /* unsupported */ + return (ENODEV); +} + +static int +bhnd_nvram_iocfe_read(struct bhnd_nvram_io *io, size_t offset, void *buffer, + size_t nbytes) +{ + struct bhnd_nvram_iocfe *iocfe; + size_t remain; + int64_t cfe_offset; + int nr, nreq; + + iocfe = (struct bhnd_nvram_iocfe *)io; + + /* Determine (and validate) the base CFE offset */ +#if (SIZE_MAX > INT64_MAX) + if (iocfe->offset > INT64_MAX || offset > INT64_MAX) + return (ENXIO); +#endif + + if (INT64_MAX - offset < iocfe->offset) + return (ENXIO); + + cfe_offset = iocfe->offset + offset; + + /* Verify that cfe_offset + nbytes is representable */ + if (INT64_MAX - cfe_offset < nbytes) + return (ENXIO); + + /* Perform the read */ + for (remain = nbytes; remain > 0;) { + void *p; + size_t nread; + int64_t cfe_noff; + + nread = (nbytes - remain); + cfe_noff = cfe_offset + nread; + p = ((uint8_t *)buffer + nread); + nreq = ummin(INT_MAX, remain); + + nr = cfe_readblk(iocfe->fd, cfe_noff, p, nreq); + if (nr < 0) { + IOCFE_LOG(iocfe, "cfe_readblk() failed: %d\n", nr); + return (ENXIO); + } + + /* Check for unexpected short read */ + if (nr == 0 && remain > 0) { + /* If the request fits entirely within the CFE + * device range, we shouldn't hit EOF */ + if (remain < iocfe->size && + iocfe->size - remain > offset) + { + IOCFE_LOG(iocfe, "cfe_readblk() returned " + "unexpected short read (%d/%d)\n", nr, + nreq); + return (ENXIO); + } + } + + if (nr == 0) + break; + + remain -= nr; + } + + /* Check for short read */ + if (remain > 0) + return (ENXIO); + + return (0); +} + +static device_method_t bhnd_nvram_cfe_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, bhnd_nvram_cfe_probe), + DEVMETHOD(device_attach, bhnd_nvram_cfe_attach), + DEVMETHOD(device_resume, bhnd_nvram_cfe_resume), + DEVMETHOD(device_suspend, bhnd_nvram_cfe_suspend), + DEVMETHOD(device_detach, bhnd_nvram_cfe_detach), + + /* NVRAM interface */ + DEVMETHOD(bhnd_nvram_getvar, bhnd_nvram_cfe_getvar), + DEVMETHOD(bhnd_nvram_setvar, bhnd_nvram_cfe_setvar), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(bhnd_nvram, bhnd_nvram_cfe, bhnd_nvram_cfe_methods, + sizeof(struct bhnd_nvram_cfe_softc)); +EARLY_DRIVER_MODULE(bhnd_nvram_cfe, nexus, bhnd_nvram_cfe, + bhnd_nvram_devclass, NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_EARLY); diff --git a/sys/dev/bhnd/nvram/bhnd_sprom_parser.h b/sys/mips/broadcom/bcm_nvram_cfevar.h similarity index 57% rename from sys/dev/bhnd/nvram/bhnd_sprom_parser.h rename to sys/mips/broadcom/bcm_nvram_cfevar.h index 5237dc10797..6044111d5ae 100644 --- a/sys/dev/bhnd/nvram/bhnd_sprom_parser.h +++ b/sys/mips/broadcom/bcm_nvram_cfevar.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,36 +29,19 @@ * $FreeBSD$ */ -#ifndef _BHND_NVRAM_BHND_SPROM_PARSER_H_ -#define _BHND_NVRAM_BHND_SPROM_PARSER_H_ +#ifndef _MIPS_BROADCOM_BCM_NVRAM_CFE_H_ +#define _MIPS_BROADCOM_BCM_NVRAM_CFE_H_ -#include +#include +#include -struct bhnd_sprom; +#include +#include -int bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r, - bus_size_t offset); -void bhnd_sprom_fini(struct bhnd_sprom *sprom); -int bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf, - size_t *len, bhnd_nvram_type type); -int bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name, - const void *buf, size_t len, bhnd_nvram_type type); - -/** - * bhnd sprom parser instance state. - */ -struct bhnd_sprom { - device_t dev; /**< sprom parent device */ - - uint8_t sp_rev; /**< sprom revision */ - - struct bhnd_resource *sp_res; /**< sprom resource. */ - bus_size_t sp_res_off; /**< offset to sprom image */ - - uint8_t *sp_shadow; /**< sprom shadow */ - bus_size_t sp_size_max; /**< maximum possible sprom length */ - size_t sp_size; /**< shadow size */ - size_t sp_capacity; /**< shadow buffer capacity */ +/** bhnd_nvram_cfe driver instance state. */ +struct bhnd_nvram_cfe_softc { + device_t dev; + struct bhnd_nvram_store *store; /**< nvram store */ }; -#endif /* _BHND_NVRAM_BHND_SPROM_PARSER_H_ */ +#endif /* _MIPS_BROADCOM_BCM_NVRAM_CFE_H_ */ diff --git a/sys/mips/broadcom/files.broadcom b/sys/mips/broadcom/files.broadcom index 04070411444..f293c1e327e 100644 --- a/sys/mips/broadcom/files.broadcom +++ b/sys/mips/broadcom/files.broadcom @@ -7,6 +7,8 @@ mips/broadcom/bcm_machdep.c standard mips/broadcom/bcm_bmips.c optional siba_nexus siba mips/broadcom/bcm_mips74k.c optional bcma_nexus bcma +mips/broadcom/bcm_nvram_cfe.c optional bhnd siba_nexus cfe | \ + bhnd bcma_nexus cfe mips/broadcom/bcm_pmu.c standard mips/mips/tick.c standard diff --git a/sys/modules/bhnd/Makefile b/sys/modules/bhnd/Makefile index 9c99afd6eaa..21c7c816e0f 100644 --- a/sys/modules/bhnd/Makefile +++ b/sys/modules/bhnd/Makefile @@ -27,11 +27,21 @@ SRCS+= bhnd_pmu.c \ SRCS+= bhnd_pmu_if.c bhnd_pmu_if.h # NVRAM/SPROM -SRCS+= bhnd_nvram.c \ - bhnd_nvram_parser.c \ - bhnd_sprom.c \ - bhnd_sprom_parser.c -SRCS+= bhnd_nvram_common.c +SRCS+= bhnd_nvram_data.c \ + bhnd_nvram_data_bcm.c \ + bhnd_nvram_data_bcmraw.c \ + bhnd_nvram_data_btxt.c \ + bhnd_nvram_data_sprom.c \ + bhnd_nvram_data_tlv.c \ + bhnd_nvram_io.c \ + bhnd_nvram_iobuf.c \ + bhnd_nvram_iores.c \ + bhnd_nvram_store.c \ + bhnd_nvram_subr.c \ + bhnd_nvram_value.c \ + bhnd_nvram_value_fmts.c \ + bhnd_nvram_value_prf.c \ + bhnd_sprom.c SRCS+= bhnd_nvram_map.h bhnd_nvram_map_data.h SRCS+= bhnd_nvram_if.c bhnd_nvram_if.h