Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -1220,14 +1220,22 @@ 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 Index: sys/dev/bhnd/bhnd_subr.c =================================================================== --- sys/dev/bhnd/bhnd_subr.c +++ sys/dev/bhnd/bhnd_subr.c @@ -1057,7 +1057,8 @@ 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; Index: sys/dev/bhnd/nvram/bhnd_nvram.h =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram.h +++ 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 @@ */ } 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 bit flags */ -enum { - BHND_NVRAM_TF_SIGNED = (1<<7), -}; + 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 */ -#define BHND_NVRAM_TYPE_ID_MASK 0xF -#define BHND_NVRAM_TYPE_FLAGS_MASK 0x70 + /* 10-15 reserved for primitive (non-array) types */ -#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_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram.c =================================================================== --- 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)); Index: sys/dev/bhnd/nvram/bhnd_nvram_cfe.c =================================================================== --- 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); Index: sys/dev/bhnd/nvram/bhnd_nvram_common.h =================================================================== --- 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_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_common.c =================================================================== --- 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); -} Index: sys/dev/bhnd/nvram/bhnd_nvram_data.h =================================================================== --- /dev/null +++ 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_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_data.c =================================================================== --- /dev/null +++ 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)); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c =================================================================== --- /dev/null +++ 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]); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c =================================================================== --- /dev/null +++ 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); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h +++ sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h @@ -25,51 +25,49 @@ * 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_PARSERREG_H_ -#define _BHND_NVRAM_BHND_NVRAM_PARSERREG_H_ - - +#ifndef _BHND_NVRAM_BHND_NVRAM_BCMREG_H_ +#define _BHND_NVRAM_BHND_NVRAM_BCMREG_H_ -#define NVRAM_GET_BITS(_value, _field) \ - ((_value & _field ## _MASK) >> _field ## _SHIFT) +#define BCM_NVRAM_GET_BITS(_value, _field) \ + ((_value & _field ## _MASK) >> _field ## _SHIFT) -/* NVRAM header fields */ -#define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */ -#define NVRAM_VERSION 1 +/* BCM NVRAM header fields */ +#define BCM_NVRAM_MAGIC 0x48534C46 /* 'FLSH' */ +#define BCM_NVRAM_VERSION 1 -#define NVRAM_CRC_SKIP 9 /* skip magic, size, and crc8 */ +#define BCM_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 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 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 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 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 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 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" +#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" -/* 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 */ +#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_PARSERVAR_H_ */ +#endif /* _BHND_NVRAM_BHND_NVRAM_BCMREG_H_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h +++ 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_ - -#include "bhnd_sprom_parser.h" - -#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) */ - -/** Maximum supported SPROM image size */ -#define SPROM_SZ_MAX SPROM_SZ_R11 +#ifndef _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ +#define _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ -#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) */ +/** + * BCM NVRAM header value data. + */ +union bhnd_nvram_bcm_hvar_value { + uint16_t u16; + uint32_t u32; +}; -/** SPROM signature (rev 10) */ -#define SPROM_SIG_R10 SPROM_SIG_R4 -#define SPROM_SIG_R10_OFF 438 /**< SPROM signature offset (rev 10) */ +/** + * 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 */ -/** SPROM signature (rev 11) */ -#define SPROM_SIG_R11 0x0634 -#define SPROM_SIG_R11_OFF 128 /**< SPROM signature offset (rev 11) */ + /** 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; -#endif /* _BHND_NVRAM_SPROM_PARSERVAR_H_ */ +#endif /* _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c =================================================================== --- /dev/null +++ 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); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c =================================================================== --- /dev/null +++ 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); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h =================================================================== --- /dev/null +++ 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_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c =================================================================== --- /dev/null +++ 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); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_data_tlvreg.h =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_data_tlvreg.h +++ 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_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_datavar.h =================================================================== --- /dev/null +++ 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_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_io.h =================================================================== --- /dev/null +++ sys/dev/bhnd/nvram/bhnd_nvram_io.h @@ -0,0 +1,78 @@ +/*- + * 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, + size_t offset, 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_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_io.c =================================================================== --- /dev/null +++ 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)); +} + Index: sys/dev/bhnd/nvram/bhnd_nvram_iobuf.c =================================================================== --- /dev/null +++ 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); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_iores.c =================================================================== --- /dev/null +++ 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); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_iovar.h =================================================================== --- /dev/null +++ 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_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_parser.h =================================================================== --- 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_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_parser.c =================================================================== --- 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); -} Index: sys/dev/bhnd/nvram/bhnd_nvram_parservar.h =================================================================== --- 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_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_private.h =================================================================== --- /dev/null +++ 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_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_store.h =================================================================== --- sys/dev/bhnd/nvram/bhnd_nvram_store.h +++ 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; + +int bhnd_nvram_store_new(struct bhnd_nvram_store **store, + struct bhnd_nvram_data *data); + +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); -#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_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_NVRAMVAR_H_ */ +#endif /* _BHND_NVRAM_BHND_NVRAM_STORE_H_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_store.c =================================================================== --- /dev/null +++ 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); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_storevar.h =================================================================== --- /dev/null +++ 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_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_subr.c =================================================================== --- /dev/null +++ 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); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_value.h =================================================================== --- /dev/null +++ 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_ */ Index: sys/dev/bhnd/nvram/bhnd_nvram_value.c =================================================================== --- /dev/null +++ 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); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c =================================================================== --- /dev/null +++ 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); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c =================================================================== --- /dev/null +++ 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); +} Index: sys/dev/bhnd/nvram/bhnd_nvram_valuevar.h =================================================================== --- /dev/null +++ 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_ */ Index: sys/dev/bhnd/nvram/bhnd_sprom.c =================================================================== --- sys/dev/bhnd/nvram/bhnd_sprom.c +++ sys/dev/bhnd/nvram/bhnd_sprom.c @@ -52,15 +52,9 @@ #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 @@ 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 @@ 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; + } - /* Initialize mutex */ - SPROM_LOCK_INIT(sc); + 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; + + /* 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 @@ 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 @@ 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; - - sc = device_get_softc(dev); - - SPROM_LOCK(sc); - error = bhnd_sprom_getvar(&sc->shadow, name, buf, len, type); - SPROM_UNLOCK(sc); + struct bhnd_sprom_softc *sc = device_get_softc(dev); - return (error); + return (bhnd_nvram_store_getvar(sc->store, name, buf, len, type)); } /** @@ -191,16 +202,9 @@ 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; - - sc = device_get_softc(dev); - - SPROM_LOCK(sc); - error = bhnd_sprom_setvar(&sc->shadow, name, buf, len, type); - SPROM_UNLOCK(sc); + struct bhnd_sprom_softc *sc = device_get_softc(dev); - return (error); + return (bhnd_nvram_store_setvar(sc->store, name, buf, len, type)); } static device_method_t bhnd_sprom_methods[] = { @@ -218,5 +222,5 @@ 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); Index: sys/dev/bhnd/nvram/bhnd_sprom_parser.c =================================================================== --- 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 (','); - } -} Index: sys/dev/bhnd/nvram/bhnd_spromvar.h =================================================================== --- sys/dev/bhnd/nvram/bhnd_spromvar.h +++ 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 @@ * 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_ */ Index: sys/dev/bhnd/nvram/nvram_map =================================================================== --- sys/dev/bhnd/nvram/nvram_map +++ 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) -} +group "Antenna Configuration" { + u8 aa2g { + desc "Available 2.4GHz Antennas" + help "Antennas 0-3 are marked as available if the + corresponding bit is set." + } + u8 aa5g { + desc "Available 5GHz Antennas" + help "Antennas 0-3 are marked as available if the + corresponding bit is set." + } -# 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 -} + 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" + } + 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" + } + 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" + } + 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" + } -# 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 -} + u8 txchain { + desc "Available TX Chains" + help "TX chains 0-3 are marked as available if the + corresponding bit is set." -# 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 -} + all1 ignore + } -# 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 -} + u8 rxchain { + desc "Available RX Chains" + help "RX chains 0-3 are marked as available if the + corresponding bit is set." -# 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 -} + all1 ignore + } -# 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 + u16 antswitch { + desc "Antenna Diversity Switch Type" + help "The antenna diversity switch configuration used by + this device. The value is hardware-specific." + all1 ignore + } } -# 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 +u8 aga0 { + #desc + #help } -u32 mcsbw20ul2gpo { - srom 9-10 0x168 +u8 aga1 { + #desc + #help } -u32 mcsbw402gpo { - srom 9-10 0x16C - srom >= 11 0x158 +u8 aga2 { + #desc + #help } - -# 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 +u8 agbg0 { + #desc + #help } -u32 mcsbw20ul5ghpo { - srom 9-10 0x18C +u8 agbg1 { + #desc + #help } -u32 mcsbw405ghpo { - srom 9-10 0x190 - srom >= 11 0x184 +u8 agbg2 { + #desc + #help } - -# 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 +u8 antswctl2g { + #desc + #help } -u32 mcsbw20ul5glpo { - srom 9-10 0x174 +u8 antswctl5g { + #desc + #help } -u32 mcsbw405glpo { - srom 9-10 0x178 - srom >= 11 0x164 +u32 boardflags { + #desc + #help } - -# 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 boardflags2 { + #desc + #help } -u32 mcsbw20ul5gmpo { - srom 9-10 0x180 +u32 boardflags3 { + #desc + #help } -u32 mcsbw405gmpo { - srom 9-10 0x184 - srom >= 11 0x174 +u16 boardnum { + fmt decimal + #desc + #help } - -# 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 +u16 boardrev { + #desc + #help } - -u8 measpower { - srom 8-10 0xB4 (&0xFE, >>1) - srom >= 11 0xB0 (&0xFE, >>1) +u16 boardtype { + #desc + #help } -u8 measpower1 { - srom 8-10 0xBF (&0x7F) - srom >= 11 0xBB (&0x7F) +u16 boardvendor { + #desc + #help } -u8 measpower2 { - srom 8-10 u16 0xBE (&0x3F80, >>7) - srom >= 11 u16 0xBA (&0x3F80, >>7) +u16 bw40po { + #desc + #help } -u16 rawtempsense { - srom 8-10 0xB4 (&0x1FF) - srom >= 11 0xB0 (&0x1FF) +u16 bwduppo { + #desc + #help } - -u8 noiselvl2ga0 { - sfmt decimal - srom 8-10 0x1AB (&0x1F) - srom >= 11 0x1BD (&0x1F) +u8 bxa2g { + #desc + #help } -u8 noiselvl2ga1 { - sfmt decimal - srom 8-10 u16 0x1AA (&0x3E0, >>5) - srom >= 11 u16 0x1BC (&0x3E0, >>5) +u8 bxa5g { + #desc + #help } -u8 noiselvl2ga2 { - sfmt decimal - srom 8-10 0x1AA (&0x7C, >>2) - srom >= 11 0x1BC (&0x7C, >>2) +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[4] noiselvl5ga0 { - sfmt decimal - srom >= 11 u8 0x1BF (&0x1F), u8 0x1C1 (&0x1F), u8 0x1C3 (&0x1F), u8 0x1C5 (&0x1F) +u8 maxp5gla0 { + #desc + #help } -u8[4] noiselvl5ga1 { - sfmt decimal - srom >= 11 u16[4] 0x1BE (&0x3E0, >>5) +u8 maxp5gla1 { + #desc + #help } -u8[4] noiselvl5ga2 { - sfmt decimal - srom >= 11 u8 0x1BE (&0x7C, >>2), u8 0x1C0 (&0x7C, >>2), u8 0x1C2 (&0x7C, >>2), u8 0x1C4 (&0x7C, >>2) +u8 maxp5gla2 { + #desc + #help } - -# paparambwver -u8 paparambwver { - sfmt decimal - srom >= 11 0x190 (&0xF0, >>4) +u8 maxp5gla3 { + #desc + #help } - -# 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 +u16 mcs2gpo0 { + #desc + #help } - -# 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 mcs2gpo1 { + #desc + #help } - -u16 pdoffset40ma0 { - srom >= 11 0xCA +u16 mcs2gpo2 { + #desc + #help } -u16 pdoffset40ma1 { - srom >= 11 0xCC +u16 mcs2gpo3 { + #desc + #help } -u16 pdoffset40ma2 { - srom >= 11 0xCE +u16 mcs2gpo4 { + #desc + #help } -u16 pdoffset80ma0 { - srom >= 11 0xD0 +u16 mcs2gpo5 { + #desc + #help } -u16 pdoffset80ma1 { - srom >= 11 0xD2 +u16 mcs2gpo6 { + #desc + #help } -u16 pdoffset80ma2 { - srom >= 11 0xD4 +u16 mcs2gpo7 { + #desc + #help } - -u8 pdoffset2g40ma0 { - srom >= 11 0xC9 (&0xF) +u16 mcs32po { + #desc + #help } -u8 pdoffset2g40ma1 { - srom >= 11 0xC9 (&0xF0, >>4) +u16 mcs5ghpo0 { + #desc + #help } -u8 pdoffset2g40ma2 { - srom >= 11 0xC8 (&0xF) +u16 mcs5ghpo1 { + #desc + #help } -u8 pdoffset2g40mvalid { - srom >= 11 0xC8 (&0x80, >>7) +u16 mcs5ghpo2 { + #desc + #help } - -# 40Mhz channel 2g/5g power offset -u16 bw40po { - srom 4-7 0x18E - srom 8 0x196 +u16 mcs5ghpo3 { + #desc + #help } - -# 40Mhz channel dup 2g/5g power offset -u16 bwduppo { - srom 4-7 0x190 - srom 8 0x198 +u16 mcs5ghpo4 { + #desc + #help } - -# cck2g/ofdm2g/ofdm5g power offset -u16 cck2gpo { - srom 4-7 0x138 - srom 8 0x140 +u16 mcs5ghpo5 { + #desc + #help } -u32 ofdm2gpo { - srom 4-7 0x13A - srom 8 0x142 +u16 mcs5ghpo6 { + #desc + #help } -u32 ofdm5gpo { - srom 4-7 0x13E - srom 8 0x146 +u16 mcs5ghpo7 { + #desc + #help } -u32 ofdm5glpo { - srom 4-7 0x142 - srom 8 0x14A +u16 mcs5glpo0 { + #desc + #help } -u32 ofdm5ghpo { - srom 4-7 0x146 - srom 8 0x14E +u16 mcs5glpo1 { + #desc + #help } - -# cdd2g/5g power offset -u16 cddpo { - srom 4-7 0x18A - srom 8 0x192 +u16 mcs5glpo2 { + #desc + #help } - -# mcs2g power offset -u16 mcs2gpo0 { - srom 4-7 0x14A - srom 8 0x152 +u16 mcs5glpo3 { + #desc + #help } -u16 mcs2gpo1 { - srom 4-7 0x14C - srom 8 0x154 +u16 mcs5glpo4 { + #desc + #help } -u16 mcs2gpo2 { - srom 4-7 0x14E - srom 8 0x156 +u16 mcs5glpo5 { + #desc + #help } -u16 mcs2gpo3 { - srom 4-7 0x150 - srom 8 0x158 +u16 mcs5glpo6 { + #desc + #help } -u16 mcs2gpo4 { - srom 4-7 0x152 - srom 8 0x15A +u16 mcs5glpo7 { + #desc + #help } -u16 mcs2gpo5 { - srom 4-7 0x154 - srom 8 0x15C +u16 mcs5gpo0 { + #desc + #help } -u16 mcs2gpo6 { - srom 4-7 0x156 - srom 8 0x15E +u16 mcs5gpo1 { + #desc + #help } -u16 mcs2gpo7 { - srom 4-7 0x158 - srom 8 0x160 +u16 mcs5gpo2 { + #desc + #help } - -# mcs5g low-high band power offset -u16 mcs5glpo0 { - srom 4-7 0x16A - srom 8 0x172 +u16 mcs5gpo3 { + #desc + #help } -u16 mcs5glpo1 { - srom 4-7 0x16C - srom 8 0x174 +u16 mcs5gpo4 { + #desc + #help } -u16 mcs5glpo2 { - srom 4-7 0x16E - srom 8 0x176 +u16 mcs5gpo5 { + #desc + #help } -u16 mcs5glpo3 { - srom 4-7 0x170 - srom 8 0x178 +u16 mcs5gpo6 { + #desc + #help } -u16 mcs5glpo4 { - srom 4-7 0x172 - srom 8 0x17A +u16 mcs5gpo7 { + #desc + #help } -u16 mcs5glpo5 { - srom 4-7 0x174 - srom 8 0x17C +u32 mcsbw202gpo { + #desc + #help } -u16 mcs5glpo6 { - srom 4-7 0x176 - srom 8 0x17E +u32 mcsbw205ghpo { + #desc + #help } -u16 mcs5glpo7 { - srom 4-7 0x178 - srom 8 0x180 +u32 mcsbw205glpo { + #desc + #help } -u16 mcs5ghpo0 { - srom 4-7 0x17A - srom 8 0x182 +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 mcs5ghpo1 { - srom 4-7 0x17C - srom 8 0x184 +u16 pa5gbw80a0[12] { + #desc + #help } -u16 mcs5ghpo2 { - srom 4-7 0x17E - srom 8 0x186 +u16 pa5ghw0a0 { + #desc + #help } -u16 mcs5ghpo3 { - srom 4-7 0x180 - srom 8 0x188 +u16 pa5ghw0a1 { + #desc + #help } -u16 mcs5ghpo4 { - srom 4-7 0x182 - srom 8 0x18A +u16 pa5ghw0a2 { + #desc + #help } -u16 mcs5ghpo5 { - srom 4-7 0x184 - srom 8 0x18C +u16 pa5ghw0a3 { + #desc + #help } -u16 mcs5ghpo6 { - srom 4-7 0x186 - srom 8 0x18E +u16 pa5ghw1a0 { + #desc + #help } -u16 mcs5ghpo7 { - srom 4-7 0x188 - srom 8 0x190 +u16 pa5ghw1a1 { + #desc + #help } - -# mcs5g mid band power offset -u16 mcs5gpo0 { - srom 4-7 0x15A - srom 8 0x162 +u16 pa5ghw1a2 { + #desc + #help } -u16 mcs5gpo1 { - srom 4-7 0x15C - srom 8 0x164 +u16 pa5ghw1a3 { + #desc + #help } -u16 mcs5gpo2 { - srom 4-7 0x15E - srom 8 0x166 +u16 pa5ghw2a0 { + #desc + #help } -u16 mcs5gpo3 { - srom 4-7 0x160 - srom 8 0x168 +u16 pa5ghw2a1 { + #desc + #help } -u16 mcs5gpo4 { - srom 4-7 0x162 - srom 8 0x16A +u16 pa5ghw2a2 { + #desc + #help } -u16 mcs5gpo5 { - srom 4-7 0x164 - srom 8 0x16C +u16 pa5ghw2a3 { + #desc + #help } -u16 mcs5gpo6 { - srom 4-7 0x166 - srom 8 0x16E +u16 pa5ghw3a0 { + #desc + #help } -u16 mcs5gpo7 { - srom 4-7 0x168 - srom 8 0x170 +u16 pa5ghw3a1 { + #desc + #help } - -# stbc2g/5g power offset -u16 stbcpo { - srom 4-7 0x18C - srom 8 0x194 +u16 pa5ghw3a2 { + #desc + #help } - -u8 regrev { - srom 3 0x78 - srom 4 0x55 - srom 5-7 0x47 - srom 8-10 0x95 - srom >= 11 0x99 +u16 pa5ghw3a3 { + #desc + #help } - -# 4328 2G RSSI mid pt sel & board switch arch, -# 2 bytes, rev 3. -# -u8 rssismf2g { - srom 3 0x51 (&0xF) - srom 8-10 0xA5 (&0xF) +u16 pa5glw0a0 { + #desc + #help } -u8 rssismc2g { - srom 3 0x51 (&0xF0, >>4) - srom 8-10 0xA5 (&0xF0, >>4) +u16 pa5glw0a1 { + #desc + #help } -u8 rssisav2g { - srom 3 0x50 (&0x7) - srom 8-10 0xA4 (&0x7) +u16 pa5glw0a2 { + #desc + #help } -u8 bxa2g { - srom 3 0x50 (&0x18, >>3) - srom 8-10 0xA4 (&0x18, >>3) +u16 pa5glw0a3 { + #desc + #help } - -# 4328 5G RSSI mid pt sel & board switch arch, -# 2 bytes, rev 3. -# -u8 rssismf5g { - srom 3 0x53 (&0xF) - srom 8-10 0xA7 (&0xF) +u16 pa5glw1a0 { + #desc + #help } -u8 rssismc5g { - srom 3 0x53 (&0xF0, >>4) - srom 8-10 0xA7 (&0xF0, >>4) +u16 pa5glw1a1 { + #desc + #help } -u8 rssisav5g { - srom 3 0x52 (&0x7) - srom 8-10 0xA6 (&0x7) +u16 pa5glw1a2 { + #desc + #help } -u8 bxa5g { - srom 3 0x52 (&0x18, >>3) - srom 8-10 0xA6 (&0x18, >>3) +u16 pa5glw1a3 { + #desc + #help } - -u8 rxgainerr2ga0 { - srom 8-10 0x19B (&0x3F) - srom >= 11 0x1C7 (&0x3F) +u16 pa5glw2a0 { + #desc + #help } -u8 rxgainerr2ga1 { - srom 8-10 u16 0x19A (&0x7C0, >>6) - srom >= 11 u16 0x1C6 (&0x7C0, >>6) +u16 pa5glw2a1 { + #desc + #help } -u8 rxgainerr2ga2 { - srom 8-10 0x19A (&0xF8, >>3) - srom >= 11 0x1C6 (&0xF8, >>3) +u16 pa5glw2a2 { + #desc + #help } -u8[4] rxgainerr5ga0 { - srom >= 11 u8 0x1C9 (&0x3F), u8 0x1CB (&0x3F), u8 0x1CD (&0x3F), u8 0x1CF (&0x3F) +u16 pa5glw2a3 { + #desc + #help } -u8[4] rxgainerr5ga1 { - srom >= 11 u16[4] 0x1C8 (&0x7C0, >>6) +u16 pa5glw3a0 { + #desc + #help } -u8[4] rxgainerr5ga2 { - srom >= 11 u8 0x1C8 (&0xF8, >>3), u8 0x1CA (&0xF8, >>3), u8 0x1CC (&0xF8, >>3), u8 0x1CE (&0xF8, >>3) +u16 pa5glw3a1 { + #desc + #help } -u8 rxgainerr5gha0 { - srom 8-10 0x1A1 (&0x3F) +u16 pa5glw3a2 { + #desc + #help } -u8 rxgainerr5gha1 { - srom 8-10 u16 0x1A0 (&0x7C0, >>6) +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 rxgainerr5gha2 { - srom 8-10 0x1A0 (&0xF8, >>3) +u8 rssisav5g { + #desc + #help } -u8 rxgainerr5gla0 { - srom 8-10 0x19D (&0x3F) +u8 rssismc2g { + #desc + #help } -u8 rxgainerr5gla1 { - srom 8-10 u16 0x19C (&0x7C0, >>6) +u8 rssismc5g { + #desc + #help } -u8 rxgainerr5gla2 { - srom 8-10 0x19C (&0xF8, >>3) +u8 rssismf2g { + #desc + #help } -u8 rxgainerr5gma0 { - srom 8-10 0x19F (&0x3F) +u8 rssismf5g { + #desc + #help } -u8 rxgainerr5gma1 { - srom 8-10 u16 0x19E (&0x7C0, >>6) +u8 rxgainerr2ga0 { + #desc + #help } -u8 rxgainerr5gma2 { - srom 8-10 0x19E (&0xF8, >>3) +u8 rxgainerr2ga1 { + #desc + #help } -u8 rxgainerr5gua0 { - srom 8-10 0x1A3 (&0x3F) +u8 rxgainerr2ga2 { + #desc + #help } -u8 rxgainerr5gua1 { - srom 8-10 u16 0x1A2 (&0x7C0, >>6) +u8 rxgainerr5ga0[4] { + #desc + #help } -u8 rxgainerr5gua2 { - srom 8-10 0x1A2 (&0xF8, >>3) +u8 rxgainerr5ga1[4] { + #desc + #help } - -# 4328 2G RX power offset -i8 rxpo2g { - sfmt decimal - srom 3 0x5B - srom 8-10 0xAD +u8 rxgainerr5ga2[4] { + #desc + #help } - -# 4328 5G RX power offset -i8 rxpo5g { - sfmt decimal - srom 3 0x5A - srom 8-10 0xAC +u8 rxgainerr5gha0 { + #desc + #help } - -u16 subband5gver { - srom 8-10 u8 0x1A5 (&0x7) - srom >= 11 0xD6 +u8 rxgainerr5gha1 { + #desc + #help } - -# 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 +u8 rxgainerr5gha2 { + #desc + #help } - -# 4328 2G TR isolation, 1 byte -u8 tri2g { - srom 3 0x55 - srom 8-10 0xA9 +u8 rxgainerr5gla0 { + #desc + #help } - -# 4328 5G TR isolation, 3 bytes -u8 tri5gl { - srom 3 0x57 - srom 8-10 0xAB +u8 rxgainerr5gla1 { + #desc + #help } -u8 tri5g { - srom 3 0x54 - srom 8-10 0xA8 +u8 rxgainerr5gla2 { + #desc + #help } -u8 tri5gh { - srom 3 0x56 - srom 8-10 0xAA +u8 rxgainerr5gma0 { + #desc + #help } - -# phy txbf rpcalvars -u16 rpcal2g { - srom >= 11 0x16C +u8 rxgainerr5gma1 { + #desc + #help } -u16 rpcal5gb0 { - srom >= 11 0x16E +u8 rxgainerr5gma2 { + #desc + #help } -u16 rpcal5gb1 { - srom >= 11 0x17C +u8 rxgainerr5gua0 { + #desc + #help } -u16 rpcal5gb2 { - srom >= 11 0x17E +u8 rxgainerr5gua1 { + #desc + #help } -u16 rpcal5gb3 { - srom >= 11 0x18C +u8 rxgainerr5gua2 { + #desc + #help } - -# Crystal frequency in kilohertz -u32 xtalfreq { - sfmt decimal - srom >= 11 u16 0xB4 +u8 rxgains2gelnagaina0 { + #desc + #help } - -# N-PHY tx power workaround -u8 txpid2ga0 { - srom 4-7 0x63 +u8 rxgains2gelnagaina1 { + #desc + #help } -u8 txpid2ga1 { - srom 4-7 0x62 +u8 rxgains2gelnagaina2 { + #desc + #help } -u8 txpid2ga2 { - srom 4-7 0x65 +u8 rxgains2gelnagaina3 { + #desc + #help } -u8 txpid2ga3 { - srom 4-7 0x64 +u8 rxgains2gtrelnabypa0 { + #desc + #help } -u8 txpid5ga0 { - srom 4-7 0x67 +u8 rxgains2gtrelnabypa1 { + #desc + #help } -u8 txpid5ga1 { - srom 4-7 0x66 +u8 rxgains2gtrelnabypa2 { + #desc + #help } -u8 txpid5ga2 { - srom 4-7 0x69 +u8 rxgains2gtrelnabypa3 { + #desc + #help } -u8 txpid5ga3 { - srom 4-7 0x68 +u8 rxgains2gtrisoa0 { + #desc + #help } -u8 txpid5gha0 { - srom 4-7 0x6F +u8 rxgains2gtrisoa1 { + #desc + #help } -u8 txpid5gha1 { - srom 4-7 0x6E +u8 rxgains2gtrisoa2 { + #desc + #help } -u8 txpid5gha2 { - srom 4-7 0x71 +u8 rxgains2gtrisoa3 { + #desc + #help } -u8 txpid5gha3 { - srom 4-7 0x70 +u8 rxgains5gelnagaina0 { + #desc + #help } -u8 txpid5gla0 { - srom 4-7 0x6B +u8 rxgains5gelnagaina1 { + #desc + #help } -u8 txpid5gla1 { - srom 4-7 0x6A +u8 rxgains5gelnagaina2 { + #desc + #help } -u8 txpid5gla2 { - srom 4-7 0x6D +u8 rxgains5gelnagaina3 { + #desc + #help } -u8 txpid5gla3 { - srom 4-7 0x6C +u8 rxgains5ghelnagaina0 { + #desc + #help } - -u16 cckPwrOffset { - srom 10 0x1B4 +u8 rxgains5ghelnagaina1 { + #desc + #help } -u8[6] et1macaddr { - sfmt macaddr - srom 0-2 u8 0x55, u8 0x54, u8 0x57, u8 0x56, u8 0x59, u8 0x58 +u8 rxgains5ghelnagaina2 { + #desc + #help } -u8 eu_edthresh2g { - srom 8 0x1A9 - srom 9 0x199 - srom 10 0x199 - srom 11 0x1D1 +u8 rxgains5ghelnagaina3 { + #desc + #help } -u8 eu_edthresh5g { - srom 8 0x1A8 - srom 9 0x198 - srom 10 0x198 - srom 11 0x1D0 +u8 rxgains5ghtrelnabypa0 { + #desc + #help } -u8 freqoffset_corr { - srom 8-10 0xB9 (&0xF) +u8 rxgains5ghtrelnabypa1 { + #desc + #help } -u8 hw_iqcal_en { - srom 8-10 0xB9 (&0x20, >>5) +u8 rxgains5ghtrelnabypa2 { + #desc + #help } -u8[6] il0macaddr { - sfmt macaddr - srom 0-2 u8 0x49, u8 0x48, u8 0x51, u8 0x50, u8 0x53, u8 0x52 +u8 rxgains5ghtrelnabypa3 { + #desc + #help } -u8 iqcal_swp_dis { - srom 8-10 0xB9 (&0x10, >>4) +u8 rxgains5ghtrisoa0 { + #desc + #help } - -u8 noisecaloffset { - srom 8-9 0x1B5 +u8 rxgains5ghtrisoa1 { + #desc + #help } -u8 noisecaloffset5g { - srom 8-9 0x1B4 +u8 rxgains5ghtrisoa2 { + #desc + #help } -u8 noiselvl5gha0 { - srom 8-10 0x1B1 (&0x1F) +u8 rxgains5ghtrisoa3 { + #desc + #help } -u8 noiselvl5gha1 { - srom 8-10 u16 0x1B0 (&0x3E0, >>5) +u8 rxgains5gmelnagaina0 { + #desc + #help } -u8 noiselvl5gha2 { - srom 8-10 0x1B0 (&0x7C, >>2) +u8 rxgains5gmelnagaina1 { + #desc + #help } -u8 noiselvl5gla0 { - srom 8-10 0x1AD (&0x1F) +u8 rxgains5gmelnagaina2 { + #desc + #help } -u8 noiselvl5gla1 { - srom 8-10 u16 0x1AC (&0x3E0, >>5) +u8 rxgains5gmelnagaina3 { + #desc + #help } -u8 noiselvl5gla2 { - srom 8-10 0x1AC (&0x7C, >>2) +u8 rxgains5gmtrelnabypa0 { + #desc + #help } -u8 noiselvl5gma0 { - srom 8-10 0x1AF (&0x1F) +u8 rxgains5gmtrelnabypa1 { + #desc + #help } -u8 noiselvl5gma1 { - srom 8-10 u16 0x1AE (&0x3E0, >>5) +u8 rxgains5gmtrelnabypa2 { + #desc + #help } -u8 noiselvl5gma2 { - srom 8-10 0x1AE (&0x7C, >>2) +u8 rxgains5gmtrelnabypa3 { + #desc + #help } -u8 noiselvl5gua0 { - srom 8-10 0x1B3 (&0x1F) +u8 rxgains5gmtrisoa0 { + #desc + #help } -u8 noiselvl5gua1 { - srom 8-10 u16 0x1B2 (&0x3E0, >>5) +u8 rxgains5gmtrisoa1 { + #desc + #help } -u8 noiselvl5gua2 { - srom 8-10 0x1B2 (&0x7C, >>2) +u8 rxgains5gmtrisoa2 { + #desc + #help } - -u8 pcieingress_war { - srom 8-10 0x1A7 (&0xF) +u8 rxgains5gmtrisoa3 { + #desc + #help } - -u8 pdoffsetcckma0 { - srom >= 11 0x18F (&0xF) +u8 rxgains5gtrelnabypa0 { + #desc + #help } -u8 pdoffsetcckma1 { - srom >= 11 0x18F (&0xF0, >>4) +u8 rxgains5gtrelnabypa1 { + #desc + #help } -u8 pdoffsetcckma2 { - srom >= 11 0x18E (&0xF) +u8 rxgains5gtrelnabypa2 { + #desc + #help } - -u8 sar2g { - srom 9-10 0x1A9 - srom >= 11 0x1BB +u8 rxgains5gtrelnabypa3 { + #desc + #help } -u8 sar5g { - srom 9-10 0x1A8 - srom >= 11 0x1BA +u8 rxgains5gtrisoa0 { + #desc + #help } - -u32[5] swctrlmap_2g { - srom 10 u32[4] 0x1B8, u16 0x1C8 +u8 rxgains5gtrisoa1 { + #desc + #help } - -u16 tssifloor2g { - srom >= 11 0xBE (&0x3FF) -} -u16[4] tssifloor5g { - srom >= 11 0xC0 (&0x3FF) -} - -u8 txidxcap2g { - srom >= 11 u16 0x1A8 (&0xFF0, >>4) +u8 rxgains5gtrisoa2 { + #desc + #help } -u8 txidxcap5g { - srom >= 11 u16 0x1AC (&0xFF0, >>4) +u8 rxgains5gtrisoa3 { + #desc + #help } - -# -# 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 - } - 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) +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 + } } - u8 rxgains2gtrelnabypa { - srom >= 11 0xB (&0x80, >>7) + 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 } - u8 rxgains5gmtrisoa { - srom >= 11 0x9 (&0x78, >>3) + 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) + } } - u8 rxgains5gmelnagaina { - srom >= 11 0x9 (&0x7) + srom 3 { + 0x072: u32 boardflags { + +0x0: u16 | + +0x8: u16 (<<16) + } } - u8 rxgains2gelnagaina { - srom >= 11 0xB (&0x7) + 0x074: u8 ag0 + 0x075: u8 ag1 + 0x076: char ccode[2] { +0x1, +0x0 } + 0x078: u8 opo + srom 3 { + 0x079: u8 regrev + 0x07C: u16 leddc } - u8 rxgains5gtrisoa { - srom >= 11 0xA (&0x78, >>3) + 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) } - u8 rxgains5gtrelnabypa { - srom >= 11 0xA (&0x80, >>7) + 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 } - u8 rxgains2gtrisoa { - srom >= 11 0xB (&0x78, >>3) - } - u8 rxgains5ghtrisoa { - srom >= 11 0x8 (&0x78, >>3) + srom 10 { + 0x1B4: u16 cckPwrOffset + 0x1B6: u16 (=0x5372) + 0x1B8: u32 swctrlmap_2g[5] { + +0x0: u32[4], + +0x10: u16 + } + 0x1CA: u8 sromrev + 0x1CB: u8 } +} - # 11n PA parameters - u16 pa5gw2a { - srom 4-7 0x12 - srom 8-10 0x10 - } - u16 pa5ghw1a { - srom 4-7 0x20 - srom 8-10 0x1A - } - u16 pa5glw3a { - srom 4-7 0x1C - } - u16 pa5glw1a { - srom 4-7 0x18 - srom 8-10 0x14 - } - u16 pa5gw1a { - srom 4-7 0x10 - srom 8-10 0xE - } - u16 pa5glw0a { - srom 4-7 0x16 - srom 8-10 0x12 - } - 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 - } + +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 } Index: sys/dev/bhnd/tools/nvram_map_gen.awk =================================================================== --- sys/dev/bhnd/tools/nvram_map_gen.awk +++ 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 @@ 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 @@ 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,441 +124,3330 @@ 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")?,?$" - - IDENT_REGEX = "^[A-Za-z_][A-Za-z0-9_]*,?$" - SROM_OFF_REGEX = "("TYPES_REGEX"|"HEX_REGEX")" - - # 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 - - # Property types - PROP_T_SFMT = "sfmt" - PROP_T_ALL1 = "all1" - - # 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" - - # Common array keys - DEF_LINE = "def_line" - NUM_REVS = "num_revs" - REV = "rev" - - # Revision array keys - REV_START = "rev_start" - REV_END = "rev_end" - REV_DESC = "rev_decl" - REV_NUM_OFFS = "num_offs" - - # Offset array keys - OFF = "off" - OFF_NUM_SEGS = "off_num_segs" - OFF_SEG = "off_seg" - - # Segment array keys - SEG_ADDR = "seg_addr" - SEG_COUNT = "seg_count" - SEG_TYPE = "seg_type" - SEG_MASK = "seg_mask" - SEG_SHIFT = "seg_shift" - - # 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" + TYPES_REGEX = "^(((u|i)(8|16|32))|char)("ARRAY_REGEX")?$" + + IDENT_REGEX = "[A-Za-z_][A-Za-z0-9_]*" + SVAR_IDENT_REGEX = "^<"IDENT_REGEX">{?$" # identifiers + VAR_IDENT_REGEX = "^"IDENT_REGEX"{?$" # var identifiers + + VACCESS_REGEX = "^(private|internal)$" + + # Property array keys + PROP_ID = "p_id" + PROP_NAME = "p_name" + + # Prop path array keys + PPATH_HEAD = "ppath_head" + PPATH_TAIL = "ppath_tail" + + # Object array keys + OBJ_IS_CLS = "o_is_cls" + OBJ_SUPER = "o_super" + OBJ_PROP = "o_prop" + + # Class array keys + CLS_NAME = "cls_name" + CLS_PROP = "cls_prop" + + # 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" + + 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" + + 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" + # Skip completion handling if exiting from an error + if (_EARLY_EXIT) + exit 1 + + # Check for complete block closure + if (!in_parser_context(NVRAM)) { + _state = parser_state_get() + _block_start = get(_state, p_line) + errorx("missing '}' for block opened on line " _block_start "") + } + + # 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) + } - if (vars[v,VAR_PRIVATE]) - _flags[_num_flags++] = "BHND_NVRAM_VF_MFGINT" + # Sort by variable name + array_sort(_output_vars, prop_to_path(p_name)) - if (vars[v,VAR_IGNALL1]) - _flags[_num_flags++] = "BHND_NVRAM_VF_IGNALL1" - - if (_num_flags == 0) - _flags[_num_flags++] = "0" + # 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 + emit("/*\n") + emit(" * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n") + emit(" *\n") + emit(" * generated from nvram map: " FILENAME "\n") + emit(" */\n") + emit("\n") + + # Emit all variable definitions + if (OUT_T == OUT_T_DATA) { + write_data(_output_vars) + } else if (OUT_T == OUT_T_HEADER) { + write_header(_output_vars) + } - return (join(_flags, "|", _num_flags)) + printf("%u variable records written to %s\n", array_size(_output_vars), + OUTPUT_FILE) >> "/dev/stderr" } -# emit the bhnd_sprom_offsets for a given variable revision key -function emit_var_sprom_offsets (v, revk) +# Write the public header (output type HEADER) +function write_header(output_vars, _noutput_vars, _var, + _tab_align, _macro, _macros, _num_macros, _i) { - emit(sprintf("{{%u, %u}, (struct bhnd_sprom_offset[]) {\n", - vars[revk,REV_START], - vars[revk,REV_END])) - output_depth++ + # 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) - 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] + # Write the macros + for (_i = 0; _i < _num_macros; _i++) + write_macro_define(_macros[_i], _tab_align) +} - for (seg = 0; seg < num_segs; seg++) { - segk = subkey(offk, OFF_SEG, seg"") +# 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) +{ + _nvram = parser_state_get_context(NVRAM) + _layouts = get(_nvram, p_srom_layouts) + _nlayouts = array_size(_layouts) - 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 + _noutput_vars = array_size(output_vars) - 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])) + # Write all our private NVAR_ID defines + write_data_defines(output_vars) - num_offs_written++ - } - } + # 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("}, " num_offs_written "},\n") + emit("},\n") } -# emit a bhnd_nvram_vardef for variable name `v` -function emit_nvram_vardef (v) +# 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) { - 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))) + _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") - for (rev = 0; rev < vars[v,NUM_REVS]; rev++) { - revk = subkey(v, REV, rev"") - emit_var_sprom_offsets(v, revk) + 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("}, " vars[v,NUM_REVS] "},\n") + 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) } -# emit a header name #define for variable `v` -function emit_var_namedef (v) +# Return the current type width, or throw an error if no type is currently +# specified. +function srom_ops_get_type_width(opstream, _type) { - emit("#define\tBHND_NVAR_" toupper(v) "\t\"" v "\"\n") + 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) } -# 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) +# 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) { - # 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] + 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)") + } } -# generate a complete set of variable definitions for struct variable `st_vid`. -function gen_struct_vars (st_vid) +# Emit initial OPCODE_VAR opcode and update opstream state +function srom_ops_reset_var(opstream, var, _vid_prev, _vid, _vid_name, + _type, _base_type) { - 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 - } + 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 } - # 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) - } - } + # 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; -END { - # Skip completion handling if exiting from an error - if (_EARLY_EXIT) - exit 1 + # Update stream state + set(opstream, p_revisions, revisions) - # Check for complete block closure - if (depth > 0) { - block_start = g(STATE_LINENO) - errorx("missing '}' for block opened on line " block_start "") + _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 } - # 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 - } + # 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)) } - # Apply lexicographical sorting. To support more effecient table - # searching, we guarantee a stable sort order (using C collation). - sort(output_vars) + 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)) +} - # Truncate output file and write common header - printf("") > OUTPUT_FILE - emit("/*\n") - emit(" * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n") - emit(" *\n") - emit(" * generated from nvram map: " FILENAME "\n") - emit(" */\n") - emit("\n") +# 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) - # 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") - } else if (OUT_T == OUT_T_HEADER) { - for (i = 0; i < num_output_vars; i++) - emit_var_namedef(output_vars[i]) + # 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)) } +} - printf("%u variable records written to %s\n", num_output_vars, - OUTPUT_FILE) >> "/dev/stderr" +# 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) -# -# Print usage -# -function usage () + 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) { - print "usage: bhnd_nvram_map.awk [-hd] [-o output file]" - _EARLY_EXIT = 1 - exit 1 + 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. # -# Join all array elements with the given separator +# 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. # -function join (array, sep, count) +# 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) { - if (count == 0) - return ("") + obj_assert_class(opstream, SromOpStream) + obj_assert_class(segment, SromSegment) - _result = array[0] - for (_ji = 1; _ji < count; _ji++) - _result = _result sep array[_ji] + # Determine basic bind parameters + _count = 1 + _skip_in = 1 + _skip_out = continued ? 0 : 1 - return (_result) + # 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) } -# -# Sort a contiguous integer-indexed array, using standard awk comparison -# operators over its values. -# -function sort (array) { - # determine array size - _sort_alen = 0 +# (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) - for (_ssort_key in array) - _sort_alen++ + _opstream_offset = get(opstream, p_offset) + _offset = get(bind, p_offset) + if (_opstream_offset != _offset) + errorx("stream/bind offset state mismatch") - if (_sort_alen <= 1) - return + _count = get(bind, p_count) + _width = get(bind, p_width) + _skip_in = get(bind, p_skip_in) - # perform sort - _qsort(array, 0, _sort_alen-1) + set(opstream, p_offset, + _opstream_offset + ((_width * _skip_in) * _count)) } -function _qsort (array, first, last) +# (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) { - if (first >= last) + 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 - # select pivot element - _qpivot = int(first + int((last-first+1) * rand())) - _qleft = first - _qright = last + # 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 + } - _qpivot_val = array[_qpivot] + # Increment bind count + set(opstream, p_bind_total, _bind_total + 1) - # partition - while (_qleft <= _qright) { - while (array[_qleft] < _qpivot_val) - _qleft++ + # 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) +} + + +# 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) + + emit("#define\t" get(macro, p_name) _tabstr get(macro, p_value) "\n") +} + +# 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 + + # select pivot element + _qpivot = int(first + int((last-first+1) * rand())) + _qleft = first + _qright = last + + _qpivot_val = _qsort_get_key(array, _qpivot, prop_path) + + # partition + while (_qleft <= _qright) { + while (_qsort_get_key(array, _qleft, prop_path) < _qpivot_val) + _qleft++ + + while (_qsort_get_key(array, _qright, prop_path) > _qpivot_val) + _qright-- + + # swap + if (_qleft <= _qright) { + _qleft_val = array_get(array, _qleft) + _qright_val = array_get(array, _qright) + + array_set(array, _qleft, _qright_val) + array_set(array, _qright, _qleft_val) + + _qleft++ + _qright-- + } + } + + # sort the partitions + _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]] - while (array[_qright] > _qpivot_val) - _qright-- + return (_out) +} - # swap - if (_qleft <= _qright) { - _qleft_val = array[_qleft] - _qright_val = array[_qright] - - array[_qleft] = _qright_val - array[_qright] = _qleft_val +# +# 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 "'") +} - _qleft++ - _qright-- - } - } +# +# Parse an offset string, stripping any leading '+' or trailing ':' or ',' +# characters +# +# +0x0: +# 0x0, +# ... +# +function parse_uint_offset(str) { + # Drop any leading '+' + sub(/^\+/, "", str) - # sort the partitions - _qsort(array, first, _qright) - _qsort(array, _qleft, last) + # 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 @@ # # 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 @@ # # 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 @@ # # 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 @@ # 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 +# Push a new parser state. +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 +} - 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") - } +# Fetch the current parser state +function parser_state_get() { + if (_g_parse_stack_depth == 0) + errorx("parser_state_get() called with empty parse stack") - result[REV_START] = _rstart - result[REV_END] = _rend + return (_g_parse_stack[_g_parse_stack_depth]) } -# -# 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) +# 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("}") } -# -# 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 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; - - for (i = scope; i < depth; i++) { - if ((depth-i,name) in symbols) - return (depth-i) - } +# 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") - 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]) -} + 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") + } + } else if ($2 ~ "[1-9][0-9]*") { + _start = int($2) + _end = int($2) + } else { + error("invalid revision descriptor") + } -function is_defined (name, scope) -{ - return (_find_sym(name, scope) >= 0) + return (revrange_new(_start, _end)) } -# Define a new variable in the symbol table's current scope, -# with the given value -function push (name, value) -{ - symbols[depth,name] = value -} +# +# Parse a variable group block starting at the current line +# +# group "Group Name" { +# u8 var_name[10] { +# ... +# } +# ... +# } +# +function parse_variable_group(_ctx, _groups, _group, _group_name) { + _ctx = parser_state_get_context(NVRAM) -# 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 - } + # 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) } - # 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) + debug("group \"" get(_group_name, p_value) "\" {") - return (type == g(STATE_TYPE)) -} + # Register the new variable group + _groups = get(_ctx, p_var_groups) + _group = var_group_new(_group_name) + array_append(_groups, _group) -# 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) + # Push our variable group block + parser_state_open_block(_group) } -# 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 "'") -} +# +# 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) +{ + _ctx = parser_state_get_context(SymbolContext) -# struct definition -$1 == ST_STRUCT_BLOCK && allow_def($1) { - name = $2 + # Check for access modifier + if ($1 == "private") { + _vaccess = VAccessPrivate + shiftf(1) + } else if ($1 == "internal") { + _vaccess = VAccessInternal + shiftf(1) + } else { + _vaccess = VAccessPublic + } - # Remove array[] specifier - if (sub(/\[\]$/, "", name) == 0) - error("expected '" name "[]', not '" name "'") + # Find the base type + if ((_type = type_named($1)) == null) + error("unknown type '" $1 "'") - if (name !~ IDENT_REGEX || name ~ TYPES_REGEX) - error("invalid identifier '" name "'") + # Parse (and trim) any array specifier from the variable name + _name = $2 + _type = parse_array_type_specifier(_name, _type) + sub(ARRAY_REGEX"$", "", _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 + # 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)) + } - # Open the block - debug("struct " name " {") - open_block(ST_STRUCT_BLOCK, name) -} + # Construct new variable instance + _var = var_new(_vaccess, _name, _type) + debug((_private ? "private " : "") type_to_string(_type) " " _name " {") -# struct srom descriptor -$1 == ST_SROM_DEFN && allow_def(ST_SROM_DEFN) && in_state(ST_STRUCT_BLOCK) { - sid = g(STATE_IDENT) + # Register in global name table + _g_var_names[_name] = _var - # parse revision descriptor - rev_desc[REV_START] = 0 - parse_revdesc(rev_desc) + # Add to our parent context + _var_list = get(_ctx, p_vars) + array_append(_var_list, _var) - # assign revision id - rev = structs[sid,NUM_REVS] "" - revk = subkey(sid, REV, rev) - structs[sid,NUM_REVS]++ + # Push our variable definition block + parser_state_open_block(_var) +} - # 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") +# +# 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 = "" - 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) "") + _nfmts = array_size(_fmts) + for (_i = 0; _i < _nfmts; _i++) { + _fmt = array_get(_fmts, _i) + if (_i+1 == _nfmts) + _result = _result "or " - if (addrs[i] !~ HEX_REGEX) - error("invalid base address '" addrs[i] "'") + _result = _name_str \ + "'" get(_fmt, p_name) "'" - structs[offk,SEG_ADDR] = addrs[i] + if (_i+1 < _nfmts) + _result = _result ", " } - debug("struct_srom " structs[revk,REV_START] "... [" addrs_str "]") - next + obj_delete(_fmts) + return (_result) } -# close any previous srom revision descriptor -$1 == ST_SROM_DEFN && in_state(ST_SROM_DEFN) { - pop_state() -} +# +# 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) -# open a new srom revision descriptor -$1 == ST_SROM_DEFN && allow_def(ST_SROM_DEFN) { - # parse revision descriptor - parse_revdesc(rev_desc) + # 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)) + } - # assign revision id - vid = g(STATE_IDENT) - rev = vars[vid,NUM_REVS] "" - revk = subkey(vid, REV, rev) - vars[vid,NUM_REVS]++ + # Validate arguments + if (NF != 2) { + error("'" $1 "' requires a single parameter value of " \ + fmt_get_human_readable_list()) + } - # vend scoped rev/revk variables for use in the - # revision offset block - push("rev_id", rev) - push("rev_key", revk) + if ((_pval = fmt_named($2)) == null) { + error("'" $1 "' value '" $2 "' unrecognized. Must be " \ + "one of " fmt_get_human_readable_list()) + } - # 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 + # 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)) + } - debug("srom " rev_desc[REV_START] "-" rev_desc[REV_END] " {") - push_state(ST_SROM_DEFN, null, 0) + # 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 first offset definition - do { + # Seek to the start of the desc/help string shiftf(1) - } while ($1 !~ SROM_OFF_REGEX && NF > 0) + + # 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 { + error("unknown variable property type: '" param_name "'") + } } + # -# Extract and return the array length from the given type string. -# Returns -1 if the type is not an array. +# Parse a top-level SROM layout block starting at the current line # -function type_array_len (type) -{ - # extract byte count[] and width - if (match(type, ARRAY_REGEX"$") > 0) { - return (substr(type, RSTART+1, RLENGTH-2)) - } else { - return (-1) - } +# 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 an offset declaration from the current line. +# Parse a nested srom range filter block starting at the current line +# srom 4-7 { +# # Filter block +# srom 5 { +# 0x000: ... +# } +# } # -function parse_offset_segment (revk, offk) -{ - vid = g(STATE_IDENT) +function parse_srom_layout_filter(_parent, _revs, _filter) { + _parent = parser_state_get_context(SromLayout) - # 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 "'") + # Parse revision descriptor + _revs = parse_revrange() - shiftf(1) - } else { - type = vars[vid,VAR_TYPE] - } + # Construct the filter (which also validates the revision range) + _filter = srom_layout_filter_new(_parent, _revs) - # read offset value - offset = $1 - if (offset !~ HEX_REGEX) - error("invalid offset value '" offset "'") + debug("srom " revrange_to_string(_revs) " {") - # 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) - } else { - count = 1 - } - width = TSIZE[type] + # Push new SROM parser state + parser_state_open_block(_filter) +} - # seek to attributes or end of the offset expr + +# +# 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) - - # fetch rev id/key defined by our parent block - rev = g("rev_id") - revk = g("rev_key") +# +# 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 + + # 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 + + # 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) + } - # parse all offsets - do { - # assign offset id - off = vars[revk,REV_NUM_OFFS] "" - offk = subkey(revk, OFF, off) - vars[revk,REV_NUM_OFFS]++ + # 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 - # initialize segment count - vars[offk,DEF_LINE] = NR - vars[offk,OFF_NUM_SEGS] = 0 + if ((_type = parse_type_string(_type_str)) == null) + error("unknown type '" _type_str "'") + } - debug("[") - # parse all segments - do { - parse_offset_segment(revk, offk) - _more_seg = ($1 == "|") - if (_more_seg) - shiftf(1, 1) - } while (_more_seg) - debug("],") - _more_vals = ($1 == ",") - if (_more_vals) - shiftf(1, 1) - } while (_more_vals) + # Parse the trailing (... attributes ...), if any + return (parse_srom_segment_attributes(_offset, _type)) } -# variable definition -(($1 == "private" && $2 ~ TYPES_REGEX) || $1 ~ TYPES_REGEX) && - allow_def(ST_VAR_BLOCK) \ +# +# 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) { - # check for 'private' flag - if ($1 == "private") { - private = 1 - shiftf(1) - } else { - private = 0 + # 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 \": \"") } - 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]) + # 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") + } - 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 + # Create and register our new offset entry + _entry = srom_entry_new(_var, _srom_revs, _base_offset, _stype) + srom_layout_add_entry(_srom, _entry) - open_block(ST_VAR_BLOCK, name) + # Seek to either the block start ('{'), or the attributes to be + # used for a single offset/segment entry at `offset` + shiftf(3) - debug("type=" DTYPE[base_type]) + # 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) - if (in_nested_state(ST_STRUCT_BLOCK)) { - # Fetch the enclosing struct's name - sid = g(STATE_IDENT, 1) + # Parse and register simplified segment syntax + _seg = parse_srom_segment_attributes(_base_offset, _stype) + array_append(get(_offset, p_segments), _seg) - # Mark as a struct-based variable - vars[name,VAR_STRUCT] = sid + debug(sprintf("0x%03x: %s %s { %s }", _base_offset, + type_to_string(_stype), _name, segment_to_string(_seg))) } } -# 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 "'") +# +# 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) - 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 -} + _base_type = get(_entry, p_type) + _base_type = type_get_base(_base_type) -# Skip comments and blank lines -/^[ \t]*#/ || /^$/ { - next -} + # Parse all offsets + do { + # Create a SromOffset + _offset = srom_offset_new() + _segs = get(_offset, p_segments) -# Close blocks -/}/ && !in_state(ST_NONE) { - while (!in_state(ST_NONE) && $0 ~ "}") { - close_block(); - debug("}") - } - next -} + array_append(_offs, _offset) -# Report unbalanced '}' -/}/ && in_state(ST_NONE) { - error("extra '}'") -} + # Parse all segments + do { + _seg = parse_srom_segment(_base_off, _base_type) + array_append(_segs, _seg) -# Invalid variable type -$1 && allow_def(ST_VAR_BLOCK) { - error("unknown type '" $1 "'") -} + # Do more segments follow? + _more_seg = ($1 == "|") + if (_more_seg) + shiftf(1, 1) -# Generic parse failure -{ - error("unrecognized statement") + if (_more_seg) + debug(segment_to_string(_seg) " |") + else + debug(segment_to_string(_seg)) + } while (_more_seg) + + # Do more offsets follow? + _more_vals = ($1 == ",") + if (_more_vals) + shiftf(1, 1) + } while (_more_vals) } Index: sys/dev/bwn/bwn_mac.c =================================================================== --- sys/dev/bwn/bwn_mac.c +++ sys/dev/bwn/bwn_mac.c @@ -111,7 +111,7 @@ // 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 Index: sys/mips/broadcom/bcm_nvram_cfe.c =================================================================== --- /dev/null +++ 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); Index: sys/mips/broadcom/bcm_nvram_cfevar.h =================================================================== --- sys/mips/broadcom/bcm_nvram_cfevar.h +++ 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_ */ Index: sys/mips/broadcom/files.broadcom =================================================================== --- sys/mips/broadcom/files.broadcom +++ 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 Index: sys/modules/bhnd/Makefile =================================================================== --- sys/modules/bhnd/Makefile +++ sys/modules/bhnd/Makefile @@ -27,11 +27,21 @@ 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