diff options
| author | Landon J. Fuller <landonf@FreeBSD.org> | 2016-11-26 23:22:32 +0000 |
|---|---|---|
| committer | Landon J. Fuller <landonf@FreeBSD.org> | 2016-11-26 23:22:32 +0000 |
| commit | 77cb4d3e5016a2fd090d07ed3e01a199723641d9 (patch) | |
| tree | eefb722db9e959216f82ba0d0d320df9d68a9a4e /sys/dev/bhnd/nvram | |
| parent | b84ef73179e9018f9197ba5293c41459f3b468d3 (diff) | |
Notes
Diffstat (limited to 'sys/dev/bhnd/nvram')
40 files changed, 15289 insertions, 5308 deletions
diff --git a/sys/dev/bhnd/nvram/bhnd_nvram.c b/sys/dev/bhnd/nvram/bhnd_nvram.c deleted file mode 100644 index 78cad99f24e45..0000000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram.c +++ /dev/null @@ -1,189 +0,0 @@ -/*- - * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> - * 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 <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -/* - * BHND CFE NVRAM driver. - * - * Provides access to device NVRAM via CFE. - */ - -#include <sys/param.h> -#include <sys/kernel.h> -#include <sys/bus.h> -#include <sys/limits.h> -#include <sys/malloc.h> -#include <sys/module.h> -#include <sys/systm.h> - -#include <machine/bus.h> -#include <sys/rman.h> -#include <machine/resource.h> - -#include <dev/bhnd/bhnd.h> - -#include <dev/cfe/cfe_api.h> -#include <dev/cfe/cfe_error.h> -#include <dev/cfe/cfe_ioctl.h> - -#include "bhnd_nvram_if.h" - -#include "bhnd_nvramvar.h" - -/** - * Default bhnd_nvram driver implementation of DEVICE_PROBE(). - */ -int -bhnd_nvram_probe(device_t dev) -{ - device_set_desc(dev, "Broadcom NVRAM"); - - /* Refuse wildcard attachments */ - return (BUS_PROBE_NOWILDCARD); -} - -/** - * Call from subclass DEVICE_ATTACH() implementations to handle - * device attachment. - * - * @param dev BHND NVRAM device. - * @param data NVRAM data to be copied and parsed. No reference to data - * will be held after return. - * @param size Size of @p data, in bytes. - * @param fmt NVRAM format. - */ -int -bhnd_nvram_attach(device_t dev, void *data, size_t size, bhnd_nvram_format fmt) -{ - struct bhnd_nvram_softc *sc; - int error; - - sc = device_get_softc(dev); - sc->dev = dev; - - /* Initialize NVRAM parser */ - error = bhnd_nvram_parser_init(&sc->nvram, dev, data, size, fmt); - if (error) - return (error); - - /* Initialize mutex */ - BHND_NVRAM_LOCK_INIT(sc); - - return (0); -} - -/** - * Default bhnd_nvram driver implementation of DEVICE_RESUME(). - */ -int -bhnd_nvram_resume(device_t dev) -{ - return (0); -} - -/** - * Default bhnd_nvram driver implementation of DEVICE_SUSPEND(). - */ -int -bhnd_nvram_suspend(device_t dev) -{ - return (0); -} - -/** - * Default bhnd_nvram driver implementation of DEVICE_DETACH(). - */ -int -bhnd_nvram_detach(device_t dev) -{ - struct bhnd_nvram_softc *sc; - - sc = device_get_softc(dev); - - bhnd_nvram_parser_fini(&sc->nvram); - BHND_NVRAM_LOCK_DESTROY(sc); - - return (0); -} - -/** - * Default bhnd_nvram driver implementation of BHND_NVRAM_GETVAR(). - */ -static int -bhnd_nvram_getvar_method(device_t dev, const char *name, void *buf, size_t *len, - bhnd_nvram_type type) -{ - struct bhnd_nvram_softc *sc; - int error; - - sc = device_get_softc(dev); - - BHND_NVRAM_LOCK(sc); - error = bhnd_nvram_parser_getvar(&sc->nvram, name, buf, len, type); - BHND_NVRAM_UNLOCK(sc); - - return (error); -} - -/** - * Default bhnd_nvram driver implementation of BHND_NVRAM_SETVAR(). - */ -static int -bhnd_nvram_setvar_method(device_t dev, const char *name, const void *buf, - size_t len, bhnd_nvram_type type) -{ - struct bhnd_nvram_softc *sc; - int error; - - sc = device_get_softc(dev); - - BHND_NVRAM_LOCK(sc); - error = bhnd_nvram_parser_setvar(&sc->nvram, name, buf, len, type); - BHND_NVRAM_UNLOCK(sc); - - return (error); -} - -static device_method_t bhnd_nvram_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, bhnd_nvram_probe), - DEVMETHOD(device_resume, bhnd_nvram_resume), - DEVMETHOD(device_suspend, bhnd_nvram_suspend), - DEVMETHOD(device_detach, bhnd_nvram_detach), - - /* NVRAM interface */ - DEVMETHOD(bhnd_nvram_getvar, bhnd_nvram_getvar_method), - DEVMETHOD(bhnd_nvram_setvar, bhnd_nvram_setvar_method), - - DEVMETHOD_END -}; - -DEFINE_CLASS_0(bhnd_nvram, bhnd_nvram_driver, bhnd_nvram_methods, sizeof(struct bhnd_nvram_softc)); diff --git a/sys/dev/bhnd/nvram/bhnd_nvram.h b/sys/dev/bhnd/nvram/bhnd_nvram.h index 5d7da07173d9a..f21ec696b2c44 100644 --- a/sys/dev/bhnd/nvram/bhnd_nvram.h +++ b/sys/dev/bhnd/nvram/bhnd_nvram.h @@ -32,11 +32,17 @@ #ifndef _BHND_NVRAM_BHND_NVRAM_H_ #define _BHND_NVRAM_BHND_NVRAM_H_ +#ifdef _KERNEL +#include <sys/types.h> +#else /* !_KERNEL */ +#include <stdbool.h> +#include <stdint.h> +#endif /* _KERNEL */ + /** * NVRAM data sources supported by bhnd(4) devices. */ typedef enum { - BHND_NVRAM_SRC_OTP, /**< On-chip one-time-programmable * memory. */ @@ -67,50 +73,52 @@ typedef enum { */ } bhnd_nvram_src; -/** Supported NVRAM formats. */ +/** + * NVRAM data types. + * + * @internal + * + * All primitive (non-array) constants should be representable as a 4-bit + * integer (e.g. 0-15) to support SPROM_OPCODE_TYPE_IMM encoding as used by + * nvram_map_gen.awk. + */ typedef enum { - BHND_NVRAM_FMT_BCM = 0, /**< Broadcom NUL-delimited key=value pairs */ - BHND_NVRAM_FMT_TLV = 1, /**< CFE TLV encoding, as used on WGT634U */ - BHND_NVRAM_FMT_BTXT = 2, /**< Broadcom board text file. This is used - to provide external NVRAM data for some - fullmac WiFi devices. */ - BHND_NVRAM_FMT_SPROM = 3, /**< SPROM/OTP-specific encoding used by - Broadcom network adapters */ - BHND_NVRAM_FMT_CIS = 4, /**< A mostly CIS-compatible encoding used - on some Broadcom network adapters */ - BHND_NVRAM_FMT_UNKNOWN = 5 /**< Unknown or unrecognized format */ -} bhnd_nvram_format; - - -/** bhnd_nvram_type 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_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_cfe.c b/sys/dev/bhnd/nvram/bhnd_nvram_cfe.c deleted file mode 100644 index 766ccd6698cb1..0000000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram_cfe.c +++ /dev/null @@ -1,373 +0,0 @@ -/*- - * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> - * 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 <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -/* - * BHND CFE NVRAM driver. - * - * Provides access to device NVRAM via CFE. - */ - -#include <sys/param.h> -#include <sys/kernel.h> -#include <sys/bus.h> -#include <sys/limits.h> -#include <sys/malloc.h> -#include <sys/module.h> -#include <sys/systm.h> - -#include <machine/bus.h> -#include <sys/rman.h> -#include <machine/resource.h> - -#include <dev/bhnd/bhnd.h> - -#include <dev/cfe/cfe_api.h> -#include <dev/cfe/cfe_error.h> -#include <dev/cfe/cfe_ioctl.h> - -#include "bhnd_nvram_if.h" - -#include "bhnd_nvramvar.h" - -static int nvram_open_cfedev(device_t dev, char *devname, int fd, - int64_t *offset, uint32_t *size, bhnd_nvram_format fmt); -static char *nvram_find_cfedev(device_t dev, int *fd, int64_t *offset, - uint32_t *size, bhnd_nvram_format *fmt); - -/** Known CFE NVRAM device names, in probe order. */ -static char *nvram_cfe_devs[] = { - "nflash0.nvram", /* NAND */ - "nflash1.nvram", - "flash0.nvram", - "flash1.nvram", -}; - -/** Supported CFE NVRAM formats, in probe order. */ -bhnd_nvram_format nvram_cfe_fmts[] = { - BHND_NVRAM_FMT_BCM, - BHND_NVRAM_FMT_TLV -}; - - -static int -bhnd_nvram_cfe_probe(device_t dev) -{ - char *devname; - bhnd_nvram_format fmt; - int64_t offset; - uint32_t size; - int error; - int fd; - - /* Defer to default driver implementation */ - if ((error = bhnd_nvram_probe(dev)) > 0) - return (error); - - /* Locate a usable CFE device */ - devname = nvram_find_cfedev(dev, &fd, &offset, &size, &fmt); - if (devname == NULL) - return (ENXIO); - cfe_close(fd); - - switch (fmt) { - case BHND_NVRAM_FMT_BCM: - device_set_desc(dev, "Broadcom NVRAM"); - break; - case BHND_NVRAM_FMT_TLV: - device_set_desc(dev, "Broadcom WGT634U NVRAM"); - break; - default: - device_printf(dev, "unknown NVRAM format: %d\n", fmt); - return (ENXIO); - } - - /* Refuse wildcard attachments */ - return (BUS_PROBE_NOWILDCARD); -} - - -static int -bhnd_nvram_cfe_attach(device_t dev) -{ - char *devname; - unsigned char *buffer; - bhnd_nvram_format fmt; - int64_t offset; - uint32_t size; - int error; - int fd; - - error = 0; - buffer = NULL; - fd = CFE_ERR; - - /* Locate NVRAM device via CFE */ - devname = nvram_find_cfedev(dev, &fd, &offset, &size, &fmt); - if (devname == NULL) { - device_printf(dev, "CFE NVRAM device not found\n"); - return (ENXIO); - } - - /* Copy out NVRAM buffer */ - buffer = malloc(size, M_TEMP, M_NOWAIT); - if (buffer == NULL) - return (ENOMEM); - - for (size_t remain = size; remain > 0;) { - int nr, req; - - req = ulmin(INT_MAX, remain); - nr = cfe_readblk(fd, size-remain, buffer+(size-remain), - req); - if (nr < 0) { - device_printf(dev, "%s: cfe_readblk() failed: %d\n", - devname, fd); - - error = ENXIO; - goto cleanup; - } - - remain -= nr; - - if (nr == 0 && remain > 0) { - device_printf(dev, "%s: cfe_readblk() unexpected EOF: " - "%zu of %zu pending\n", devname, remain, size); - - error = ENXIO; - goto cleanup; - } - } - - device_printf(dev, "CFE %s (%#jx+%#jx)\n", devname, (uintmax_t)offset, - (uintmax_t)size); - - /* Delegate to default driver implementation */ - error = bhnd_nvram_attach(dev, buffer, size, fmt); - -cleanup: - if (buffer != NULL) - free(buffer, M_TEMP); - - if (fd >= 0) - cfe_close(fd); - - return (error); -} - -/** - * Identify and open a CFE NVRAM device. - * - * @param dev bhnd_nvram_cfe device. - * @param devname The name of the CFE device to be probed. - * @param fd An open CFE file descriptor for @p devname. - * @param[out] offset On success, the NVRAM data offset within @p @fd. - * @param[out] size On success, maximum the NVRAM data size within @p fd. - * @param fmt The expected NVRAM data format for this device. - * - * @retval 0 success - * @retval non-zero If probing @p devname fails, a regular unix - * error code will be returned. - */ -static int -nvram_open_cfedev(device_t dev, char *devname, int fd, int64_t *offset, - uint32_t *size, bhnd_nvram_format fmt) -{ - union bhnd_nvram_ident ident; - nvram_info_t nvram_info; - int cerr, devinfo, dtype, rlen; - int error; - - /* Try to fetch device info */ - if ((devinfo = cfe_getdevinfo(devname)) == CFE_ERR_DEVNOTFOUND) - return (ENODEV); - - if (devinfo < 0) { - device_printf(dev, "cfe_getdevinfo() failed: %d", - devinfo); - return (ENXIO); - } - - /* Verify device type */ - dtype = devinfo & CFE_DEV_MASK; - switch (dtype) { - case CFE_DEV_FLASH: - case CFE_DEV_NVRAM: - /* Valid device type */ - break; - default: - device_printf(dev, "%s: unknown device type %d\n", - devname, dtype); - return (ENXIO); - } - - /* Try to fetch nvram info from CFE */ - cerr = cfe_ioctl(fd, IOCTL_NVRAM_GETINFO, (unsigned char *)&nvram_info, - sizeof(nvram_info), &rlen, 0); - if (cerr != CFE_OK && cerr != CFE_ERR_INV_COMMAND) { - device_printf(dev, "%s: IOCTL_NVRAM_GETINFO failed: %d\n", - devname, cerr); - return (ENXIO); - } - - /* Fall back on flash info. - * - * This is known to be required on the Asus RT-N53 (CFE 5.70.55.33, - * BBP 1.0.37, BCM5358UB0), where IOCTL_NVRAM_GETINFO returns - * CFE_ERR_INV_COMMAND. - */ - if (cerr == CFE_ERR_INV_COMMAND) { - flash_info_t fi; - - cerr = cfe_ioctl(fd, IOCTL_FLASH_GETINFO, (unsigned char *)&fi, - sizeof(fi), &rlen, 0); - - if (cerr != CFE_OK) { - device_printf(dev, "%s: IOCTL_FLASH_GETINFO failed: " - "%d\n", devname, cerr); - return (ENXIO); - } - - nvram_info.nvram_eraseflg = - !(fi.flash_flags & FLASH_FLAG_NOERASE); - nvram_info.nvram_offset = 0x0; - nvram_info.nvram_size = fi.flash_size; - } - - /* Try to read NVRAM header/format identification */ - cerr = cfe_readblk(fd, 0, (unsigned char *)&ident, sizeof(ident)); - if (cerr < 0) { - device_printf(dev, "%s: cfe_readblk() failed: %d\n", - devname, cerr); - return (ENXIO); - } else if (cerr == 0) { - /* EOF */ - return (ENODEV); - } else if (cerr != sizeof(ident)) { - device_printf(dev, "%s: cfe_readblk() short read: %d\n", - devname, cerr); - return (ENXIO); - } - - /* Verify expected format */ - if ((error = bhnd_nvram_parser_identify(&ident, fmt))) - return (error); - - /* Provide offset and size */ - switch (fmt) { - case BHND_NVRAM_FMT_TLV: - /* No size field is available; must assume the NVRAM data - * consumes up to the full CFE NVRAM range */ - *offset = nvram_info.nvram_offset; - *size = nvram_info.nvram_size; - break; - case BHND_NVRAM_FMT_BCM: - if (ident.bcm.size > nvram_info.nvram_size) { - device_printf(dev, "%s: NVRAM size %#x overruns %#x " - "device limit\n", devname, ident.bcm.size, - nvram_info.nvram_size); - return (ENODEV); - } - - *offset = nvram_info.nvram_offset; - *size = ident.bcm.size; - break; - default: - return (EINVAL); - } - - return (0); -} - -/** - * Find (and open) a CFE NVRAM device. - * - * @param dev bhnd_nvram_cfe device. - * @param[out] fd On success, a valid CFE file descriptor. The callee - * is responsible for closing this file descriptor via - * cfe_close(). - * @param[out] offset On success, the NVRAM data offset within @p @fd. - * @param[out] size On success, maximum the NVRAM data size within @p fd. - * @param fmt The expected NVRAM data format for this device. - * - * @return On success, the opened CFE device's name will be returned. On - * error, returns NULL. - */ -static char * -nvram_find_cfedev(device_t dev, int *fd, int64_t *offset, - uint32_t *size, bhnd_nvram_format *fmt) -{ - char *devname; - int error; - - for (u_int i = 0; i < nitems(nvram_cfe_fmts); i++) { - *fmt = nvram_cfe_fmts[i]; - - for (u_int j = 0; j < nitems(nvram_cfe_devs); j++) { - devname = nvram_cfe_devs[j]; - - /* Open for reading */ - *fd = cfe_open(devname); - if (*fd == CFE_ERR_DEVNOTFOUND) { - continue; - } else if (*fd < 0) { - device_printf(dev, "%s: cfe_open() failed: " - "%d\n", devname, *fd); - continue; - } - - /* Probe */ - error = nvram_open_cfedev(dev, devname, *fd, offset, - size, *fmt); - if (error == 0) - return (devname); - - /* Keep searching */ - devname = NULL; - cfe_close(*fd); - } - } - - return (NULL); -} - -static device_method_t bhnd_nvram_cfe_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, bhnd_nvram_cfe_probe), - DEVMETHOD(device_attach, bhnd_nvram_cfe_attach), - - DEVMETHOD_END -}; - -DEFINE_CLASS_1(bhnd_nvram, bhnd_nvram_cfe, bhnd_nvram_cfe_methods, - sizeof(struct bhnd_nvram_softc), bhnd_nvram_driver); -EARLY_DRIVER_MODULE(bhnd_nvram_cfe, nexus, bhnd_nvram_cfe, - bhnd_nvram_devclass, NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_EARLY); diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_common.c b/sys/dev/bhnd/nvram/bhnd_nvram_common.c deleted file mode 100644 index 16249b4f04d07..0000000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram_common.c +++ /dev/null @@ -1,661 +0,0 @@ -/*- - * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> - * 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 <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/ctype.h> -#include <sys/hash.h> - -#include <sys/param.h> -#include <sys/limits.h> -#include <sys/malloc.h> -#include <sys/kernel.h> -#include <sys/systm.h> - -#include <machine/_inttypes.h> - -#include "bhnd_nvram_common.h" - -#include "bhnd_nvram_map_data.h" - -/* - * Common NVRAM/SPROM support, including NVRAM variable map - * lookup. - */ - -MALLOC_DEFINE(M_BHND_NVRAM, "bhnd_nvram", "bhnd nvram data"); - -/* - * CRC-8 lookup table used to checksum SPROM and NVRAM data via - * bhnd_nvram_crc8(). - * - * Generated with following parameters: - * polynomial: CRC-8 (x^8 + x^7 + x^6 + x^4 + x^2 + 1) - * reflected bits: false - * reversed: true - */ -const uint8_t bhnd_nvram_crc8_tab[] = { - 0x00, 0xf7, 0xb9, 0x4e, 0x25, 0xd2, 0x9c, 0x6b, 0x4a, 0xbd, 0xf3, - 0x04, 0x6f, 0x98, 0xd6, 0x21, 0x94, 0x63, 0x2d, 0xda, 0xb1, 0x46, - 0x08, 0xff, 0xde, 0x29, 0x67, 0x90, 0xfb, 0x0c, 0x42, 0xb5, 0x7f, - 0x88, 0xc6, 0x31, 0x5a, 0xad, 0xe3, 0x14, 0x35, 0xc2, 0x8c, 0x7b, - 0x10, 0xe7, 0xa9, 0x5e, 0xeb, 0x1c, 0x52, 0xa5, 0xce, 0x39, 0x77, - 0x80, 0xa1, 0x56, 0x18, 0xef, 0x84, 0x73, 0x3d, 0xca, 0xfe, 0x09, - 0x47, 0xb0, 0xdb, 0x2c, 0x62, 0x95, 0xb4, 0x43, 0x0d, 0xfa, 0x91, - 0x66, 0x28, 0xdf, 0x6a, 0x9d, 0xd3, 0x24, 0x4f, 0xb8, 0xf6, 0x01, - 0x20, 0xd7, 0x99, 0x6e, 0x05, 0xf2, 0xbc, 0x4b, 0x81, 0x76, 0x38, - 0xcf, 0xa4, 0x53, 0x1d, 0xea, 0xcb, 0x3c, 0x72, 0x85, 0xee, 0x19, - 0x57, 0xa0, 0x15, 0xe2, 0xac, 0x5b, 0x30, 0xc7, 0x89, 0x7e, 0x5f, - 0xa8, 0xe6, 0x11, 0x7a, 0x8d, 0xc3, 0x34, 0xab, 0x5c, 0x12, 0xe5, - 0x8e, 0x79, 0x37, 0xc0, 0xe1, 0x16, 0x58, 0xaf, 0xc4, 0x33, 0x7d, - 0x8a, 0x3f, 0xc8, 0x86, 0x71, 0x1a, 0xed, 0xa3, 0x54, 0x75, 0x82, - 0xcc, 0x3b, 0x50, 0xa7, 0xe9, 0x1e, 0xd4, 0x23, 0x6d, 0x9a, 0xf1, - 0x06, 0x48, 0xbf, 0x9e, 0x69, 0x27, 0xd0, 0xbb, 0x4c, 0x02, 0xf5, - 0x40, 0xb7, 0xf9, 0x0e, 0x65, 0x92, 0xdc, 0x2b, 0x0a, 0xfd, 0xb3, - 0x44, 0x2f, 0xd8, 0x96, 0x61, 0x55, 0xa2, 0xec, 0x1b, 0x70, 0x87, - 0xc9, 0x3e, 0x1f, 0xe8, 0xa6, 0x51, 0x3a, 0xcd, 0x83, 0x74, 0xc1, - 0x36, 0x78, 0x8f, 0xe4, 0x13, 0x5d, 0xaa, 0x8b, 0x7c, 0x32, 0xc5, - 0xae, 0x59, 0x17, 0xe0, 0x2a, 0xdd, 0x93, 0x64, 0x0f, 0xf8, 0xb6, - 0x41, 0x60, 0x97, 0xd9, 0x2e, 0x45, 0xb2, 0xfc, 0x0b, 0xbe, 0x49, - 0x07, 0xf0, 0x9b, 0x6c, 0x22, 0xd5, 0xf4, 0x03, 0x4d, 0xba, 0xd1, - 0x26, 0x68, 0x9f -}; - -/** - * Return the size of type @p type, or 0 if @p type has a variable width - * (e.g. a C string). - * - * @param type NVRAM data type. - * @result the byte width of @p type. - */ -size_t -bhnd_nvram_type_width(bhnd_nvram_type type) -{ - switch (type) { - case BHND_NVRAM_TYPE_INT8: - case BHND_NVRAM_TYPE_UINT8: - case BHND_NVRAM_TYPE_CHAR: - return (sizeof(uint8_t)); - - case BHND_NVRAM_TYPE_INT16: - case BHND_NVRAM_TYPE_UINT16: - return (sizeof(uint16_t)); - - case BHND_NVRAM_TYPE_INT32: - case BHND_NVRAM_TYPE_UINT32: - return (sizeof(uint32_t)); - - case BHND_NVRAM_TYPE_CSTR: - return (0); - } - - /* Quiesce gcc4.2 */ - panic("bhnd nvram type %u unknown", type); -} - -/** - * Return the format string to use when printing @p type with @p sfmt - * - * @param type The value type being printed. - * @param sfmt The string format required for @p type. - * @param elem_num The element index being printed. If this is the first - * value in an array of elements, the index would be 0, the next would be 1, - * and so on. - * - * @retval non-NULL A valid printf format string. - * @retval NULL If no format string is available for @p type and @p sfmt. - */ -const char * -bhnd_nvram_type_fmt(bhnd_nvram_type type, bhnd_nvram_sfmt sfmt, - size_t elem_num) -{ - size_t width; - - width = bhnd_nvram_type_width(type); - - /* Sanity-check the type width */ - switch (width) { - case 1: - case 2: - case 4: - break; - default: - return (NULL); - } - - /* Special-cased string formats */ - switch (sfmt) { - case BHND_NVRAM_SFMT_LEDDC: - /* If this is the first element, use the 0x-prefixed - * SFMT_HEX */ - if (elem_num == 0) - sfmt = BHND_NVRAM_SFMT_HEX; - break; - default: - break; - } - - /* Return the format string */ - switch (sfmt) { - case BHND_NVRAM_SFMT_MACADDR: - switch (width) { - case 1: return ("%02" PRIx8); - } - break; - - case BHND_NVRAM_SFMT_HEX: - switch (width) { - case 1: return ("0x%02" PRIx8); - case 2: return ("0x%04" PRIx16); - case 4: return ("0x%08" PRIx32); - } - break; - case BHND_NVRAM_SFMT_DEC: - if (BHND_NVRAM_SIGNED_TYPE(type)) { - switch (width) { - case 1: return ("%" PRId8); - case 2: return ("%" PRId16); - case 4: return ("%" PRId32); - } - } else { - switch (width) { - case 1: return ("%" PRIu8); - case 2: return ("%" PRIu16); - case 4: return ("%" PRIu32); - } - } - break; - case BHND_NVRAM_SFMT_LEDDC: - switch (width) { - case 1: return ("%02" PRIx8); - case 2: return ("%04" PRIx16); - case 4: return ("%08" PRIx32); - } - break; - - case BHND_NVRAM_SFMT_CCODE: - switch (width) { - case 1: return ("%c"); - } - break; - } - - return (NULL); -} - -/** - * Find and return the variable definition for @p varname, if any. - * - * @param varname variable name - * - * @retval bhnd_nvram_vardefn If a valid definition for @p varname is found. - * @retval NULL If no definition for @p varname is found. - */ -const struct bhnd_nvram_vardefn * -bhnd_nvram_find_vardefn(const char *varname) -{ - size_t min, mid, max; - int order; - - /* - * Locate the requested variable using a binary search. - * - * The variable table is guaranteed to be sorted in lexicographical - * order (using the 'C' locale for collation rules) - */ - min = 0; - mid = 0; - max = nitems(bhnd_nvram_vardefs) - 1; - - while (max >= min) { - /* Select midpoint */ - mid = (min + max) / 2; - - /* Determine which side of the partition to search */ - order = strcmp(bhnd_nvram_vardefs[mid].name, varname); - if (order < 0) { - /* Search upper partition */ - min = mid + 1; - } else if (order > 0) { - /* Search lower partition */ - max = mid - 1; - } else if (order == 0) { - /* Match found */ - return (&bhnd_nvram_vardefs[mid]); - } - } - - /* Not found */ - return (NULL); -} - -/** - * Validate an NVRAM variable name. - * - * Scans for special characters (path delimiters, value delimiters, path - * alias prefixes), returning false if the given name cannot be used - * as a relative NVRAM key. - * - * @param name A relative NVRAM variable name to validate. - * @param name_len The length of @p name, in bytes. - * - * @retval true If @p name is a valid relative NVRAM key. - * @retval false If @p name should not be used as a relative NVRAM key. - */ -bool -bhnd_nvram_validate_name(const char *name, size_t name_len) -{ - size_t limit; - - limit = strnlen(name, name_len); - if (limit == 0) - return (false); - - /* Disallow path alias prefixes ([0-9]+:.*) */ - if (limit >= 2 && isdigit(*name)) { - for (const char *p = name; p - name < limit; p++) { - if (isdigit(*p)) - continue; - else if (*p == ':') - return (false); - else - break; - } - } - - /* Scan for special characters */ - for (const char *p = name; p - name < limit; p++) { - switch (*p) { - case '/': /* path delimiter */ - case '=': /* key=value delimiter */ - return (false); - - default: - if (isspace(*p) || !isascii(*p)) - return (false); - } - } - - return (true); -} - -/** - * Parse an octet string, such as a MAC address, consisting of hex octets - * separated with ':' or '-'. - * - * @param value The octet string to parse. - * @param value_len The length of @p value, in bytes. - * @param buf The output buffer to which parsed octets will be written. May be - * NULL. - * @param[in,out] len The capacity of @p buf. On success, will be set - * to the actual size of the requested value. - * @param type - */ -int -bhnd_nvram_parse_octet_string(const char *value, size_t value_len, void *buf, - size_t *len, bhnd_nvram_type type) -{ - size_t limit, nbytes, width; - size_t slen; - uint8_t octet; - char delim; - - slen = strnlen(value, value_len); - - nbytes = 0; - if (buf != NULL) - limit = *len; - else - limit = 0; - - /* Type must have a fixed width */ - if ((width = bhnd_nvram_type_width(type)) == 0) - return (EINVAL); - - /* String length (not including NUL) must be aligned on an octet - * boundary ('AA:BB', not 'AA:B', etc), and must be large enough - * to contain at least two octet entries. */ - if (slen % 3 != 2 || slen < sizeof("AA:BB") - 1) - return (EINVAL); - - /* Identify the delimiter used. The standard delimiter for - * MAC addresses is ':', but some earlier NVRAM formats may use - * '-' */ - switch ((delim = value[2])) { - case ':': - case '-': - break; - default: - return (EINVAL); - } - - /* Parse octets */ - for (const char *p = value; p - value < value_len; p++) { - void *outp; - size_t pos; - unsigned char c; - - pos = (p - value); - - /* Skip delimiter after each octet */ - if (pos % 3 == 2) { - if (*p == delim) - continue; - - if (*p == '\0') - return (0); - - /* No delimiter? */ - return (EINVAL); - } - - c = *(const unsigned char *)p; - - if (isdigit(c)) - c -= '0'; - else if (isxdigit(c)) - c -= islower(c) ? 'a' - 10 : 'A' - 10; - else - return (EINVAL); - - if (pos % 3 == 0) { - /* MSB */ - octet = (c << 4); - continue; - } else if (pos % 3 == 1) { - /* LSB */ - octet |= (c & 0xF); - } - - /* Skip writing? */ - if (limit < width || limit - width < nbytes) { - nbytes += width; - continue; - } - - /* Write output */ - outp = ((uint8_t *)buf) + nbytes; - switch (type) { - case BHND_NVRAM_TYPE_UINT8: - *(uint8_t *)outp = octet; - break; - - case BHND_NVRAM_TYPE_UINT16: - *(uint16_t *)outp = octet; - break; - - case BHND_NVRAM_TYPE_UINT32: - *(uint32_t *)outp = octet; - break; - - case BHND_NVRAM_TYPE_INT8: - if (octet > INT8_MAX) - return (ERANGE); - *(int8_t *)outp = (int8_t)octet; - break; - - case BHND_NVRAM_TYPE_INT16: - *(int16_t *)outp = (int8_t)octet; - break; - - case BHND_NVRAM_TYPE_INT32: - *(int32_t *)outp = (int8_t)octet; - break; - - case BHND_NVRAM_TYPE_CHAR: -#if (CHAR_MAX < UINT8_MAX) - if (octet > CHAR_MAX) - return (ERANGE); -#endif - *(char *)outp = (char)octet; - break; - default: - printf("unknown type %d\n", type); - return (EINVAL); - } - - nbytes += width; - } - - return (0); -} - - -/** - * Initialize a new variable hash table with @p nelements. - * - * @param map Hash table instance to be initialized. - * @param nelements The number of hash table buckets to allocate. - * @param flags Hash table flags (HASH_*). - */ -int -bhnd_nvram_varmap_init(struct bhnd_nvram_varmap *map, size_t nelements, - int flags) -{ - map->table = hashinit_flags(nelements, M_BHND_NVRAM, &map->mask, - flags); - if (map->table == NULL) - return (ENOMEM); - - return (0); -} - -/** - * Deallocate all resources associated with @p map. - * - * @param map Hash table to be deallocated. - */ -void -bhnd_nvram_varmap_free(struct bhnd_nvram_varmap *map) -{ - struct bhnd_nvram_tuple *t, *tnext; - - /* Free all elements */ - for (size_t i = 0; i <= map->mask; i++) { - LIST_FOREACH_SAFE(t, &map->table[i], t_link, tnext) { - LIST_REMOVE(t, t_link); - bhnd_nvram_tuple_free(t); - } - } - - /* Free hash table */ - hashdestroy(map->table, M_BHND_NVRAM, map->mask); -} - -/** - * Add a variable entry to @p map. - * - * @param map Hash table to modify. - * @param name Variable name. - * @param value Variable value. - * @param value_len The length of @p value, in bytes. - * - * @retval 0 success - * @retval ENOMEM unable to allocate new entry - */ -int -bhnd_nvram_varmap_add(struct bhnd_nvram_varmap *map, const char *name, - const char *value, size_t value_len) -{ - struct bhnd_nvram_tuples *head; - struct bhnd_nvram_tuple *t; - - /* Locate target bucket */ - head = &map->table[hash32_str(name, HASHINIT) & map->mask]; - - /* Allocate new entry */ - if ((t = bhnd_nvram_tuple_alloc(name, value)) == NULL) - return (ENOMEM); - - /* Remove any existing entry */ - bhnd_nvram_varmap_remove(map, name); - - /* Insert new entry */ - LIST_INSERT_HEAD(head, t, t_link); - return (0); -} - -/** - * Remove @p map in @p tuples, if it exists. - * - * @param map Hash table to modify. - * @param key Key to remove. - * - * @retval 0 success - * @retval ENOENT If @p name is not found in @p map. - */ -int -bhnd_nvram_varmap_remove(struct bhnd_nvram_varmap *map, const char *name) -{ - struct bhnd_nvram_tuples *head; - struct bhnd_nvram_tuple *t; - size_t name_len; - - /* Locate target bucket */ - head = &map->table[hash32_str(name, HASHINIT) & map->mask]; - name_len = strlen(name); - - LIST_FOREACH(t, head, t_link) { - if (t->name_len != name_len) - continue; - - if (strncmp(t->name, name, name_len) != 0) - continue; - - LIST_REMOVE(t, t_link); - bhnd_nvram_tuple_free(t); - return (0); - } - - /* Not found */ - return (ENOENT); -} - -/** - * Search for @p name in @p map. - * - * @param map Hash table to modify. - * @param name Variable name. - * @param name_len Length of @p name, not including trailing NUL. - * - * @retval bhnd_nvram_tuple If @p name is found in @p map. - * @retval NULL If @p name is not found. - */ -struct bhnd_nvram_tuple * -bhnd_nvram_varmap_find(struct bhnd_nvram_varmap *map, const char *name, - size_t name_len) -{ - struct bhnd_nvram_tuples *head; - struct bhnd_nvram_tuple *t; - - head = &map->table[hash32_str(name, HASHINIT) & map->mask]; - - LIST_FOREACH(t, head, t_link) { - if (t->name_len != name_len) - continue; - - if (strncmp(t->name, name, name_len) != 0) - continue; - - /* Match */ - return (t); - } - - /* not found */ - return (NULL); -} - -/** - * Check for @p name in @p map. - * - * @param map Hash table to modify. - * @param name Variable name. - * @param name_len Length of @p name, not including trailing NUL. - * - * @retval true If @p name is found in @p tuples. - * @retval false If @p name is not found. - */ -bool bhnd_nvram_varmap_contains(struct bhnd_nvram_varmap *map, - const char *name, size_t name_len) -{ - return (bhnd_nvram_varmap_find(map, name, name_len) != NULL); -} - -/** - * Allocate a new tuple with @p name and @p value. - * - * @param name Variable name. - * @param value Variable value. - * - * @retval bhnd_nvram_tuple success. - * @retval NULL if allocation fails. - */ -struct bhnd_nvram_tuple * -bhnd_nvram_tuple_alloc(const char *name, const char *value) -{ - struct bhnd_nvram_tuple *t; - - t = malloc(sizeof(*t), M_BHND_NVRAM, M_NOWAIT); - if (t == NULL) - return (NULL); - - t->name_len = strlen(name); - t->name = malloc(t->name_len+1, M_BHND_NVRAM, M_NOWAIT); - - t->value_len = strlen(value); - t->value = malloc(t->value_len+1, M_BHND_NVRAM, M_NOWAIT); - - if (t->name == NULL || t->value == NULL) - goto failed; - - strcpy(t->name, name); - strcpy(t->value, value); - - return (t); - -failed: - if (t->name != NULL) - free(t->name, M_BHND_NVRAM); - - if (t->value != NULL) - free(t->value, M_BHND_NVRAM); - - free(t, M_BHND_NVRAM); - - return (NULL); -} - -void -bhnd_nvram_tuple_free(struct bhnd_nvram_tuple *tuple) -{ - free(tuple->name, M_BHND_NVRAM); - free(tuple->value, M_BHND_NVRAM); - free(tuple, M_BHND_NVRAM); -} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_common.h b/sys/dev/bhnd/nvram/bhnd_nvram_common.h deleted file mode 100644 index 3d25b2b3419f3..0000000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram_common.h +++ /dev/null @@ -1,180 +0,0 @@ -/*- - * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> - * 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 <sys/param.h> -#include <sys/malloc.h> - -#include "bhnd_nvram.h" - -struct bhnd_nvram_tuple; -struct bhnd_nvram_varmap; - -struct bhnd_nvram_vardefn; - -MALLOC_DECLARE(M_BHND_NVRAM); - -extern const uint8_t bhnd_nvram_crc8_tab[]; - -#define BHND_NVRAM_CRC8_INITIAL 0xFF /**< Initial bhnd_nvram_crc8 value */ -#define BHND_NVRAM_CRC8_VALID 0x9F /**< Valid CRC-8 checksum */ -#define BHND_SPROMREV_MAX UINT8_MAX /**< maximum supported SPROM revision */ - - -/** NVRAM data type string representations */ -typedef enum { - BHND_NVRAM_SFMT_HEX = 1, /**< hex format */ - BHND_NVRAM_SFMT_DEC = 2, /**< decimal format */ - BHND_NVRAM_SFMT_MACADDR = 3, /**< mac address (canonical form, hex octets, - separated with ':') */ - BHND_NVRAM_SFMT_LEDDC = 4, /**< LED PWM duty-cycle (2 bytes -- on/off) */ - BHND_NVRAM_SFMT_CCODE = 5 /**< count code format (2-3 ASCII chars, or hex string) */ -} bhnd_nvram_sfmt; - -size_t bhnd_nvram_type_width(bhnd_nvram_type type); -const char *bhnd_nvram_type_fmt(bhnd_nvram_type type, - bhnd_nvram_sfmt sfmt, size_t elem_num); - -const struct bhnd_nvram_vardefn *bhnd_nvram_find_vardefn(const char *varname); - -bool bhnd_nvram_validate_name(const char *name, - size_t name_len); -int bhnd_nvram_parse_octet_string( - const char *value, size_t value_len, - void *buf, size_t *len, - bhnd_nvram_type type); - -int bhnd_nvram_varmap_init( - struct bhnd_nvram_varmap *map, - size_t nelements, int flags); -void bhnd_nvram_varmap_free( - struct bhnd_nvram_varmap *map); -int bhnd_nvram_varmap_add( - struct bhnd_nvram_varmap *map, - const char *name, const char *value, - size_t value_len); -int bhnd_nvram_varmap_remove( - struct bhnd_nvram_varmap *map, - const char *name); -struct bhnd_nvram_tuple *bhnd_nvram_varmap_find( - struct bhnd_nvram_varmap *map, - const char *name, size_t name_len); -bool bhnd_nvram_varmap_contains( - struct bhnd_nvram_varmap *map, - const char *name, size_t name_len); - -struct bhnd_nvram_tuple *bhnd_nvram_tuple_alloc(const char *name, - const char *value); -void bhnd_nvram_tuple_free( - struct bhnd_nvram_tuple *tuple); - -/** NVRAM variable flags */ -enum { - BHND_NVRAM_VF_ARRAY = (1<<0), /**< variable is an array */ - BHND_NVRAM_VF_MFGINT = (1<<1), /**< mfg-internal variable; should not be externally visible */ - BHND_NVRAM_VF_IGNALL1 = (1<<2) /**< hide variable if its value has all bits set. */ -}; - -/** SPROM revision compatibility declaration */ -struct bhnd_sprom_compat { - uint8_t first; /**< first compatible SPROM revision */ - uint8_t last; /**< last compatible SPROM revision, or BHND_SPROMREV_MAX */ -}; - -/** SPROM value descriptor */ -struct bhnd_sprom_offset { - uint16_t offset; /**< byte offset within SPROM */ - bool cont:1; /**< value should be bitwise OR'd with the - * previous offset descriptor */ - bhnd_nvram_type type:7; /**< data type */ - int8_t shift; /**< shift to be applied to the value */ - uint32_t mask; /**< mask to be applied to the value(s) */ -}; - -/** SPROM-specific variable definition */ -struct bhnd_sprom_vardefn { - struct bhnd_sprom_compat compat; /**< sprom compatibility declaration */ - const struct bhnd_sprom_offset *offsets; /**< offset descriptors */ - size_t num_offsets; /**< number of offset descriptors */ -}; - -/** NVRAM variable definition */ -struct bhnd_nvram_vardefn { - const char *name; /**< variable name */ - bhnd_nvram_type type; /**< base data type */ - bhnd_nvram_sfmt sfmt; /**< string format */ - uint32_t flags; /**< BHND_NVRAM_VF_* flags */ - - const struct bhnd_sprom_vardefn *sp_defs; /**< SPROM-specific variable definitions */ - size_t num_sp_defs; /**< number of sprom definitions */ -}; - -/** - * NVRAM value tuple. - */ -struct bhnd_nvram_tuple { - char *name; /**< variable name. */ - size_t name_len; /**< variable length. */ - char *value; /**< value, or NULL if this tuple represents variable - deletion */ - size_t value_len; /**< value length. */ - - LIST_ENTRY(bhnd_nvram_tuple) t_link; -}; - -LIST_HEAD(bhnd_nvram_tuples, bhnd_nvram_tuple); - -/** NVRAM tuple hash table */ -struct bhnd_nvram_varmap { - struct bhnd_nvram_tuples *table; /**< hash buckets */ - u_long mask; /**< hash index mask */ -}; - -/** - * Calculate CRC-8 over @p buf. - * - * @param buf input buffer - * @param size buffer size - * @param crc last computed crc, or BHND_NVRAM_CRC8_INITIAL - */ -static inline uint8_t -bhnd_nvram_crc8(const void *buf, size_t size, uint8_t crc) -{ - const uint8_t *p = (const uint8_t *)buf; - while (size--) - crc = bhnd_nvram_crc8_tab[(crc ^ *p++)]; - - return (crc); -} - -#endif /* _BHND_NVRAM_BHND_NVRAM_COMMON_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data.c b/sys/dev/bhnd/nvram/bhnd_nvram_data.c new file mode 100644 index 0000000000000..87300857bb8b4 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data.c @@ -0,0 +1,527 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + + +#ifdef _KERNEL + +#include <sys/param.h> +#include <sys/systm.h> + +#include <machine/_inttypes.h> + +#else /* !_KERNEL */ + +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" +#include "bhnd_nvram_io.h" + +#include "bhnd_nvram_datavar.h" +#include "bhnd_nvram_data.h" + +/** + * Return a human-readable description for the given NVRAM data class. + * + * @param cls The NVRAM class. + */ +const char * +bhnd_nvram_data_class_desc(bhnd_nvram_data_class_t *cls) +{ + return (cls->desc); +} + +/** + * Probe to see if this NVRAM data class class supports the data mapped by the + * given I/O context, returning a BHND_NVRAM_DATA_PROBE probe result. + * + * @param cls The NVRAM class. + * @param io An I/O context mapping the NVRAM data. + * + * @retval 0 if this is the only possible NVRAM data class for @p io. + * @retval negative if the probe succeeds, a negative value should be returned; + * the class returning the highest negative value should be selected to handle + * NVRAM parsing. + * @retval ENXIO If the NVRAM format is not handled by @p cls. + * @retval positive if an error occurs during probing, a regular unix error + * code should be returned. + */ +int +bhnd_nvram_data_probe(bhnd_nvram_data_class_t *cls, struct bhnd_nvram_io *io) +{ + return (cls->op_probe(io)); +} + +/** + * Probe to see if an NVRAM data class in @p classes supports parsing + * of the data mapped by @p io, returning the parsed data in @p data. + * + * The caller is responsible for deallocating the returned instance via + * bhnd_nvram_data_release(). + * + * @param[out] data On success, the parsed NVRAM data instance. + * @param io An I/O context mapping the NVRAM data to be copied and parsed. + * @param classes An array of NVRAM data classes to be probed, or NULL to + * probe the default supported set. + * @param num_classes The number of NVRAM data classes in @p classes. + * + * @retval 0 success + * @retval ENXIO if no class is found capable of parsing @p io. + * @retval non-zero if an error otherwise occurs during allocation, + * initialization, or parsing of the NVRAM data, a regular unix error code + * will be returned. + */ +int +bhnd_nvram_data_probe_classes(struct bhnd_nvram_data **data, + struct bhnd_nvram_io *io, bhnd_nvram_data_class_t *classes[], + size_t num_classes) +{ + bhnd_nvram_data_class_t *cls; + int error, prio, result; + + cls = NULL; + prio = 0; + *data = NULL; + + /* If class array is NULL, default to our linker set */ + if (classes == NULL) { + classes = SET_BEGIN(bhnd_nvram_data_class_set); + num_classes = SET_COUNT(bhnd_nvram_data_class_set); + } + + /* Try to find the best data class capable of parsing io */ + for (size_t i = 0; i < num_classes; i++) { + bhnd_nvram_data_class_t *next_cls; + + next_cls = classes[i]; + + /* Try to probe */ + result = bhnd_nvram_data_probe(next_cls, io); + + /* The parser did not match if an error was returned */ + if (result > 0) + continue; + + /* Lower priority than previous match; keep + * searching */ + if (cls != NULL && result <= prio) + continue; + + /* Drop any previously parsed data */ + if (*data != NULL) { + bhnd_nvram_data_release(*data); + *data = NULL; + } + + /* If this is a 'maybe' match, attempt actual parsing to + * verify that this does in fact match */ + if (result <= BHND_NVRAM_DATA_PROBE_MAYBE) { + /* If parsing fails, keep searching */ + error = bhnd_nvram_data_new(next_cls, data, io); + if (error) + continue; + } + + /* Record best new match */ + prio = result; + cls = next_cls; + + + /* Terminate search immediately on + * BHND_NVRAM_DATA_PROBE_SPECIFIC */ + if (result == BHND_NVRAM_DATA_PROBE_SPECIFIC) + break; + } + + /* If no match, return error */ + if (cls == NULL) + return (ENXIO); + + /* If the NVRAM data was not parsed above, do so now */ + if (*data == NULL) { + if ((error = bhnd_nvram_data_new(cls, data, io))) + return (error); + } + + return (0); +} + +/** + * Allocate and initialize a new instance of data class @p cls, copying and + * parsing NVRAM data from @p io. + * + * The caller is responsible for releasing the returned parser instance + * reference via bhnd_nvram_data_release(). + * + * @param cls If non-NULL, the data class to be allocated. If NULL, + * bhnd_nvram_data_probe_classes() will be used to determine the data format. + * @param[out] nv On success, a pointer to the newly allocated NVRAM data instance. + * @param io An I/O context mapping the NVRAM data to be copied and parsed. + * + * @retval 0 success + * @retval non-zero if an error occurs during allocation or initialization, a + * regular unix error code will be returned. + */ +int +bhnd_nvram_data_new(bhnd_nvram_data_class_t *cls, + struct bhnd_nvram_data **nv, struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_data *data; + int error; + + /* If NULL, try to identify the appropriate class */ + if (cls == NULL) + return (bhnd_nvram_data_probe_classes(nv, io, NULL, 0)); + + /* Allocate new instance */ + BHND_NV_ASSERT(sizeof(struct bhnd_nvram_data) <= cls->size, + ("instance size %zu less than minimum %zu", cls->size, + sizeof(struct bhnd_nvram_data))); + + data = bhnd_nv_calloc(1, cls->size); + data->cls = cls; + refcount_init(&data->refs, 1); + + /* Let the class handle initialization */ + if ((error = cls->op_new(data, io))) { + bhnd_nv_free(data); + return (error); + } + + *nv = data; + return (0); +} + +/** + * Retain and return a reference to the given data instance. + * + * @param nv The reference to be retained. + */ +struct bhnd_nvram_data * +bhnd_nvram_data_retain(struct bhnd_nvram_data *nv) +{ + refcount_acquire(&nv->refs); + return (nv); +} + +/** + * Release a reference to the given data instance. + * + * If this is the last reference, the data instance and its associated + * resources will be freed. + * + * @param nv The reference to be released. + */ +void +bhnd_nvram_data_release(struct bhnd_nvram_data *nv) +{ + if (!refcount_release(&nv->refs)) + return; + + /* Free any internal resources */ + nv->cls->op_free(nv); + + /* Free the instance allocation */ + bhnd_nv_free(nv); +} + +/** + * Return a pointer to @p nv's data class. + * + * @param nv The NVRAM data instance to be queried. + */ +bhnd_nvram_data_class_t * +bhnd_nvram_data_class(struct bhnd_nvram_data *nv) +{ + return (nv->cls); +} + +/** + * Return the number of variables in @p nv. + * + * @param nv The NVRAM data to be queried. + */ +size_t +bhnd_nvram_data_count(struct bhnd_nvram_data *nv) +{ + return (nv->cls->op_count(nv)); +} + +/** + * Compute the size of the serialized form of @p nv. + * + * Serialization may be performed via bhnd_nvram_data_serialize(). + * + * @param nv The NVRAM data to be queried. + * @param[out] len On success, will be set to the computed size. + * + * @retval 0 success + * @retval non-zero if computing the serialized size otherwise fails, a + * regular unix error code will be returned. + */ +int +bhnd_nvram_data_size(struct bhnd_nvram_data *nv, size_t *len) +{ + return (nv->cls->op_size(nv, len)); +} + +/** + * Serialize the NVRAM data to @p buf, using the NVRAM data class' native + * format. + * + * The resulting serialization may be reparsed with @p nv's BHND NVRAM data + * class. + * + * @param nv The NVRAM data to be serialized. + * @param[out] buf On success, the serialed NVRAM data will be + * written to this buffer. This argment may be + * NULL if the value is not desired. + * @param[in,out] len The capacity of @p buf. On success, will be set + * to the actual length of the serialized data. + * + * @retval 0 success + * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too + * small to hold the serialized data. + * @retval non-zero If serialization otherwise fails, a regular unix error + * code will be returned. + */ +int +bhnd_nvram_data_serialize(struct bhnd_nvram_data *nv, + void *buf, size_t *len) +{ + return (nv->cls->op_serialize(nv, buf, len)); +} + +/** + * Return the capability flags (@see BHND_NVRAM_DATA_CAP_*) for @p nv. + * + * @param nv The NVRAM data to be queried. + */ +uint32_t +bhnd_nvram_data_caps(struct bhnd_nvram_data *nv) +{ + return (nv->cls->op_caps(nv)); +} + +/** + * Iterate over @p nv, returning the names of subsequent variables. + * + * @param nv The NVRAM data to be iterated. + * @param[in,out] cookiep A pointer to a cookiep value previously returned + * by bhnd_nvram_data_next(), or a NULL value to + * begin iteration. + * + * @return Returns the next variable name, or NULL if there are no more + * variables defined in @p nv. + */ +const char * +bhnd_nvram_data_next(struct bhnd_nvram_data *nv, void **cookiep) +{ + return (nv->cls->op_next(nv, cookiep)); +} + +/** + * Search @p nv for a named variable, returning the variable's opaque reference + * if found, or NULL if unavailable. + * + * The BHND_NVRAM_DATA_CAP_INDEXED capability flag will be returned by + * bhnd_nvram_data_caps() if @p nv supports effecient name-based + * lookups. + * + * @param nv The NVRAM data to search. + * @param name The name to search for. + * + * @retval non-NULL If @p name is found, the opaque cookie value will be + * returned. + * @retval NULL If @p name is not found. + */ +void * +bhnd_nvram_data_find(struct bhnd_nvram_data *nv, const char *name) +{ + return (nv->cls->op_find(nv, name)); +} + +/** + * A generic implementation of bhnd_nvram_data_find(). + * + * This implementation will use bhnd_nvram_data_next() to perform a + * simple O(n) case-insensitve search for @p name. + */ +void * +bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv, const char *name) +{ + const char *next; + void *cookiep; + + cookiep = NULL; + while ((next = bhnd_nvram_data_next(nv, &cookiep))) { + if (strcasecmp(name, next) == 0) + return (cookiep); + } + + /* Not found */ + return (NULL); +} + +/** + * Read a variable and decode as @p type. + * + * @param nv The NVRAM data. + * @param cookiep An NVRAM variable cookie previously returned + * via bhnd_nvram_data_next() or + * bhnd_nvram_data_find(). + * @param[out] buf On success, the requested value will be written + * to this buffer. This argment may be NULL if + * the value is not desired. + * @param[in,out] len The capacity of @p buf. On success, will be set + * to the actual size of the requested value. + * @param type The data type to be written to @p buf. + * + * @retval 0 success + * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too + * small to hold the requested value. + * @retval EFTYPE If the variable data cannot be coerced to @p type. + * @retval ERANGE If value coercion would overflow @p type. + */ +int +bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, + size_t *len, bhnd_nvram_type type) +{ + return (nv->cls->op_getvar(nv, cookiep, buf, len, type)); +} + +/** + * A generic implementation of bhnd_nvram_data_getvar(). + * + * This implementation will call bhnd_nvram_data_getvar_ptr() to fetch + * a pointer to the variable data and perform data coercion on behalf + * of the caller. + * + * If a variable definition for the requested variable is available via + * bhnd_nvram_find_vardefn(), the definition will be used to provide + * formatting hints to bhnd_nvram_coerce_value(). + */ +int +bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, void *cookiep, + void *outp, size_t *olen, bhnd_nvram_type otype) +{ + bhnd_nvram_val_t val; + const struct bhnd_nvram_vardefn *vdefn; + const bhnd_nvram_val_fmt_t *fmt; + const char *name; + const void *vptr; + bhnd_nvram_type vtype; + size_t vlen; + int error; + + BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR, + ("instance does not advertise READ_PTR support")); + + /* Fetch pointer to our variable data */ + vptr = bhnd_nvram_data_getvar_ptr(nv, cookiep, &vlen, &vtype); + if (vptr == NULL) + return (EINVAL); + + /* Use the NVRAM string support */ + switch (vtype) { + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_STRING_ARRAY: + fmt = &bhnd_nvram_val_bcm_string_fmt; + break; + default: + fmt = NULL; + } + + /* Check the variable definition table for a matching entry; if + * it exists, use it to populate the value format. */ + name = bhnd_nvram_data_getvar_name(nv, cookiep); + vdefn = bhnd_nvram_find_vardefn(name); + if (vdefn != NULL) + fmt = vdefn->fmt; + + /* Attempt value coercion */ + error = bhnd_nvram_val_init(&val, fmt, vptr, vlen, vtype, + BHND_NVRAM_VAL_BORROW_DATA); + if (error) + return (error); + + error = bhnd_nvram_val_encode(&val, outp, olen, otype); + + /* Clean up */ + bhnd_nvram_val_release(&val); + return (error); +} + +/** + * If available and supported by the NVRAM data instance, return a reference + * to the internal buffer containing an entry's variable data, + * + * Note that string values may not be NUL terminated. + * + * @param nv The NVRAM data. + * @param cookiep An NVRAM variable cookie previously returned + * via bhnd_nvram_data_next() or + * bhnd_nvram_data_find(). + * @param[out] len On success, will be set to the actual size of + * the requested value. + * @param[out] type The data type of the entry data. + * + * @retval non-NULL success + * @retval NULL if direct data access is unsupported by @p nv, or + * unavailable for @p cookiep. + */ +const void * +bhnd_nvram_data_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type) +{ + return (nv->cls->op_getvar_ptr(nv, cookiep, len, type)); +} + + +/** + * Return the variable name associated with a given @p cookiep. + * @param nv The NVRAM data to be iterated. + * @param[in,out] cookiep A pointer to a cookiep value previously returned + * via bhnd_nvram_data_next() or + * bhnd_nvram_data_find(). + * + * @return Returns the variable's name. + */ +const char * +bhnd_nvram_data_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) +{ + return (nv->cls->op_getvar_name(nv, cookiep)); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data.h b/sys/dev/bhnd/nvram/bhnd_nvram_data.h new file mode 100644 index 0000000000000..ed8098f81c040 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data.h @@ -0,0 +1,137 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/param.h> +#include <sys/bus.h> +#else /* !_KERNEL */ +#include <errno.h> + +#include <stdint.h> +#include <stdlib.h> +#endif /* _KERNEL */ + +#include "bhnd_nvram.h" +#include "bhnd_nvram_io.h" + +/* NVRAM data class */ +typedef struct bhnd_nvram_data_class bhnd_nvram_data_class_t; + +/* NVRAM data instance */ +struct bhnd_nvram_data; + +/** Declare a bhnd_nvram_data_class with name @p _n */ +#define BHND_NVRAM_DATA_CLASS_DECL(_n) \ + extern struct bhnd_nvram_data_class bhnd_nvram_ ## _n ## _class + +BHND_NVRAM_DATA_CLASS_DECL(bcm); +BHND_NVRAM_DATA_CLASS_DECL(bcmraw); +BHND_NVRAM_DATA_CLASS_DECL(tlv); +BHND_NVRAM_DATA_CLASS_DECL(btxt); +BHND_NVRAM_DATA_CLASS_DECL(sprom); + +/** bhnd_nvram_data capabilities */ +enum { + /** Supports efficient lookup of variables by name */ + BHND_NVRAM_DATA_CAP_INDEXED = (1<<0), + + /** Supports direct access to backing buffer */ + BHND_NVRAM_DATA_CAP_READ_PTR = (1<<1), + + /** Supports device path prefixed variables */ + BHND_NVRAM_DATA_CAP_DEVPATHS = (1<<2), +}; + +/** + * A standard set of probe priorities returned by bhnd_nvram_data_probe(). + * + * Priority is defined in ascending order, with 0 being the highest priority. + * Return values greater than zero are interpreted as regular unix error codes. + */ +enum { + BHND_NVRAM_DATA_PROBE_MAYBE = -40, /**< Possible match */ + BHND_NVRAM_DATA_PROBE_DEFAULT = -20, /**< Definite match of a base + OS-supplied data class */ + BHND_NVRAM_DATA_PROBE_SPECIFIC = 0, /**< Terminate search and use + this data class for + parsing */ +}; + +const char *bhnd_nvram_data_class_desc( + bhnd_nvram_data_class_t *cls); + +int bhnd_nvram_data_probe(bhnd_nvram_data_class_t *cls, + struct bhnd_nvram_io *io); +int bhnd_nvram_data_probe_classes( + struct bhnd_nvram_data **data, + struct bhnd_nvram_io *io, + bhnd_nvram_data_class_t *classes[], + size_t num_classes); + +int bhnd_nvram_data_new(bhnd_nvram_data_class_t *cls, + struct bhnd_nvram_data **nv, + struct bhnd_nvram_io *io); + +struct bhnd_nvram_data *bhnd_nvram_data_retain(struct bhnd_nvram_data *nv); +void bhnd_nvram_data_release(struct bhnd_nvram_data *nv); + +bhnd_nvram_data_class_t *bhnd_nvram_data_class(struct bhnd_nvram_data *nv); + +size_t bhnd_nvram_data_count(struct bhnd_nvram_data *nv); + +int bhnd_nvram_data_size(struct bhnd_nvram_data *nv, + size_t *size); + +int bhnd_nvram_data_serialize(struct bhnd_nvram_data *nv, + void *buf, size_t *len); + +uint32_t bhnd_nvram_data_caps(struct bhnd_nvram_data *nv); + +const char *bhnd_nvram_data_next(struct bhnd_nvram_data *nv, + void **cookiep); + +void *bhnd_nvram_data_find(struct bhnd_nvram_data *nv, + const char *name); + +int bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv, + void *cookiep, void *buf, size_t *len, + bhnd_nvram_type type); + +const void *bhnd_nvram_data_getvar_ptr(struct bhnd_nvram_data *nv, + void *cookiep, size_t *len, bhnd_nvram_type *type); + +const char *bhnd_nvram_data_getvar_name(struct bhnd_nvram_data *nv, + void *cookiep); + +#endif /* _BHND_NVRAM_BHND_NVRAM_DATA_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c new file mode 100644 index 0000000000000..79cf8bb2aecfd --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c @@ -0,0 +1,748 @@ +/*- + * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/endian.h> + +#ifdef _KERNEL + +#include <sys/bus.h> +#include <sys/ctype.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#else /* !_KERNEL */ + +#include <ctype.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_datavar.h" + +#include "bhnd_nvram_data_bcmreg.h" +#include "bhnd_nvram_data_bcmvar.h" + +/* + * Broadcom NVRAM data class. + * + * The Broadcom NVRAM NUL-delimited ASCII format is used by most + * Broadcom SoCs. + * + * The NVRAM data is encoded as a standard header, followed by series of + * NUL-terminated 'key=value' strings; the end of the stream is denoted + * by a single extra NUL character. + */ + +struct bhnd_nvram_bcm; + +static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_gethdrvar( + struct bhnd_nvram_bcm *bcm, + const char *name); +static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_to_hdrvar( + struct bhnd_nvram_bcm *bcm, + void *cookiep); +static size_t bhnd_nvram_bcm_hdrvar_index( + struct bhnd_nvram_bcm *bcm, + struct bhnd_nvram_bcm_hvar *hvar); +/* + * Set of BCM NVRAM header values that are required to be mirrored in the + * NVRAM data itself. + * + * If they're not included in the parsed NVRAM data, we need to vend the + * header-parsed values with their appropriate keys, and add them in any + * updates to the NVRAM data. + * + * If they're modified in NVRAM, we need to sync the changes with the + * the NVRAM header values. + */ +static const struct bhnd_nvram_bcm_hvar bhnd_nvram_bcm_hvars[] = { + { + .name = BCM_NVRAM_CFG0_SDRAM_INIT_VAR, + .type = BHND_NVRAM_TYPE_UINT16, + .len = sizeof(uint16_t), + .nelem = 1, + }, + { + .name = BCM_NVRAM_CFG1_SDRAM_CFG_VAR, + .type = BHND_NVRAM_TYPE_UINT16, + .len = sizeof(uint16_t), + .nelem = 1, + }, + { + .name = BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR, + .type = BHND_NVRAM_TYPE_UINT16, + .len = sizeof(uint16_t), + .nelem = 1, + }, + { + .name = BCM_NVRAM_SDRAM_NCDL_VAR, + .type = BHND_NVRAM_TYPE_UINT32, + .len = sizeof(uint32_t), + .nelem = 1, + }, +}; + +/** BCM NVRAM data class instance */ +struct bhnd_nvram_bcm { + struct bhnd_nvram_data nv; /**< common instance state */ + struct bhnd_nvram_io *data; /**< backing buffer */ + + /** BCM header values */ + struct bhnd_nvram_bcm_hvar hvars[nitems(bhnd_nvram_bcm_hvars)]; + + size_t count; /**< total variable count */ +}; + +BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", sizeof(struct bhnd_nvram_bcm)) + +static int +bhnd_nvram_bcm_probe(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_bcmhdr hdr; + int error; + + if ((error = bhnd_nvram_io_read(io, 0x0, &hdr, sizeof(hdr)))) + return (error); + + if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC) + return (ENXIO); + + return (BHND_NVRAM_DATA_PROBE_DEFAULT); +} + +/** + * Initialize @p bcm with the provided NVRAM data mapped by @p src. + * + * @param bcm A newly allocated data instance. + */ +static int +bhnd_nvram_bcm_init(struct bhnd_nvram_bcm *bcm, struct bhnd_nvram_io *src) +{ + struct bhnd_nvram_bcmhdr hdr; + uint8_t *p; + void *ptr; + size_t io_offset, io_size; + uint8_t crc, valid; + int error; + + if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr)))) + return (error); + + if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC) + return (ENXIO); + + /* Fetch the actual NVRAM image size */ + io_size = le32toh(hdr.size); + if (io_size < sizeof(hdr)) { + /* The header size must include the header itself */ + BHND_NV_LOG("corrupt header size: %zu\n", io_size); + return (EINVAL); + } + + if (io_size > bhnd_nvram_io_getsize(src)) { + BHND_NV_LOG("header size %zu exceeds input size %zu\n", + io_size, bhnd_nvram_io_getsize(src)); + return (EINVAL); + } + + /* Allocate a buffer large enough to hold the NVRAM image, and + * an extra EOF-signaling NUL (on the chance it's missing from the + * source data) */ + if (io_size == SIZE_MAX) + return (ENOMEM); + + bcm->data = bhnd_nvram_iobuf_empty(io_size, io_size + 1); + if (bcm->data == NULL) + return (ENOMEM); + + /* Fetch a pointer into our backing buffer and copy in the + * NVRAM image. */ + error = bhnd_nvram_io_write_ptr(bcm->data, 0x0, &ptr, io_size, NULL); + if (error) + return (error); + + p = ptr; + if ((error = bhnd_nvram_io_read(src, 0x0, p, io_size))) + return (error); + + /* Verify the CRC */ + valid = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC); + crc = bhnd_nvram_crc8(p + BCM_NVRAM_CRC_SKIP, + io_size - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL); + + if (crc != valid) { + BHND_NV_LOG("warning: NVRAM CRC error (crc=%#hhx, " + "expected=%hhx)\n", crc, valid); + } + + /* Populate header variable definitions */ +#define BCM_READ_HDR_VAR(_name, _dest, _swap) do { \ + struct bhnd_nvram_bcm_hvar *data; \ + data = bhnd_nvram_bcm_gethdrvar(bcm, _name ##_VAR); \ + BHND_NV_ASSERT(data != NULL, \ + ("no such header variable: " __STRING(_name))); \ + \ + \ + data->value. _dest = _swap(BCM_NVRAM_GET_BITS( \ + hdr. _name ## _FIELD, _name)); \ +} while(0) + + BCM_READ_HDR_VAR(BCM_NVRAM_CFG0_SDRAM_INIT, u16, le16toh); + BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_CFG, u16, le16toh); + BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_REFRESH, u16, le16toh); + BCM_READ_HDR_VAR(BCM_NVRAM_SDRAM_NCDL, u32, le32toh); + + _Static_assert(nitems(bcm->hvars) == 4, "missing initialization for" + "NVRAM header variable(s)"); + +#undef BCM_READ_HDR_VAR + + /* Process the buffer */ + bcm->count = 0; + io_offset = sizeof(hdr); + while (io_offset < io_size) { + char *envp; + const char *name, *value; + size_t envp_len; + size_t name_len, value_len; + + /* Parse the key=value string */ + envp = (char *) (p + io_offset); + envp_len = strnlen(envp, io_size - io_offset); + error = bhnd_nvram_parse_env(envp, envp_len, '=', &name, + &name_len, &value, &value_len); + if (error) { + BHND_NV_LOG("error parsing envp at offset %#zx: %d\n", + io_offset, error); + return (error); + } + + /* Insert a '\0' character, replacing the '=' delimiter and + * allowing us to vend references directly to the variable + * name */ + *(envp + name_len) = '\0'; + + /* Record any NVRAM variables that mirror our header variables. + * This is a brute-force search -- for the amount of data we're + * operating on, it shouldn't be an issue. */ + for (size_t i = 0; i < nitems(bcm->hvars); i++) { + struct bhnd_nvram_bcm_hvar *hvar; + union bhnd_nvram_bcm_hvar_value hval; + size_t hval_len; + + hvar = &bcm->hvars[i]; + + /* Already matched? */ + if (hvar->envp != NULL) + continue; + + /* Name matches? */ + if ((strcmp(name, hvar->name)) != 0) + continue; + + /* Save pointer to mirrored envp */ + hvar->envp = envp; + + /* Check for stale value */ + hval_len = sizeof(hval); + error = bhnd_nvram_value_coerce(value, value_len, + BHND_NVRAM_TYPE_STRING, &hval, &hval_len, + hvar->type); + if (error) { + /* If parsing fails, we can likely only make + * things worse by trying to synchronize the + * variables */ + BHND_NV_LOG("error parsing header variable " + "'%s=%s': %d\n", name, value, error); + } else if (hval_len != hvar->len) { + hvar->stale = true; + } else if (memcmp(&hval, &hvar->value, hval_len) != 0) { + hvar->stale = true; + } + } + + /* Seek past the value's terminating '\0' */ + io_offset += envp_len; + if (io_offset == io_size) { + BHND_NV_LOG("missing terminating NUL at offset %#zx\n", + io_offset); + return (EINVAL); + } + + if (*(p + io_offset) != '\0') { + BHND_NV_LOG("invalid terminator '%#hhx' at offset " + "%#zx\n", *(p + io_offset), io_offset); + return (EINVAL); + } + + /* Update variable count */ + bcm->count++; + + /* Seek to the next record */ + if (++io_offset == io_size) { + char ch; + + /* Hit EOF without finding a terminating NUL + * byte; we need to grow our buffer and append + * it */ + io_size++; + if ((error = bhnd_nvram_io_setsize(bcm->data, io_size))) + return (error); + + /* Write NUL byte */ + ch = '\0'; + error = bhnd_nvram_io_write(bcm->data, io_size-1, &ch, + sizeof(ch)); + if (error) + return (error); + } + + /* Check for explicit EOF (encoded as a single empty NUL + * terminated string) */ + if (*(p + io_offset) == '\0') + break; + } + + /* Add non-mirrored header variables to total count variable */ + for (size_t i = 0; i < nitems(bcm->hvars); i++) { + if (bcm->hvars[i].envp == NULL) + bcm->count++; + } + + return (0); +} + +static int +bhnd_nvram_bcm_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_bcm *bcm; + int error; + + bcm = (struct bhnd_nvram_bcm *)nv; + + /* Populate default BCM mirrored header variable set */ + _Static_assert(sizeof(bcm->hvars) == sizeof(bhnd_nvram_bcm_hvars), + "hvar declarations must match bhnd_nvram_bcm_hvars template"); + memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars)); + + /* Parse the BCM input data and initialize our backing + * data representation */ + if ((error = bhnd_nvram_bcm_init(bcm, io))) { + bhnd_nvram_bcm_free(nv); + return (error); + } + + return (0); +} + +static void +bhnd_nvram_bcm_free(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv; + + if (bcm->data != NULL) + bhnd_nvram_io_free(bcm->data); +} + +size_t +bhnd_nvram_bcm_count(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv; + return (bcm->count); +} + +static int +bhnd_nvram_bcm_size(struct bhnd_nvram_data *nv, size_t *size) +{ + return (bhnd_nvram_bcm_serialize(nv, NULL, size)); +} + +static int +bhnd_nvram_bcm_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len) +{ + struct bhnd_nvram_bcm *bcm; + struct bhnd_nvram_bcmhdr hdr; + void *cookiep; + const char *name; + size_t nbytes, limit; + uint8_t crc; + int error; + + bcm = (struct bhnd_nvram_bcm *)nv; + nbytes = 0; + + /* Save the output buffer limit */ + if (buf == NULL) + limit = 0; + else + limit = *len; + + /* Reserve space for the NVRAM header */ + nbytes += sizeof(struct bhnd_nvram_bcmhdr); + + /* Write all variables to the output buffer */ + cookiep = NULL; + while ((name = bhnd_nvram_data_next(nv, &cookiep))) { + uint8_t *outp; + size_t olen; + size_t name_len, val_len; + + if (limit > nbytes) { + outp = (uint8_t *)buf + nbytes; + olen = limit - nbytes; + } else { + outp = NULL; + olen = 0; + } + + /* Determine length of variable name */ + name_len = strlen(name) + 1; + + /* Write the variable name and '=' delimiter */ + if (olen >= name_len) { + /* Copy name */ + memcpy(outp, name, name_len - 1); + + /* Append '=' */ + *(outp + name_len - 1) = '='; + } + + /* Adjust byte counts */ + if (SIZE_MAX - name_len < nbytes) + return (ERANGE); + + nbytes += name_len; + + /* Reposition output */ + if (limit > nbytes) { + outp = (uint8_t *)buf + nbytes; + olen = limit - nbytes; + } else { + outp = NULL; + olen = 0; + } + + /* Coerce to NUL-terminated C string, writing to the output + * buffer (or just calculating the length if outp is NULL) */ + val_len = olen; + error = bhnd_nvram_data_getvar(nv, cookiep, outp, &val_len, + BHND_NVRAM_TYPE_STRING); + + if (error && error != ENOMEM) + return (error); + + /* Adjust byte counts */ + if (SIZE_MAX - val_len < nbytes) + return (ERANGE); + + nbytes += val_len; + } + + /* Write terminating NUL */ + if (nbytes < limit) + *((uint8_t *)buf + nbytes) = '\0'; + nbytes++; + + /* Provide actual size */ + *len = nbytes; + if (buf == NULL || nbytes > limit) { + if (buf != NULL) + return (ENOMEM); + + return (0); + } + + /* Fetch current NVRAM header */ + if ((error = bhnd_nvram_io_read(bcm->data, 0x0, &hdr, sizeof(hdr)))) + return (error); + + /* Update values covered by CRC and write to output buffer */ + hdr.size = htole32(*len); + memcpy(buf, &hdr, sizeof(hdr)); + + /* Calculate new CRC */ + crc = bhnd_nvram_crc8((uint8_t *)buf + BCM_NVRAM_CRC_SKIP, + *len - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL); + + /* Update header with valid CRC */ + hdr.cfg0 &= ~BCM_NVRAM_CFG0_CRC_MASK; + hdr.cfg0 |= (crc << BCM_NVRAM_CFG0_CRC_SHIFT); + memcpy(buf, &hdr, sizeof(hdr)); + + return (0); +} + +static uint32_t +bhnd_nvram_bcm_caps(struct bhnd_nvram_data *nv) +{ + return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); +} + +static const char * +bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep) +{ + struct bhnd_nvram_bcm *bcm; + struct bhnd_nvram_bcm_hvar *hvar, *hvar_next; + const void *ptr; + const char *envp, *basep; + size_t io_size, io_offset; + int error; + + bcm = (struct bhnd_nvram_bcm *)nv; + + io_offset = sizeof(struct bhnd_nvram_bcmhdr); + io_size = bhnd_nvram_io_getsize(bcm->data) - io_offset; + + /* Map backing buffer */ + error = bhnd_nvram_io_read_ptr(bcm->data, io_offset, &ptr, io_size, + NULL); + if (error) { + BHND_NV_LOG("error mapping backing buffer: %d\n", error); + return (NULL); + } + + basep = ptr; + + /* If cookiep pointers into our header variable array, handle as header + * variable iteration. */ + hvar = bhnd_nvram_bcm_to_hdrvar(bcm, *cookiep); + if (hvar != NULL) { + size_t idx; + + /* Advance to next entry, if any */ + idx = bhnd_nvram_bcm_hdrvar_index(bcm, hvar) + 1; + + /* Find the next header-defined variable that isn't defined in + * the NVRAM data, start iteration there */ + for (size_t i = idx; i < nitems(bcm->hvars); i++) { + hvar_next = &bcm->hvars[i]; + if (hvar_next->envp != NULL && !hvar_next->stale) + continue; + + *cookiep = hvar_next; + return (hvar_next->name); + } + + /* No further header-defined variables; iteration + * complete */ + return (NULL); + } + + /* Handle standard NVRAM data iteration */ + if (*cookiep == NULL) { + /* Start at the first NVRAM data record */ + envp = basep; + } else { + /* Seek to next record */ + envp = *cookiep; + envp += strlen(envp) + 1; /* key + '\0' */ + envp += strlen(envp) + 1; /* value + '\0' */ + } + + /* + * Skip entries that have an existing header variable entry that takes + * precedence over the NVRAM data value. + * + * The header's value will be provided when performing header variable + * iteration + */ + while ((size_t)(envp - basep) < io_size && *envp != '\0') { + /* Locate corresponding header variable */ + hvar = NULL; + for (size_t i = 0; i < nitems(bcm->hvars); i++) { + if (bcm->hvars[i].envp != envp) + continue; + + hvar = &bcm->hvars[i]; + break; + } + + /* If no corresponding hvar entry, or the entry does not take + * precedence over this NVRAM value, we can safely return this + * value as-is. */ + if (hvar == NULL || !hvar->stale) + break; + + /* Seek to next record */ + envp += strlen(envp) + 1; /* key + '\0' */ + envp += strlen(envp) + 1; /* value + '\0' */ + } + + /* On NVRAM data EOF, try switching to header variables */ + if ((size_t)(envp - basep) == io_size || *envp == '\0') { + /* Find first valid header variable */ + for (size_t i = 0; i < nitems(bcm->hvars); i++) { + if (bcm->hvars[i].envp != NULL) + continue; + + *cookiep = &bcm->hvars[i]; + return (bcm->hvars[i].name); + } + + /* No header variables */ + return (NULL); + } + + *cookiep = (void *)(uintptr_t)envp; + return (envp); +} + +static void * +bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name) +{ + return (bhnd_nvram_data_generic_find(nv, name)); +} + +static int +bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, + size_t *len, bhnd_nvram_type type) +{ + return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); +} + +static const void * +bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type) +{ + struct bhnd_nvram_bcm *bcm; + struct bhnd_nvram_bcm_hvar *hvar; + const char *envp; + + bcm = (struct bhnd_nvram_bcm *)nv; + + /* Handle header variables */ + if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) { + BHND_NV_ASSERT( + hvar->len % bhnd_nvram_value_size(hvar->type, NULL, 0, + hvar->nelem) == 0, + ("length is not aligned to type width")); + + *type = hvar->type; + *len = hvar->len; + return (&hvar->value); + } + + /* Cookie points to key\0value\0 -- get the value address */ + BHND_NV_ASSERT(cookiep != NULL, ("NULL cookiep")); + + envp = cookiep; + envp += strlen(envp) + 1; /* key + '\0' */ + *len = strlen(envp) + 1; /* value + '\0' */ + *type = BHND_NVRAM_TYPE_STRING; + + return (envp); +} + +static const char * +bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) +{ + struct bhnd_nvram_bcm *bcm; + struct bhnd_nvram_bcm_hvar *hvar; + + bcm = (struct bhnd_nvram_bcm *)nv; + + /* Handle header variables */ + if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) { + return (hvar->name); + } + + /* Cookie points to key\0value\0 */ + return (cookiep); +} + +/** + * Return the internal BCM data reference for a header-defined variable + * with @p name, or NULL if none exists. + */ +static struct bhnd_nvram_bcm_hvar * +bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm *bcm, const char *name) +{ + for (size_t i = 0; i < nitems(bcm->hvars); i++) { + if (strcmp(bcm->hvars[i].name, name) == 0) + return (&bcm->hvars[i]); + } + + /* Not found */ + return (NULL); +} + +/** + * If @p cookiep references a header-defined variable, return the + * internal BCM data reference. Otherwise, returns NULL. + */ +static struct bhnd_nvram_bcm_hvar * +bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm *bcm, void *cookiep) +{ +#ifdef BHND_NVRAM_INVARIANTS + uintptr_t base, ptr; +#endif + + /* If the cookie falls within the hvar array, it's a + * header variable cookie */ + if (nitems(bcm->hvars) == 0) + return (NULL); + + if (cookiep < (void *)&bcm->hvars[0]) + return (NULL); + + if (cookiep > (void *)&bcm->hvars[nitems(bcm->hvars)-1]) + return (NULL); + +#ifdef BHND_NVRAM_INVARIANTS + base = (uintptr_t)bcm->hvars; + ptr = (uintptr_t)cookiep; + + BHND_NV_ASSERT((ptr - base) % sizeof(bcm->hvars[0]) == 0, + ("misaligned hvar pointer %p/%p", cookiep, bcm->hvars)); +#endif /* INVARIANTS */ + + return ((struct bhnd_nvram_bcm_hvar *)cookiep); +} + +/** + * Return the index of @p hdrvar within @p bcm's backing hvars array. + */ +static size_t +bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm *bcm, + struct bhnd_nvram_bcm_hvar *hdrvar) +{ + BHND_NV_ASSERT(bhnd_nvram_bcm_to_hdrvar(bcm, (void *)hdrvar) != NULL, + ("%p is not a valid hdrvar reference", hdrvar)); + + return (hdrvar - &bcm->hvars[0]); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c new file mode 100644 index 0000000000000..f673fa19f2e9a --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c @@ -0,0 +1,380 @@ +/*- + * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#ifdef _KERNEL + +#include <sys/param.h> +#include <sys/ctype.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#else /* !_KERNEL */ + +#include <ctype.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_datavar.h" + +/* + * Broadcom-RAW NVRAM data class. + * + * The Broadcom NVRAM NUL-delimited ASCII format is used by most + * Broadcom SoCs. + * + * The NVRAM data is encoded as a stream of of NUL-terminated 'key=value' + * strings; the end of the stream is denoted by a single extra NUL character. + */ + +struct bhnd_nvram_bcmraw; + +/** BCM-RAW NVRAM data class instance */ +struct bhnd_nvram_bcmraw { + struct bhnd_nvram_data nv; /**< common instance state */ + char *data; /**< backing buffer */ + size_t size; /**< buffer size */ + size_t count; /**< variable count */ +}; + +BHND_NVRAM_DATA_CLASS_DEFN(bcmraw, "Broadcom (RAW)", + sizeof(struct bhnd_nvram_bcmraw)) + +static int +bhnd_nvram_bcmraw_probe(struct bhnd_nvram_io *io) +{ + char envp[16]; + size_t envp_len; + int error; + + /* + * Fetch the initial bytes to try to identify BCM data. + * + * We always assert a low probe priority, as we only scan the initial + * bytes of the file. + */ + envp_len = bhnd_nv_ummin(sizeof(envp), bhnd_nvram_io_getsize(io)); + if ((error = bhnd_nvram_io_read(io, 0x0, envp, envp_len))) + return (error); + + /* A zero-length BCM-RAW buffer should contain a single terminating + * NUL */ + if (envp_len == 0) + return (ENXIO); + + if (envp_len == 1) { + if (envp[0] != '\0') + return (ENXIO); + + return (BHND_NVRAM_DATA_PROBE_MAYBE); + } + + /* Don't match on non-ASCII, non-printable data */ + for (size_t i = 0; i < envp_len; i++) { + char c = envp[i]; + if (envp[i] == '\0') + break; + + if (!bhnd_nv_isprint(c)) + return (ENXIO); + } + + /* The first character should be a valid key char */ + if (!bhnd_nv_isalpha(envp[0])) + return (ENXIO); + + return (BHND_NVRAM_DATA_PROBE_MAYBE); +} + +/** + * Initialize @p bcm with the provided NVRAM data mapped by @p src. + * + * @param bcm A newly allocated data instance. + */ +static int +bhnd_nvram_bcmraw_init(struct bhnd_nvram_bcmraw *bcm, struct bhnd_nvram_io *src) +{ + size_t io_size; + size_t capacity, offset; + int error; + + /* Fetch the input image size */ + io_size = bhnd_nvram_io_getsize(src); + + /* Allocate a buffer large enough to hold the NVRAM image, and + * an extra EOF-signaling NUL (on the chance it's missing from the + * source data) */ + if (io_size == SIZE_MAX) + return (ENOMEM); + + capacity = io_size + 1 /* room for extra NUL */; + bcm->size = io_size; + if ((bcm->data = bhnd_nv_malloc(capacity)) == NULL) + return (ENOMEM); + + /* Copy in the NVRAM image */ + if ((error = bhnd_nvram_io_read(src, 0x0, bcm->data, io_size))) + return (error); + + /* Process the buffer */ + bcm->count = 0; + for (offset = 0; offset < bcm->size; offset++) { + char *envp; + const char *name, *value; + size_t envp_len; + size_t name_len, value_len; + + /* Parse the key=value string */ + envp = (char *) (bcm->data + offset); + envp_len = strnlen(envp, bcm->size - offset); + error = bhnd_nvram_parse_env(envp, envp_len, '=', &name, + &name_len, &value, &value_len); + if (error) { + BHND_NV_LOG("error parsing envp at offset %#zx: %d\n", + offset, error); + return (error); + } + + /* Insert a '\0' character, replacing the '=' delimiter and + * allowing us to vend references directly to the variable + * name */ + *(envp + name_len) = '\0'; + + /* Add to variable count */ + bcm->count++; + + /* Seek past the value's terminating '\0' */ + offset += envp_len; + if (offset == io_size) { + BHND_NV_LOG("missing terminating NUL at offset %#zx\n", + offset); + return (EINVAL); + } + + /* If we hit EOF without finding a terminating NUL + * byte, we need to append it */ + if (++offset == bcm->size) { + BHND_NV_ASSERT(offset < capacity, + ("appending past end of buffer")); + bcm->size++; + *(bcm->data + offset) = '\0'; + } + + /* Check for explicit EOF (encoded as a single empty NUL + * terminated string) */ + if (*(bcm->data + offset) == '\0') + break; + } + + /* Reclaim any unused space in he backing buffer */ + if (offset < bcm->size) { + bcm->data = bhnd_nv_reallocf(bcm->data, bcm->size); + if (bcm->data == NULL) + return (ENOMEM); + } + + return (0); +} + +static int +bhnd_nvram_bcmraw_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_bcmraw *bcm; + int error; + + bcm = (struct bhnd_nvram_bcmraw *)nv; + + /* Parse the BCM input data and initialize our backing + * data representation */ + if ((error = bhnd_nvram_bcmraw_init(bcm, io))) { + bhnd_nvram_bcmraw_free(nv); + return (error); + } + + return (0); +} + +static void +bhnd_nvram_bcmraw_free(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv; + + if (bcm->data != NULL) + bhnd_nv_free(bcm->data); +} + +static size_t +bhnd_nvram_bcmraw_count(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv; + + return (bcm->count); +} + +static int +bhnd_nvram_bcmraw_size(struct bhnd_nvram_data *nv, size_t *size) +{ + return (bhnd_nvram_bcmraw_serialize(nv, NULL, size)); +} + +static int +bhnd_nvram_bcmraw_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len) +{ + struct bhnd_nvram_bcmraw *bcm; + char * const p = (char *)buf; + size_t limit; + size_t offset; + + bcm = (struct bhnd_nvram_bcmraw *)nv; + + /* Save the output buffer limit */ + if (buf == NULL) + limit = 0; + else + limit = *len; + + /* The serialized form will be exactly the length + * of our backing buffer representation */ + *len = bcm->size; + + /* Skip serialization if not requested, or report ENOMEM if + * buffer is too small */ + if (buf == NULL) { + return (0); + } else if (*len > limit) { + return (ENOMEM); + } + + /* Write all variables to the output buffer */ + memcpy(buf, bcm->data, *len); + + /* Rewrite all '\0' delimiters back to '=' */ + offset = 0; + while (offset < bcm->size) { + size_t name_len, value_len; + + name_len = strlen(p + offset); + + /* EOF? */ + if (name_len == 0) { + BHND_NV_ASSERT(*(p + offset) == '\0', + ("no NUL terminator")); + + offset++; + break; + } + + /* Rewrite 'name\0' to 'name=' */ + offset += name_len; + BHND_NV_ASSERT(*(p + offset) == '\0', ("incorrect offset")); + + *(p + offset) = '='; + offset++; + + value_len = strlen(p + offset); + offset += value_len + 1; + } + + return (0); +} + +static uint32_t +bhnd_nvram_bcmraw_caps(struct bhnd_nvram_data *nv) +{ + return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); +} + +static const char * +bhnd_nvram_bcmraw_next(struct bhnd_nvram_data *nv, void **cookiep) +{ + struct bhnd_nvram_bcmraw *bcm; + const char *envp; + + bcm = (struct bhnd_nvram_bcmraw *)nv; + + if (*cookiep == NULL) { + /* Start at the first NVRAM data record */ + envp = bcm->data; + } else { + /* Seek to next record */ + envp = *cookiep; + envp += strlen(envp) + 1; /* key + '\0' */ + envp += strlen(envp) + 1; /* value + '\0' */ + } + + /* EOF? */ + if (*envp == '\0') + return (NULL); + + *cookiep = (void *)(uintptr_t)envp; + return (envp); +} + +static void * +bhnd_nvram_bcmraw_find(struct bhnd_nvram_data *nv, const char *name) +{ + return (bhnd_nvram_data_generic_find(nv, name)); +} + +static int +bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, + size_t *len, bhnd_nvram_type type) +{ + return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); +} + +static const void * +bhnd_nvram_bcmraw_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type) +{ + const char *envp; + + /* Cookie points to key\0value\0 -- get the value address */ + envp = cookiep; + envp += strlen(envp) + 1; /* key + '\0' */ + *len = strlen(envp) + 1; /* value + '\0' */ + *type = BHND_NVRAM_TYPE_STRING; + + return (envp); +} + +static const char * +bhnd_nvram_bcmraw_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) +{ + /* Cookie points to key\0value\0 */ + return (cookiep); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h new file mode 100644 index 0000000000000..ee4583386024e --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $FreeBSD$ + */ + +#ifndef _BHND_NVRAM_BHND_NVRAM_BCMREG_H_ +#define _BHND_NVRAM_BHND_NVRAM_BCMREG_H_ + +#define BCM_NVRAM_GET_BITS(_value, _field) \ + ((_value & _field ## _MASK) >> _field ## _SHIFT) + +/* BCM NVRAM header fields */ +#define BCM_NVRAM_MAGIC 0x48534C46 /* 'FLSH' */ +#define BCM_NVRAM_VERSION 1 + +#define BCM_NVRAM_CRC_SKIP 9 /* skip magic, size, and crc8 */ + +#define BCM_NVRAM_CFG0_CRC_MASK 0x000000FF +#define BCM_NVRAM_CFG0_CRC_SHIFT 0 +#define BCM_NVRAM_CFG0_VER_MASK 0x0000FF00 +#define BCM_NVRAM_CFG0_VER_SHIFT 8 + +#define BCM_NVRAM_CFG0_SDRAM_INIT_FIELD cfg0 +#define BCM_NVRAM_CFG0_SDRAM_INIT_MASK 0xFFFF0000 +#define BCM_NVRAM_CFG0_SDRAM_INIT_SHIFT 16 +#define BCM_NVRAM_CFG0_SDRAM_INIT_VAR "sdram_init" +#define BCM_NVRAM_CFG0_SDRAM_INIT_FMT "0x%04x" + +#define BCM_NVRAM_CFG1_SDRAM_CFG_FIELD cfg1 +#define BCM_NVRAM_CFG1_SDRAM_CFG_MASK 0x0000FFFF +#define BCM_NVRAM_CFG1_SDRAM_CFG_SHIFT 0 +#define BCM_NVRAM_CFG1_SDRAM_CFG_VAR "sdram_config" +#define BCM_NVRAM_CFG1_SDRAM_CFG_FMT "0x%04x" + +#define BCM_NVRAM_CFG1_SDRAM_REFRESH_FIELD cfg1 +#define BCM_NVRAM_CFG1_SDRAM_REFRESH_MASK 0xFFFF0000 +#define BCM_NVRAM_CFG1_SDRAM_REFRESH_SHIFT 16 +#define BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR "sdram_refresh" +#define BCM_NVRAM_CFG1_SDRAM_REFRESH_FMT "0x%04x" + +#define BCM_NVRAM_SDRAM_NCDL_FIELD sdram_ncdl +#define BCM_NVRAM_SDRAM_NCDL_MASK UINT32_MAX +#define BCM_NVRAM_SDRAM_NCDL_SHIFT 0 +#define BCM_NVRAM_SDRAM_NCDL_VAR "sdram_ncdl" +#define BCM_NVRAM_SDRAM_NCDL_FMT "0x%08x" + +#endif /* _BHND_NVRAM_BHND_NVRAM_BCMREG_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_sprom_parser.h b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h index 5237dc10797d0..ab5b2a44e607e 100644 --- a/sys/dev/bhnd/nvram/bhnd_sprom_parser.h +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org> + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> * 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_BHND_SPROM_PARSER_H_ -#define _BHND_NVRAM_BHND_SPROM_PARSER_H_ - -#include <dev/bhnd/bhnd.h> - -struct bhnd_sprom; - -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); +#ifndef _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ +#define _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ /** - * bhnd sprom parser instance state. + * BCM NVRAM header value data. */ -struct bhnd_sprom { - device_t dev; /**< sprom parent device */ +union bhnd_nvram_bcm_hvar_value { + uint16_t u16; + uint32_t u32; +}; - uint8_t sp_rev; /**< sprom revision */ - - struct bhnd_resource *sp_res; /**< sprom resource. */ - bus_size_t sp_res_off; /**< offset to sprom image */ +/** + * 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 */ - 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 */ + /** 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_BHND_SPROM_PARSER_H_ */ +#endif /* _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c b/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c new file mode 100644 index 0000000000000..efd68a276a745 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c @@ -0,0 +1,586 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/endian.h> + +#ifdef _KERNEL + +#include <sys/param.h> +#include <sys/ctype.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#else /* !_KERNEL */ + +#include <ctype.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_datavar.h" + +#include "bhnd_nvram_data_bcmreg.h" /* for BCM_NVRAM_MAGIC */ + +/** + * Broadcom "Board Text" data class. + * + * This format is used to provide external NVRAM data for some + * fullmac WiFi devices, and as an input format when programming + * NVRAM/SPROM/OTP. + */ + +struct bhnd_nvram_btxt { + struct bhnd_nvram_data nv; /**< common instance state */ + struct bhnd_nvram_io *data; /**< memory-backed board text data */ + size_t count; /**< variable count */ +}; + +BHND_NVRAM_DATA_CLASS_DEFN(btxt, "Broadcom Board Text", + sizeof(struct bhnd_nvram_btxt)) + +/** Minimal identification header */ +union bhnd_nvram_btxt_ident { + uint32_t bcm_magic; + char btxt[8]; +}; + +static size_t bhnd_nvram_btxt_io_offset(struct bhnd_nvram_btxt *btxt, + void *cookiep); + +static int bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io, + size_t offset, size_t *line_len, size_t *env_len); +static int bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io, + size_t *offset); +static int bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io, + size_t *offset); + +static int +bhnd_nvram_btxt_probe(struct bhnd_nvram_io *io) +{ + union bhnd_nvram_btxt_ident ident; + char c; + int error; + + /* Look at the initial header for something that looks like + * an ASCII board text file */ + if ((error = bhnd_nvram_io_read(io, 0x0, &ident, sizeof(ident)))) + return (error); + + /* The BCM NVRAM format uses a 'FLSH' little endian magic value, which + * shouldn't be interpreted as BTXT */ + if (le32toh(ident.bcm_magic) == BCM_NVRAM_MAGIC) + return (ENXIO); + + /* Don't match on non-ASCII/non-printable data */ + for (size_t i = 0; i < nitems(ident.btxt); i++) { + c = ident.btxt[i]; + if (!bhnd_nv_isprint(c)) + return (ENXIO); + } + + /* The first character should either be a valid key char (alpha), + * whitespace, or the start of a comment ('#') */ + c = ident.btxt[0]; + if (!bhnd_nv_isspace(c) && !bhnd_nv_isalpha(c) && c != '#') + return (ENXIO); + + /* We assert a low priority, given that we've only scanned an + * initial few bytes of the file. */ + return (BHND_NVRAM_DATA_PROBE_MAYBE); +} + +/** + * Initialize @p btxt with the provided board text data mapped by @p src. + * + * @param btxt A newly allocated data instance. + */ +static int +bhnd_nvram_btxt_init(struct bhnd_nvram_btxt *btxt, struct bhnd_nvram_io *src) +{ + const void *ptr; + const char *name, *value; + size_t name_len, value_len; + size_t line_len, env_len; + size_t io_offset, io_size, str_size; + int error; + + BHND_NV_ASSERT(btxt->data == NULL, ("btxt data already allocated")); + + if ((btxt->data = bhnd_nvram_iobuf_copy(src)) == NULL) + return (ENOMEM); + + io_size = bhnd_nvram_io_getsize(btxt->data); + io_offset = 0; + + /* Fetch a pointer mapping the entirity of the board text data */ + error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_size, NULL); + if (error) + return (error); + + /* Determine the actual size, minus any terminating NUL. We + * parse NUL-terminated C strings, but do not include NUL termination + * in our internal or serialized representations */ + str_size = strnlen(ptr, io_size); + + /* If the terminating NUL is not found at the end of the buffer, + * this is BCM-RAW or other NUL-delimited NVRAM format. */ + if (str_size < io_size && str_size + 1 < io_size) + return (EINVAL); + + /* Adjust buffer size to account for NUL termination (if any) */ + io_size = str_size; + if ((error = bhnd_nvram_io_setsize(btxt->data, io_size))) + return (error); + + /* Process the buffer */ + btxt->count = 0; + while (io_offset < io_size) { + const void *envp; + + /* Seek to the next key=value entry */ + if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset))) + return (error); + + /* Determine the entry and line length */ + error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset, + &line_len, &env_len); + if (error) + return (error); + + /* EOF? */ + if (env_len == 0) { + BHND_NV_ASSERT(io_offset == io_size, + ("zero-length record returned from " + "bhnd_nvram_btxt_seek_next()")); + break; + } + + /* Fetch a pointer to the line start */ + error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &envp, + env_len, NULL); + if (error) + return (error); + + /* Parse the key=value string */ + error = bhnd_nvram_parse_env(envp, env_len, '=', &name, + &name_len, &value, &value_len); + if (error) { + return (error); + } + + /* Insert a '\0' character, replacing the '=' delimiter and + * allowing us to vend references directly to the variable + * name */ + error = bhnd_nvram_io_write(btxt->data, io_offset+name_len, + &(char){'\0'}, 1); + if (error) + return (error); + + /* Add to variable count */ + btxt->count++; + + /* Advance past EOL */ + io_offset += line_len; + } + + return (0); +} + +static int +bhnd_nvram_btxt_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_btxt *btxt; + int error; + + /* Allocate and initialize the BTXT data instance */ + btxt = (struct bhnd_nvram_btxt *)nv; + + /* Parse the BTXT input data and initialize our backing + * data representation */ + if ((error = bhnd_nvram_btxt_init(btxt, io))) { + bhnd_nvram_btxt_free(nv); + return (error); + } + + return (0); +} + +static void +bhnd_nvram_btxt_free(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv; + if (btxt->data != NULL) + bhnd_nvram_io_free(btxt->data); +} + +size_t +bhnd_nvram_btxt_count(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv; + return (btxt->count); +} + +static int +bhnd_nvram_btxt_size(struct bhnd_nvram_data *nv, size_t *size) +{ + struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv; + + /* The serialized form will be identical in length + * to our backing buffer representation */ + *size = bhnd_nvram_io_getsize(btxt->data); + return (0); +} + +static int +bhnd_nvram_btxt_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len) +{ + struct bhnd_nvram_btxt *btxt; + size_t limit; + int error; + + btxt = (struct bhnd_nvram_btxt *)nv; + + limit = *len; + + /* Provide actual output size */ + if ((error = bhnd_nvram_data_size(nv, len))) + return (error); + + if (buf == NULL) { + return (0); + } else if (limit < *len) { + return (ENOMEM); + } + + /* Copy our internal representation to the output buffer */ + if ((error = bhnd_nvram_io_read(btxt->data, 0x0, buf, *len))) + return (error); + + /* Restore the original key=value format, rewriting all '\0' + * key\0value delimiters back to '=' */ + for (char *p = buf; (size_t)(p - (char *)buf) < *len; p++) { + if (*p == '\0') + *p = '='; + } + + return (0); +} + +static uint32_t +bhnd_nvram_btxt_caps(struct bhnd_nvram_data *nv) +{ + return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); +} + +static void * +bhnd_nvram_btxt_find(struct bhnd_nvram_data *nv, const char *name) +{ + return (bhnd_nvram_data_generic_find(nv, name)); +} + +static const char * +bhnd_nvram_btxt_next(struct bhnd_nvram_data *nv, void **cookiep) +{ + struct bhnd_nvram_btxt *btxt; + const void *nptr; + size_t io_offset, io_size; + int error; + + btxt = (struct bhnd_nvram_btxt *)nv; + + io_size = bhnd_nvram_io_getsize(btxt->data); + io_offset = bhnd_nvram_btxt_io_offset(btxt, *cookiep); + + /* Already at EOF? */ + if (io_offset == io_size) + return (NULL); + + /* Seek to the next entry (if any) */ + if ((error = bhnd_nvram_btxt_seek_eol(btxt->data, &io_offset))) { + BHND_NV_LOG("unexpected error in seek_eol(): %d\n", error); + return (NULL); + } + + if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset))) { + BHND_NV_LOG("unexpected error in seek_next(): %d\n", error); + return (NULL); + } + + /* Provide the new cookie for this offset */ + if (io_offset > UINTPTR_MAX) { + BHND_NV_LOG("io_offset > UINPTR_MAX!\n"); + return (NULL); + } + + *cookiep = (void *)(uintptr_t)io_offset; + + /* Hit EOF? */ + if (io_offset == io_size) + return (NULL); + + /* Fetch the name pointer; it must be at least 1 byte long */ + error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &nptr, 1, NULL); + if (error) { + BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error); + return (NULL); + } + + /* Return the name pointer */ + return (nptr); +} + +static int +bhnd_nvram_btxt_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, + size_t *len, bhnd_nvram_type type) +{ + return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); +} + +const void * +bhnd_nvram_btxt_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type) +{ + struct bhnd_nvram_btxt *btxt; + const void *eptr; + const char *vptr; + size_t io_offset, io_size; + size_t line_len, env_len; + int error; + + btxt = (struct bhnd_nvram_btxt *)nv; + + io_size = bhnd_nvram_io_getsize(btxt->data); + io_offset = bhnd_nvram_btxt_io_offset(btxt, cookiep); + + /* At EOF? */ + if (io_offset == io_size) + return (NULL); + + /* Determine the entry length */ + error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset, &line_len, + &env_len); + if (error) { + BHND_NV_LOG("unexpected error in entry_len(): %d\n", error); + return (NULL); + } + + /* Fetch the entry's value pointer and length */ + error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &eptr, env_len, + NULL); + if (error) { + BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error); + return (NULL); + } + + error = bhnd_nvram_parse_env(eptr, env_len, '\0', NULL, NULL, &vptr, + len); + if (error) { + BHND_NV_LOG("unexpected error in parse_env(): %d\n", error); + return (NULL); + } + + /* Type is always CSTR */ + *type = BHND_NVRAM_TYPE_STRING; + + return (vptr); +} + +static const char * +bhnd_nvram_btxt_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) +{ + struct bhnd_nvram_btxt *btxt; + const void *ptr; + size_t io_offset, io_size; + int error; + + btxt = (struct bhnd_nvram_btxt *)nv; + + io_size = bhnd_nvram_io_getsize(btxt->data); + io_offset = bhnd_nvram_btxt_io_offset(btxt, cookiep); + + /* At EOF? */ + if (io_offset == io_size) + BHND_NV_PANIC("invalid cookiep: %p", cookiep); + + /* Variable name is found directly at the given offset; trailing + * NUL means we can assume that it's at least 1 byte long */ + error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &ptr, 1, NULL); + if (error) + BHND_NV_PANIC("unexpected error in read_ptr(): %d\n", error); + + return (ptr); +} + +/* Convert cookie back to an I/O offset */ +static size_t +bhnd_nvram_btxt_io_offset(struct bhnd_nvram_btxt *btxt, void *cookiep) +{ + size_t io_size; + uintptr_t cval; + + io_size = bhnd_nvram_io_getsize(btxt->data); + cval = (uintptr_t)cookiep; + + BHND_NV_ASSERT(cval < SIZE_MAX, ("cookie > SIZE_MAX)")); + BHND_NV_ASSERT(cval <= io_size, ("cookie > io_size)")); + + return ((size_t)cval); +} + +/* Determine the entry length and env 'key=value' string length of the entry + * at @p offset */ +static int +bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io, size_t offset, + size_t *line_len, size_t *env_len) +{ + const uint8_t *baseptr, *p; + const void *rbuf; + size_t nbytes; + int error; + + /* Fetch read buffer */ + if ((error = bhnd_nvram_io_read_ptr(io, offset, &rbuf, 0, &nbytes))) + return (error); + + /* Find record termination (EOL, or '#') */ + p = rbuf; + baseptr = rbuf; + while ((size_t)(p - baseptr) < nbytes) { + if (*p == '#' || *p == '\n' || *p == '\r') + break; + + p++; + } + + /* Got line length, now trim any trailing whitespace to determine + * actual env length */ + *line_len = p - baseptr; + *env_len = *line_len; + + for (size_t i = 0; i < *line_len; i++) { + char c = baseptr[*line_len - i - 1]; + if (!bhnd_nv_isspace(c)) + break; + + *env_len -= 1; + } + + return (0); +} + +/* Seek past the next line ending (\r, \r\n, or \n) */ +static int +bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io, size_t *offset) +{ + const uint8_t *baseptr, *p; + const void *rbuf; + size_t nbytes; + int error; + + /* Fetch read buffer */ + if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes))) + return (error); + + baseptr = rbuf; + p = rbuf; + while ((size_t)(p - baseptr) < nbytes) { + char c = *p; + + /* Advance to next char. The next position may be EOF, in which + * case a read will be invalid */ + p++; + + if (c == '\r') { + /* CR, check for optional LF */ + if ((size_t)(p - baseptr) < nbytes) { + if (*p == '\n') + p++; + } + + break; + } else if (c == '\n') { + break; + } + } + + /* Hit newline or EOF */ + *offset += (p - baseptr); + return (0); +} + +/* Seek to the next valid non-comment line (or EOF) */ +static int +bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io, size_t *offset) +{ + const uint8_t *baseptr, *p; + const void *rbuf; + size_t nbytes; + int error; + + /* Fetch read buffer */ + if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes))) + return (error); + + /* Skip leading whitespace and comments */ + baseptr = rbuf; + p = rbuf; + while ((size_t)(p - baseptr) < nbytes) { + char c = *p; + + /* Skip whitespace */ + if (bhnd_nv_isspace(c)) { + p++; + continue; + } + + /* Skip entire comment line */ + if (c == '#') { + size_t line_off = *offset + (p - baseptr); + + if ((error = bhnd_nvram_btxt_seek_eol(io, &line_off))) + return (error); + + p = baseptr + (line_off - *offset); + continue; + } + + /* Non-whitespace, non-comment */ + break; + } + + *offset += (p - baseptr); + return (0); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c b/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c new file mode 100644 index 0000000000000..4b5825dcbf8fc --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c @@ -0,0 +1,1988 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/endian.h> + +#ifdef _KERNEL +#include <sys/param.h> +#include <sys/ctype.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#include <machine/_inttypes.h> +#else /* !_KERNEL */ +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_datavar.h" + +#include "bhnd_nvram_data_spromvar.h" + +/* + * BHND SPROM NVRAM data class + * + * The SPROM data format is a fixed-layout, non-self-descriptive binary format, + * used on Broadcom wireless and wired adapters, that provides a subset of the + * variables defined by Broadcom SoC NVRAM formats. + */ +BHND_NVRAM_DATA_CLASS_DEFN(sprom, "Broadcom SPROM", + sizeof(struct bhnd_nvram_sprom)) + +static int sprom_sort_idx(const void *lhs, const void *rhs); + +static int sprom_opcode_state_init(struct sprom_opcode_state *state, + const struct bhnd_sprom_layout *layout); +static int sprom_opcode_state_reset(struct sprom_opcode_state *state); +static int sprom_opcode_state_seek(struct sprom_opcode_state *state, + struct sprom_opcode_idx *indexed); + +static int sprom_opcode_next_var(struct sprom_opcode_state *state); +static int sprom_opcode_parse_var(struct sprom_opcode_state *state, + struct sprom_opcode_idx *indexed); + +static int sprom_opcode_next_binding(struct sprom_opcode_state *state); + +static int sprom_opcode_set_type(struct sprom_opcode_state *state, + bhnd_nvram_type type); + +static int sprom_opcode_set_var(struct sprom_opcode_state *state, + size_t vid); +static int sprom_opcode_clear_var(struct sprom_opcode_state *state); +static int sprom_opcode_flush_bind(struct sprom_opcode_state *state); +static int sprom_opcode_read_opval32(struct sprom_opcode_state *state, + uint8_t type, uint32_t *opval); +static int sprom_opcode_apply_scale(struct sprom_opcode_state *state, + uint32_t *value); + +static int sprom_opcode_step(struct sprom_opcode_state *state, + uint8_t *opcode); + +#define SPROM_OP_BAD(_state, _fmt, ...) \ + BHND_NV_LOG("bad encoding at %td: " _fmt, \ + (_state)->input - (_state)->layout->bindings, ##__VA_ARGS__) + +#define SPROM_COOKIE_TO_NVRAM(_cookie) \ + bhnd_nvram_get_vardefn(((struct sprom_opcode_idx *)_cookie)->vid) + +/** + * Read the magic value from @p io, and verify that it matches + * the @p layout's expected magic value. + * + * If @p layout does not defined a magic value, @p magic is set to 0x0 + * and success is returned. + * + * @param io An I/O context mapping the SPROM data to be identified. + * @param layout The SPROM layout against which @p io should be verified. + * @param[out] magic On success, the SPROM magic value. + * + * @retval 0 success + * @retval non-zero If checking @p io otherwise fails, a regular unix + * error code will be returned. + */ +static int +bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io *io, + const struct bhnd_sprom_layout *layout, uint16_t *magic) +{ + int error; + + /* Skip if layout does not define a magic value */ + if (layout->flags & SPROM_LAYOUT_MAGIC_NONE) + return (0); + + /* Read the magic value */ + error = bhnd_nvram_io_read(io, layout->magic_offset, magic, + sizeof(*magic)); + if (error) + return (error); + + *magic = le16toh(*magic); + + /* If the signature does not match, skip to next layout */ + if (*magic != layout->magic_value) + return (ENXIO); + + return (0); +} + +/** + * Attempt to identify the format of the SPROM data mapped by @p io. + * + * The SPROM data format does not provide any identifying information at a + * known offset, instead requiring that we iterate over the known SPROM image + * sizes until we are able to compute a valid checksum (and, for later + * revisions, validate a signature at a revision-specific offset). + * + * @param io An I/O context mapping the SPROM data to be identified. + * @param[out] ident On success, the identified SPROM layout. + * @param[out] shadow On success, a correctly sized iobuf instance mapping + * a copy of the identified SPROM image. The caller is + * responsible for deallocating this instance via + * bhnd_nvram_io_free() + * + * @retval 0 success + * @retval non-zero If identifying @p io otherwise fails, a regular unix + * error code will be returned. + */ +static int +bhnd_nvram_sprom_ident(struct bhnd_nvram_io *io, + const struct bhnd_sprom_layout **ident, struct bhnd_nvram_io **shadow) +{ + struct bhnd_nvram_io *buf; + uint8_t crc; + size_t crc_errors; + size_t sprom_sz_max; + int error; + + /* Find the largest SPROM layout size */ + sprom_sz_max = 0; + for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) { + sprom_sz_max = bhnd_nv_ummax(sprom_sz_max, + bhnd_sprom_layouts[i].size); + } + + /* Allocate backing buffer and initialize CRC state */ + buf = bhnd_nvram_iobuf_empty(0, sprom_sz_max); + crc = BHND_NVRAM_CRC8_INITIAL; + crc_errors = 0; + + /* We iterate the SPROM layouts smallest to largest, allowing us to + * perform incremental checksum calculation */ + for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) { + const struct bhnd_sprom_layout *layout; + void *ptr; + size_t nbytes, nr; + uint16_t magic; + uint8_t srev; + bool crc_valid; + bool have_magic; + + layout = &bhnd_sprom_layouts[i]; + nbytes = bhnd_nvram_io_getsize(buf); + + if ((layout->flags & SPROM_LAYOUT_MAGIC_NONE)) { + have_magic = false; + } else { + have_magic = true; + } + + /* Layout instances must be ordered from smallest to largest by + * the nvram_map compiler */ + if (nbytes > layout->size) + BHND_NV_PANIC("SPROM layout is defined out-of-order"); + + /* Calculate number of additional bytes to be read */ + nr = layout->size - nbytes; + + /* Adjust the buffer size and fetch a write pointer */ + if ((error = bhnd_nvram_io_setsize(buf, layout->size))) + goto failed; + + error = bhnd_nvram_io_write_ptr(buf, nbytes, &ptr, nr, NULL); + if (error) + goto failed; + + /* Read image data and update CRC (errors are reported + * after the signature check) */ + if ((error = bhnd_nvram_io_read(io, nbytes, ptr, nr))) + goto failed; + + crc = bhnd_nvram_crc8(ptr, nr, crc); + crc_valid = (crc == BHND_NVRAM_CRC8_VALID); + if (!crc_valid) + crc_errors++; + + /* Fetch SPROM revision */ + error = bhnd_nvram_io_read(buf, layout->srev_offset, &srev, + sizeof(srev)); + if (error) + goto failed; + + /* Early sromrev 1 devices (specifically some BCM440x enet + * cards) are reported to have been incorrectly programmed + * with a revision of 0x10. */ + if (layout->rev == 1 && srev == 0x10) + srev = 0x1; + + /* Check revision against the layout definition */ + if (srev != layout->rev) + continue; + + /* Check the magic value, skipping to the next layout on + * failure. */ + error = bhnd_nvram_sprom_check_magic(buf, layout, &magic); + if (error) { + /* If the CRC is was valid, log the mismatch */ + if (crc_valid || BHND_NV_VERBOSE) { + BHND_NV_LOG("invalid sprom %hhu signature: " + "0x%hx (expected 0x%hx)\n", srev, + magic, layout->magic_value); + + error = ENXIO; + goto failed; + } + + continue; + } + + /* Check for an earlier CRC error */ + if (!crc_valid) { + /* If the magic check succeeded, then we may just have + * data corruption -- log the CRC error */ + if (have_magic || BHND_NV_VERBOSE) { + BHND_NV_LOG("sprom %hhu CRC error (crc=%#hhx, " + "expected=%#x)\n", srev, crc, + BHND_NVRAM_CRC8_VALID); + } + + continue; + } + + /* Identified */ + *shadow = buf; + *ident = layout; + return (0); + } + + /* No match -- set error and fallthrough */ + error = ENXIO; + if (crc_errors > 0 && BHND_NV_VERBOSE) { + BHND_NV_LOG("sprom parsing failed with %zu CRC errors\n", + crc_errors); + } + +failed: + bhnd_nvram_io_free(buf); + return (error); +} + +static int +bhnd_nvram_sprom_probe(struct bhnd_nvram_io *io) +{ + const struct bhnd_sprom_layout *layout; + struct bhnd_nvram_io *shadow; + int error; + + /* Try to parse the input */ + if ((error = bhnd_nvram_sprom_ident(io, &layout, &shadow))) + return (error); + + /* Clean up the shadow iobuf */ + bhnd_nvram_io_free(shadow); + + return (BHND_NVRAM_DATA_PROBE_DEFAULT); +} + +static int +bhnd_nvram_sprom_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_sprom *sp; + size_t num_vars; + int error; + + sp = (struct bhnd_nvram_sprom *)nv; + + /* Identify the SPROM input data */ + if ((error = bhnd_nvram_sprom_ident(io, &sp->layout, &sp->data))) + goto failed; + + /* Initialize SPROM binding eval state */ + if ((error = sprom_opcode_state_init(&sp->state, sp->layout))) + goto failed; + + /* Allocate our opcode index */ + sp->num_idx = sp->layout->num_vars; + if ((sp->idx = bhnd_nv_calloc(sp->num_idx, sizeof(*sp->idx))) == NULL) + goto failed; + + /* Parse out index entries from our stateful opcode stream */ + for (num_vars = 0; num_vars < sp->num_idx; num_vars++) { + size_t opcodes; + + /* Seek to next entry */ + if ((error = sprom_opcode_next_var(&sp->state))) { + SPROM_OP_BAD(&sp->state, + "error reading expected variable entry: %d\n", + error); + goto failed; + } + + /* We limit the SPROM index representations to the minimal + * type widths capable of covering all known layouts */ + + /* Save SPROM image offset */ + if (sp->state.offset > UINT16_MAX) { + SPROM_OP_BAD(&sp->state, + "cannot index large offset %u\n", sp->state.offset); + } + sp->idx[num_vars].offset = sp->state.offset; + + /* Save current variable ID */ + if (sp->state.vid > UINT16_MAX) { + SPROM_OP_BAD(&sp->state, + "cannot index large vid %zu\n", sp->state.vid); + } + sp->idx[num_vars].vid = sp->state.vid; + + /* Save opcode position */ + opcodes = (sp->state.input - sp->layout->bindings); + if (opcodes > UINT16_MAX) { + SPROM_OP_BAD(&sp->state, + "cannot index large opcode offset %zu\n", opcodes); + } + sp->idx[num_vars].opcodes = opcodes; + } + + /* Should have reached end of binding table; next read must return + * ENOENT */ + if ((error = sprom_opcode_next_var(&sp->state)) != ENOENT) { + BHND_NV_LOG("expected EOF parsing binding table: %d\n", error); + goto failed; + } + + /* Sort index by variable ID, ascending */ + qsort(sp->idx, sp->num_idx, sizeof(sp->idx[0]), sprom_sort_idx); + + return (0); + +failed: + if (sp->data != NULL) + bhnd_nvram_io_free(sp->data); + + if (sp->idx != NULL) + bhnd_nv_free(sp->idx); + + return (error); +} + +/* sort function for sprom_opcode_idx values */ +static int +sprom_sort_idx(const void *lhs, const void *rhs) +{ + const struct sprom_opcode_idx *l, *r; + + l = lhs; + r = rhs; + + if (l->vid < r->vid) + return (-1); + if (l->vid > r->vid) + return (1); + return (0); +} + +static void +bhnd_nvram_sprom_free(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_sprom *sp = (struct bhnd_nvram_sprom *)nv; + + bhnd_nvram_io_free(sp->data); + bhnd_nv_free(sp->idx); +} + +size_t +bhnd_nvram_sprom_count(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv; + return (sprom->layout->num_vars); +} + +static int +bhnd_nvram_sprom_size(struct bhnd_nvram_data *nv, size_t *size) +{ + struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv; + + /* The serialized form will be identical in length + * to our backing buffer representation */ + *size = bhnd_nvram_io_getsize(sprom->data); + return (0); +} + +static int +bhnd_nvram_sprom_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len) +{ + struct bhnd_nvram_sprom *sprom; + size_t limit, req_len; + int error; + + sprom = (struct bhnd_nvram_sprom *)nv; + limit = *len; + + /* Provide the required size */ + if ((error = bhnd_nvram_sprom_size(nv, &req_len))) + return (error); + + *len = req_len; + + if (buf == NULL) { + return (0); + } else if (*len > limit) { + return (ENOMEM); + } + + /* Write to the output buffer */ + return (bhnd_nvram_io_read(sprom->data, 0x0, buf, *len)); +} + +static uint32_t +bhnd_nvram_sprom_caps(struct bhnd_nvram_data *nv) +{ + return (BHND_NVRAM_DATA_CAP_INDEXED); +} + +static const char * +bhnd_nvram_sprom_next(struct bhnd_nvram_data *nv, void **cookiep) +{ + struct bhnd_nvram_sprom *sp; + struct sprom_opcode_idx *idx_entry; + size_t idx_next; + const struct bhnd_nvram_vardefn *var; + + sp = (struct bhnd_nvram_sprom *)nv; + + /* Seek to appropriate starting point */ + if (*cookiep == NULL) { + /* Start search at first index entry */ + idx_next = 0; + } else { + /* Determine current index position */ + idx_entry = *cookiep; + idx_next = (size_t)(idx_entry - sp->idx); + BHND_NV_ASSERT(idx_next < sp->num_idx, + ("invalid index %zu; corrupt cookie?", idx_next)); + + /* Advance to next entry */ + idx_next++; + + /* Check for EOF */ + if (idx_next == sp->num_idx) + return (NULL); + } + + /* Skip entries that are disabled by virtue of IGNALL1 */ + for (; idx_next < sp->num_idx; idx_next++) { + /* Fetch index entry and update cookiep */ + idx_entry = &sp->idx[idx_next]; + *cookiep = idx_entry; + + /* Fetch variable definition */ + var = bhnd_nvram_get_vardefn(idx_entry->vid); + + /* We might need to parse the variable's value to determine + * whether it should be treated as unset */ + if (var->flags & BHND_NVRAM_VF_IGNALL1) { + int error; + size_t len; + + error = bhnd_nvram_sprom_getvar(nv, *cookiep, NULL, + &len, var->type); + if (error) { + BHND_NV_ASSERT(error == ENOENT, ("unexpected " + "error parsing variable: %d", error)); + + continue; + } + } + + /* Found! */ + return (var->name); + } + + /* Reached end of index entries */ + return (NULL); +} + +/* bsearch function used by bhnd_nvram_sprom_find() */ +static int +bhnd_nvram_sprom_find_vid_compare(const void *key, const void *rhs) +{ + const struct sprom_opcode_idx *r; + size_t l; + + l = *(const size_t *)key; + r = rhs; + + if (l < r->vid) + return (-1); + if (l > r->vid) + return (1); + return (0); +} + +static void * +bhnd_nvram_sprom_find(struct bhnd_nvram_data *nv, const char *name) +{ + struct bhnd_nvram_sprom *sp; + const struct bhnd_nvram_vardefn *var; + size_t vid; + + sp = (struct bhnd_nvram_sprom *)nv; + + /* Determine the variable ID for the given name */ + if ((var = bhnd_nvram_find_vardefn(name)) == NULL) + return (NULL); + + vid = bhnd_nvram_get_vardefn_id(var); + + /* Search our index for the variable ID */ + return (bsearch(&vid, sp->idx, sp->num_idx, sizeof(sp->idx[0]), + bhnd_nvram_sprom_find_vid_compare)); +} + +/** + * Read the value of @p type from the SPROM data at @p offset, apply @p mask + * and @p shift, and OR with the existing @p value. + * + * @param sp The SPROM data instance. + * @param var The NVRAM variable definition + * @param type The type to read at @p offset + * @param offset The data offset to be read. + * @param mask The mask to be applied to the value read at @p offset. + * @param shift The shift to be applied after masking; if positive, a right + * shift will be applied, if negative, a left shift. + * @param value The read destination; the parsed value will be OR'd with the + * current contents of @p value. + */ +static int +bhnd_nvram_sprom_read_offset(struct bhnd_nvram_sprom *sp, + const struct bhnd_nvram_vardefn *var, bhnd_nvram_type type, + size_t offset, uint32_t mask, int8_t shift, + union bhnd_nvram_sprom_intv *value) +{ + size_t sp_width; + int error; + union { + uint8_t u8; + uint16_t u16; + uint32_t u32; + int8_t s8; + int16_t s16; + int32_t s32; + } sp_value; + + /* Determine type width */ + sp_width = bhnd_nvram_value_size(type, NULL, 0, 1); + if (sp_width == 0) { + /* Variable-width types are unsupported */ + BHND_NV_LOG("invalid %s SPROM offset type %d\n", var->name, + type); + return (EFTYPE); + } + + /* Perform read */ + error = bhnd_nvram_io_read(sp->data, offset, &sp_value, + sp_width); + if (error) { + BHND_NV_LOG("error reading %s SPROM offset %#zx: %d\n", + var->name, offset, error); + return (EFTYPE); + } + +#define NV_PARSE_INT(_type, _src, _dest, _swap) do { \ + /* Swap to host byte order */ \ + sp_value. _src = (_type) _swap(sp_value. _src); \ + \ + /* Mask and shift the value */ \ + sp_value. _src &= mask; \ + if (shift > 0) { \ + sp_value. _src >>= shift; \ + } else if (shift < 0) { \ + sp_value. _src <<= -shift; \ + } \ + \ + /* Emit output, widening to 32-bit representation */ \ + value-> _dest |= sp_value. _src; \ +} while(0) + + /* Apply mask/shift and widen to a common 32bit representation */ + switch (type) { + case BHND_NVRAM_TYPE_UINT8: + NV_PARSE_INT(uint8_t, u8, u32, ); + break; + case BHND_NVRAM_TYPE_UINT16: + NV_PARSE_INT(uint16_t, u16, u32, le16toh); + break; + case BHND_NVRAM_TYPE_UINT32: + NV_PARSE_INT(uint32_t, u32, u32, le32toh); + break; + case BHND_NVRAM_TYPE_INT8: + NV_PARSE_INT(int8_t, s8, s32, ); + break; + case BHND_NVRAM_TYPE_INT16: + NV_PARSE_INT(int16_t, s16, s32, le16toh); + break; + case BHND_NVRAM_TYPE_INT32: + NV_PARSE_INT(int32_t, s32, s32, le32toh); + break; + case BHND_NVRAM_TYPE_CHAR: + NV_PARSE_INT(uint8_t, u8, u32, ); + break; + + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_INT64: + case BHND_NVRAM_TYPE_STRING: + /* fallthrough (unused by SPROM) */ + default: + BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type); + return (EFTYPE); + } + + return (0); +} + +static int +bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, + size_t *len, bhnd_nvram_type otype) +{ + bhnd_nvram_val_t val; + struct bhnd_nvram_sprom *sp; + struct sprom_opcode_idx *idx; + const struct bhnd_nvram_vardefn *var; + union bhnd_nvram_sprom_storage storage; + union bhnd_nvram_sprom_storage *inp; + union bhnd_nvram_sprom_intv intv; + bhnd_nvram_type var_btype; + size_t ilen, ipos, iwidth; + size_t nelem; + bool all_bits_set; + int error; + + sp = (struct bhnd_nvram_sprom *)nv; + idx = cookiep; + + BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep")); + + /* Fetch canonical variable definition */ + var = SPROM_COOKIE_TO_NVRAM(cookiep); + BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep)); + + /* + * Fetch the array length from the SPROM variable definition. + * + * This generally be identical to the array length provided by the + * canonical NVRAM variable definition, but some SPROM layouts may + * define a smaller element count. + */ + if ((error = sprom_opcode_parse_var(&sp->state, idx))) { + BHND_NV_LOG("variable evaluation failed: %d\n", error); + return (error); + } + + nelem = sp->state.var.nelem; + if (nelem > var->nelem) { + BHND_NV_LOG("SPROM array element count %zu cannot be " + "represented by '%s' element count of %hhu\n", nelem, + var->name, var->nelem); + return (EFTYPE); + } + + /* Fetch the var's base element type */ + var_btype = bhnd_nvram_base_type(var->type); + + /* Calculate total byte length of the native encoding */ + if ((iwidth = bhnd_nvram_value_size(var_btype, NULL, 0, 1)) == 0) { + /* SPROM does not use (and we do not support) decoding of + * variable-width data types */ + BHND_NV_LOG("invalid SPROM data type: %d", var->type); + return (EFTYPE); + } + ilen = nelem * iwidth; + + /* Decode into our own local storage. */ + inp = &storage; + if (ilen > sizeof(storage)) { + BHND_NV_LOG("error decoding '%s', SPROM_ARRAY_MAXLEN " + "incorrect\n", var->name); + return (EFTYPE); + } + + /* Zero-initialize our decode buffer; any output elements skipped + * during decode should default to zero. */ + memset(inp, 0, ilen); + + /* + * Decode the SPROM data, iteratively decoding up to nelem values. + */ + if ((error = sprom_opcode_state_seek(&sp->state, idx))) { + BHND_NV_LOG("variable seek failed: %d\n", error); + return (error); + } + + ipos = 0; + intv.u32 = 0x0; + if (var->flags & BHND_NVRAM_VF_IGNALL1) + all_bits_set = true; + else + all_bits_set = false; + while ((error = sprom_opcode_next_binding(&sp->state)) == 0) { + struct sprom_opcode_bind *binding; + struct sprom_opcode_var *binding_var; + bhnd_nvram_type intv_type; + size_t offset; + size_t nbyte; + uint32_t skip_in_bytes; + void *ptr; + + BHND_NV_ASSERT( + sp->state.var_state >= SPROM_OPCODE_VAR_STATE_OPEN, + ("invalid var state")); + BHND_NV_ASSERT(sp->state.var.have_bind, ("invalid bind state")); + + binding_var = &sp->state.var; + binding = &sp->state.var.bind; + + if (ipos >= nelem) { + BHND_NV_LOG("output skip %u positioned " + "%zu beyond nelem %zu\n", + binding->skip_out, ipos, nelem); + return (EINVAL); + } + + /* Calculate input skip bytes for this binding */ + skip_in_bytes = binding->skip_in; + error = sprom_opcode_apply_scale(&sp->state, &skip_in_bytes); + if (error) + return (error); + + /* Bind */ + offset = sp->state.offset; + for (size_t i = 0; i < binding->count; i++) { + /* Read the offset value, OR'ing with the current + * value of intv */ + error = bhnd_nvram_sprom_read_offset(sp, var, + binding_var->base_type, + offset, + binding_var->mask, + binding_var->shift, + &intv); + if (error) + return (error); + + /* If IGNALL1, record whether value does not have + * all bits set. */ + if (var->flags & BHND_NVRAM_VF_IGNALL1 && + all_bits_set) + { + uint32_t all1; + + all1 = binding_var->mask; + if (binding_var->shift > 0) + all1 >>= binding_var->shift; + else if (binding_var->shift < 0) + all1 <<= -binding_var->shift; + + if ((intv.u32 & all1) != all1) + all_bits_set = false; + } + + /* Adjust input position; this was already verified to + * not overflow/underflow during SPROM opcode + * evaluation */ + if (binding->skip_in_negative) { + offset -= skip_in_bytes; + } else { + offset += skip_in_bytes; + } + + /* Skip writing to inp if additional bindings are + * required to fully populate intv */ + if (binding->skip_out == 0) + continue; + + /* We use bhnd_nvram_value_coerce() to perform + * overflow-checked coercion from the widened + * uint32/int32 intv value to the requested output + * type */ + if (bhnd_nvram_is_signed_type(var_btype)) + intv_type = BHND_NVRAM_TYPE_INT32; + else + intv_type = BHND_NVRAM_TYPE_UINT32; + + /* Calculate address of the current element output + * position */ + ptr = (uint8_t *)inp + (iwidth * ipos); + + /* Perform coercion of the array element */ + nbyte = iwidth; + error = bhnd_nvram_value_coerce(&intv, sizeof(intv), + intv_type, ptr, &nbyte, var_btype); + if (error) + return (error); + + /* Clear temporary state */ + intv.u32 = 0x0; + + /* Advance output position */ + if (SIZE_MAX - binding->skip_out < ipos) { + BHND_NV_LOG("output skip %u would overflow " + "%zu\n", binding->skip_out, ipos); + return (EINVAL); + } + + ipos += binding->skip_out; + } + } + + /* Did we iterate all bindings until hitting end of the variable + * definition? */ + BHND_NV_ASSERT(error != 0, ("loop terminated early")); + if (error != ENOENT) { + return (error); + } + + /* If marked IGNALL1 and all bits are set, treat variable as + * unavailable */ + if ((var->flags & BHND_NVRAM_VF_IGNALL1) && all_bits_set) + return (ENOENT); + + + /* Perform value coercion from our local representation */ + error = bhnd_nvram_val_init(&val, var->fmt, inp, ilen, var->type, + BHND_NVRAM_VAL_BORROW_DATA); + if (error) + return (error); + + error = bhnd_nvram_val_encode(&val, buf, len, otype); + + /* Clean up */ + bhnd_nvram_val_release(&val); + return (error); +} + +static const void * +bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type) +{ + /* Unsupported */ + return (NULL); +} + +static const char * +bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) +{ + const struct bhnd_nvram_vardefn *var; + + BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep")); + + var = SPROM_COOKIE_TO_NVRAM(cookiep); + BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep)); + + return (var->name); +} + +/** + * Initialize SPROM opcode evaluation state. + * + * @param state The opcode state to be initialized. + * @param layout The SPROM layout to be parsed by this instance. + * + * + * @retval 0 success + * @retval non-zero If initialization fails, a regular unix error code will be + * returned. + */ +static int +sprom_opcode_state_init(struct sprom_opcode_state *state, + const struct bhnd_sprom_layout *layout) +{ + memset(state, 0, sizeof(*state)); + + state->layout = layout; + state->input = layout->bindings; + state->var_state = SPROM_OPCODE_VAR_STATE_NONE; + + bit_set(state->revs, layout->rev); + + return (0); +} + +/** + * Reset SPROM opcode evaluation state; future evaluation will be performed + * starting at the first opcode. + * + * @param state The opcode state to be reset. + * + * @retval 0 success + * @retval non-zero If reset fails, a regular unix error code will be returned. + */ +static int +sprom_opcode_state_reset(struct sprom_opcode_state *state) +{ + return (sprom_opcode_state_init(state, state->layout)); +} + +/** + * Reset SPROM opcode evaluation state and seek to the @p indexed position. + * + * @param state The opcode state to be reset. + * @param indexed The indexed location to which we'll seek the opcode state. + */ +static int +sprom_opcode_state_seek(struct sprom_opcode_state *state, + struct sprom_opcode_idx *indexed) +{ + int error; + + BHND_NV_ASSERT(indexed->opcodes < state->layout->bindings_size, + ("index entry references invalid opcode position")); + + /* Reset state */ + if ((error = sprom_opcode_state_reset(state))) + return (error); + + /* Seek to the indexed sprom opcode offset */ + state->input = state->layout->bindings + indexed->opcodes; + + /* Restore the indexed sprom data offset and VID */ + state->offset = indexed->offset; + + /* Restore the indexed sprom variable ID */ + if ((error = sprom_opcode_set_var(state, indexed->vid))) + return (error); + + return (0); +} + +/** + * Set the current revision range for @p state. This also resets + * variable state. + * + * @param state The opcode state to update + * @param start The first revision in the range. + * @param end The last revision in the range. + * + * @retval 0 success + * @retval non-zero If updating @p state fails, a regular unix error code will + * be returned. + */ +static inline int +sprom_opcode_set_revs(struct sprom_opcode_state *state, uint8_t start, + uint8_t end) +{ + int error; + + /* Validate the revision range */ + if (start > SPROM_OP_REV_MAX || + end > SPROM_OP_REV_MAX || + end < start) + { + SPROM_OP_BAD(state, "invalid revision range: %hhu-%hhu\n", + start, end); + return (EINVAL); + } + + /* Clear variable state */ + if ((error = sprom_opcode_clear_var(state))) + return (error); + + /* Reset revision mask */ + memset(state->revs, 0x0, sizeof(state->revs)); + bit_nset(state->revs, start, end); + + return (0); +} + +/** + * Set the current variable's value mask for @p state. + * + * @param state The opcode state to update + * @param mask The mask to be set + * + * @retval 0 success + * @retval non-zero If updating @p state fails, a regular unix error code will + * be returned. + */ +static inline int +sprom_opcode_set_mask(struct sprom_opcode_state *state, uint32_t mask) +{ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "no open variable definition\n"); + return (EINVAL); + } + + state->var.mask = mask; + return (0); +} + +/** + * Set the current variable's value shift for @p state. + * + * @param state The opcode state to update + * @param shift The shift to be set + * + * @retval 0 success + * @retval non-zero If updating @p state fails, a regular unix error code will + * be returned. + */ +static inline int +sprom_opcode_set_shift(struct sprom_opcode_state *state, int8_t shift) +{ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "no open variable definition\n"); + return (EINVAL); + } + + state->var.shift = shift; + return (0); +} + +/** + * Register a new BIND/BINDN operation with @p state. + * + * @param state The opcode state to update. + * @param count The number of elements to be bound. + * @param skip_in The number of input elements to skip after each bind. + * @param skip_in_negative If true, the input skip should be subtracted from + * the current offset after each bind. If false, the input skip should be + * added. + * @param skip_out The number of output elements to skip after each bind. + * + * @retval 0 success + * @retval EINVAL if a variable definition is not open. + * @retval EINVAL if @p skip_in and @p count would trigger an overflow or + * underflow when applied to the current input offset. + * @retval ERANGE if @p skip_in would overflow uint32_t when multiplied by + * @p count and the scale value. + * @retval ERANGE if @p skip_out would overflow uint32_t when multiplied by + * @p count and the scale value. + * @retval non-zero If updating @p state otherwise fails, a regular unix error + * code will be returned. + */ +static inline int +sprom_opcode_set_bind(struct sprom_opcode_state *state, uint8_t count, + uint8_t skip_in, bool skip_in_negative, uint8_t skip_out) +{ + uint32_t iskip_total; + uint32_t iskip_scaled; + int error; + + /* Must have an open variable */ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "no open variable definition\n"); + SPROM_OP_BAD(state, "BIND outside of variable definition\n"); + return (EINVAL); + } + + /* Cannot overwite an existing bind definition */ + if (state->var.have_bind) { + SPROM_OP_BAD(state, "BIND overwrites existing definition\n"); + return (EINVAL); + } + + /* Must have a count of at least 1 */ + if (count == 0) { + SPROM_OP_BAD(state, "BIND with zero count\n"); + return (EINVAL); + } + + /* Scale skip_in by the current type width */ + iskip_scaled = skip_in; + if ((error = sprom_opcode_apply_scale(state, &iskip_scaled))) + return (error); + + /* Calculate total input bytes skipped: iskip_scaled * count) */ + if (iskip_scaled > 0 && UINT32_MAX / iskip_scaled < count) { + SPROM_OP_BAD(state, "skip_in %hhu would overflow", skip_in); + return (EINVAL); + } + + iskip_total = iskip_scaled * count; + + /* Verify that the skip_in value won't under/overflow the current + * input offset. */ + if (skip_in_negative) { + if (iskip_total > state->offset) { + SPROM_OP_BAD(state, "skip_in %hhu would underflow " + "offset %u\n", skip_in, state->offset); + return (EINVAL); + } + } else { + if (UINT32_MAX - iskip_total < state->offset) { + SPROM_OP_BAD(state, "skip_in %hhu would overflow " + "offset %u\n", skip_in, state->offset); + return (EINVAL); + } + } + + /* Set the actual count and skip values */ + state->var.have_bind = true; + state->var.bind.count = count; + state->var.bind.skip_in = skip_in; + state->var.bind.skip_out = skip_out; + + state->var.bind.skip_in_negative = skip_in_negative; + + /* Update total bind count for the current variable */ + state->var.bind_total++; + + return (0); +} + + +/** + * Apply and clear the current opcode bind state, if any. + * + * @param state The opcode state to update. + * + * @retval 0 success + * @retval non-zero If updating @p state otherwise fails, a regular unix error + * code will be returned. + */ +static int +sprom_opcode_flush_bind(struct sprom_opcode_state *state) +{ + int error; + uint32_t skip; + + /* Nothing to do? */ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN || + !state->var.have_bind) + return (0); + + /* Apply SPROM offset adjustment */ + if (state->var.bind.count > 0) { + skip = state->var.bind.skip_in * state->var.bind.count; + if ((error = sprom_opcode_apply_scale(state, &skip))) + return (error); + + if (state->var.bind.skip_in_negative) { + state->offset -= skip; + } else { + state->offset += skip; + } + } + + /* Clear bind state */ + memset(&state->var.bind, 0, sizeof(state->var.bind)); + state->var.have_bind = false; + + return (0); +} + +/** + * Set the current type to @p type, and reset type-specific + * stream state. + * + * @param state The opcode state to update. + * @param type The new type. + * + * @retval 0 success + * @retval EINVAL if @p vid is not a valid variable ID. + */ +static int +sprom_opcode_set_type(struct sprom_opcode_state *state, bhnd_nvram_type type) +{ + bhnd_nvram_type base_type; + size_t width; + uint32_t mask; + + /* Must have an open variable definition */ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "type set outside variable definition\n"); + return (EINVAL); + } + + /* Fetch type width for use as our scale value */ + width = bhnd_nvram_value_size(type, NULL, 0, 1); + if (width == 0) { + SPROM_OP_BAD(state, "unsupported variable-width type: %d\n", + type); + return (EINVAL); + } else if (width > UINT32_MAX) { + SPROM_OP_BAD(state, "invalid type width %zu for type: %d\n", + width, type); + return (EINVAL); + } + + /* Determine default mask value for the element type */ + base_type = bhnd_nvram_base_type(type); + switch (base_type) { + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_CHAR: + mask = UINT8_MAX; + break; + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_INT16: + mask = UINT16_MAX; + break; + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_INT32: + mask = UINT32_MAX; + break; + case BHND_NVRAM_TYPE_STRING: + /* fallthrough (unused by SPROM) */ + default: + SPROM_OP_BAD(state, "unsupported type: %d\n", type); + return (EINVAL); + } + + /* Update state */ + state->var.base_type = base_type; + state->var.mask = mask; + state->var.scale = (uint32_t)width; + + return (0); +} + +/** + * Clear current variable state, if any. + * + * @param state The opcode state to update. + */ +static int +sprom_opcode_clear_var(struct sprom_opcode_state *state) +{ + if (state->var_state == SPROM_OPCODE_VAR_STATE_NONE) + return (0); + + BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE, + ("incomplete variable definition")); + BHND_NV_ASSERT(!state->var.have_bind, ("stale bind state")); + + memset(&state->var, 0, sizeof(state->var)); + state->var_state = SPROM_OPCODE_VAR_STATE_NONE; + + return (0); +} + +/** + * Set the current variable's array element count to @p nelem. + * + * @param state The opcode state to update. + * @param nelem The new array length. + * + * @retval 0 success + * @retval EINVAL if no open variable definition exists. + * @retval EINVAL if @p nelem is zero. + * @retval ENXIO if @p nelem is greater than one, and the current variable does + * not have an array type. + * @retval ENXIO if @p nelem exceeds the array length of the NVRAM variable + * definition. + */ +static int +sprom_opcode_set_nelem(struct sprom_opcode_state *state, uint8_t nelem) +{ + const struct bhnd_nvram_vardefn *var; + + /* Must have a defined variable */ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "array length set without open variable " + "state"); + return (EINVAL); + } + + /* Locate the actual variable definition */ + if ((var = bhnd_nvram_get_vardefn(state->vid)) == NULL) { + SPROM_OP_BAD(state, "unknown variable ID: %zu\n", state->vid); + return (EINVAL); + } + + /* Must be greater than zero */ + if (nelem == 0) { + SPROM_OP_BAD(state, "invalid nelem: %hhu\n", nelem); + return (EINVAL); + } + + /* If the variable is not an array-typed value, the array length + * must be 1 */ + if (!bhnd_nvram_is_array_type(var->type) && nelem != 1) { + SPROM_OP_BAD(state, "nelem %hhu on non-array %zu\n", nelem, + state->vid); + return (ENXIO); + } + + /* Cannot exceed the variable's defined array length */ + if (nelem > var->nelem) { + SPROM_OP_BAD(state, "nelem %hhu exceeds %zu length %hhu\n", + nelem, state->vid, var->nelem); + return (ENXIO); + } + + /* Valid length; update state */ + state->var.nelem = nelem; + + return (0); +} + +/** + * Set the current variable ID to @p vid, and reset variable-specific + * stream state. + * + * @param state The opcode state to update. + * @param vid The new variable ID. + * + * @retval 0 success + * @retval EINVAL if @p vid is not a valid variable ID. + */ +static int +sprom_opcode_set_var(struct sprom_opcode_state *state, size_t vid) +{ + const struct bhnd_nvram_vardefn *var; + int error; + + BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_NONE, + ("overwrite of open variable definition")); + + /* Locate the variable definition */ + if ((var = bhnd_nvram_get_vardefn(vid)) == NULL) { + SPROM_OP_BAD(state, "unknown variable ID: %zu\n", vid); + return (EINVAL); + } + + /* Update vid and var state */ + state->vid = vid; + state->var_state = SPROM_OPCODE_VAR_STATE_OPEN; + + /* Initialize default variable record values */ + memset(&state->var, 0x0, sizeof(state->var)); + + /* Set initial base type */ + if ((error = sprom_opcode_set_type(state, var->type))) + return (error); + + /* Set default array length */ + if ((error = sprom_opcode_set_nelem(state, var->nelem))) + return (error); + + return (0); +} + +/** + * Mark the currently open variable definition as complete. + * + * @param state The opcode state to update. + * + * @retval 0 success + * @retval EINVAL if no incomplete open variable definition exists. + */ +static int +sprom_opcode_end_var(struct sprom_opcode_state *state) +{ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "no open variable definition\n"); + return (EINVAL); + } + + state->var_state = SPROM_OPCODE_VAR_STATE_DONE; + return (0); +} + +/** + * Apply the current scale to @p value. + * + * @param state The SPROM opcode state. + * @param[in,out] value The value to scale + * + * @retval 0 success + * @retval EINVAL if no open variable definition exists. + * @retval EINVAL if applying the current scale would overflow. + */ +static int +sprom_opcode_apply_scale(struct sprom_opcode_state *state, uint32_t *value) +{ + /* Must have a defined variable (and thus, scale) */ + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) { + SPROM_OP_BAD(state, "scaled value encoded without open " + "variable state"); + return (EINVAL); + } + + /* Applying the scale value must not overflow */ + if (UINT32_MAX / state->var.scale < *value) { + SPROM_OP_BAD(state, "cannot represent %" PRIu32 " * %" PRIu32 + "\n", *value, state->var.scale); + return (EINVAL); + } + + *value = (*value) * state->var.scale; + return (0); +} + +/** + * Read a SPROM_OP_DATA_* value from @p opcodes. + * + * @param state The SPROM opcode state. + * @param type The SROM_OP_DATA_* type to be read. + * @param opval On success, the 32bit data representation. If @p type is signed, + * the value will be appropriately sign extended and may be directly cast to + * int32_t. + * + * @retval 0 success + * @retval non-zero If reading the value otherwise fails, a regular unix error + * code will be returned. + */ +static int +sprom_opcode_read_opval32(struct sprom_opcode_state *state, uint8_t type, + uint32_t *opval) +{ + const uint8_t *p; + int error; + + p = state->input; + switch (type) { + case SPROM_OP_DATA_I8: + /* Convert to signed value first, then sign extend */ + *opval = (int32_t)(int8_t)(*p); + p += 1; + break; + case SPROM_OP_DATA_U8: + *opval = *p; + p += 1; + break; + case SPROM_OP_DATA_U8_SCALED: + *opval = *p; + + if ((error = sprom_opcode_apply_scale(state, opval))) + return (error); + + p += 1; + break; + case SPROM_OP_DATA_U16: + *opval = le16dec(p); + p += 2; + break; + case SPROM_OP_DATA_U32: + *opval = le32dec(p); + p += 4; + break; + default: + SPROM_OP_BAD(state, "unsupported data type: %hhu\n", type); + return (EINVAL); + } + + /* Update read address */ + state->input = p; + + return (0); +} + +/** + * Return true if our layout revision is currently defined by the SPROM + * opcode state. + * + * This may be used to test whether the current opcode stream state applies + * to the layout that we are actually parsing. + * + * A given opcode stream may cover multiple layout revisions, switching + * between them prior to defining a set of variables. + */ +static inline bool +sprom_opcode_matches_layout_rev(struct sprom_opcode_state *state) +{ + return (bit_test(state->revs, state->layout->rev)); +} + +/** + * When evaluating @p state and @p opcode, rewrite @p opcode and the current + * evaluation state, as required. + * + * If @p opcode is rewritten, it should be returned from + * sprom_opcode_step() instead of the opcode parsed from @p state's opcode + * stream. + * + * If @p opcode remains unmodified, then sprom_opcode_step() should proceed + * to standard evaluation. + */ +static int +sprom_opcode_rewrite_opcode(struct sprom_opcode_state *state, uint8_t *opcode) +{ + uint8_t op; + int error; + + op = SPROM_OPCODE_OP(*opcode); + switch (state->var_state) { + case SPROM_OPCODE_VAR_STATE_NONE: + /* No open variable definition */ + return (0); + + case SPROM_OPCODE_VAR_STATE_OPEN: + /* Open variable definition; check for implicit closure. */ + + /* + * If a variable definition contains no explicit bind + * instructions prior to closure, we must generate a DO_BIND + * instruction with count and skip values of 1. + */ + if (SPROM_OP_IS_VAR_END(op) && + state->var.bind_total == 0) + { + uint8_t count, skip_in, skip_out; + bool skip_in_negative; + + /* Create bind with skip_in/skip_out of 1, count of 1 */ + count = 1; + skip_in = 1; + skip_out = 1; + skip_in_negative = false; + + error = sprom_opcode_set_bind(state, count, skip_in, + skip_in_negative, skip_out); + if (error) + return (error); + + /* Return DO_BIND */ + *opcode = SPROM_OPCODE_DO_BIND | + (0 << SPROM_OP_BIND_SKIP_IN_SIGN) | + (1 << SPROM_OP_BIND_SKIP_IN_SHIFT) | + (1 << SPROM_OP_BIND_SKIP_OUT_SHIFT); + + return (0); + } + + /* + * If a variable is implicitly closed (e.g. by a new variable + * definition), we must generate a VAR_END instruction. + */ + if (SPROM_OP_IS_IMPLICIT_VAR_END(op)) { + /* Mark as complete */ + if ((error = sprom_opcode_end_var(state))) + return (error); + + /* Return VAR_END */ + *opcode = SPROM_OPCODE_VAR_END; + return (0); + } + break; + + + case SPROM_OPCODE_VAR_STATE_DONE: + /* Previously completed variable definition. Discard variable + * state */ + return (sprom_opcode_clear_var(state)); + } + + /* Nothing to do */ + return (0); +} + +/** + * Evaluate one opcode from @p state. + * + * @param state The opcode state to be evaluated. + * @param[out] opcode On success, the evaluated opcode + * + * @retval 0 success + * @retval ENOENT if EOF is reached + * @retval non-zero if evaluation otherwise fails, a regular unix error + * code will be returned. + */ +static int +sprom_opcode_step(struct sprom_opcode_state *state, uint8_t *opcode) +{ + int error; + + while (*state->input != SPROM_OPCODE_EOF) { + uint32_t val; + uint8_t op, rewrite, immd; + + /* Fetch opcode */ + *opcode = *state->input; + op = SPROM_OPCODE_OP(*opcode); + immd = SPROM_OPCODE_IMM(*opcode); + + /* Clear any existing bind state */ + if ((error = sprom_opcode_flush_bind(state))) + return (error); + + /* Insert local opcode based on current state? */ + rewrite = *opcode; + if ((error = sprom_opcode_rewrite_opcode(state, &rewrite))) + return (error); + + if (rewrite != *opcode) { + /* Provide rewritten opcode */ + *opcode = rewrite; + + /* We must keep evaluating until we hit a state + * applicable to the SPROM revision we're parsing */ + if (!sprom_opcode_matches_layout_rev(state)) + continue; + + return (0); + } + + /* Advance input */ + state->input++; + + switch (op) { + case SPROM_OPCODE_VAR_IMM: + if ((error = sprom_opcode_set_var(state, immd))) + return (error); + break; + + case SPROM_OPCODE_VAR_REL_IMM: + error = sprom_opcode_set_var(state, state->vid + immd); + if (error) + return (error); + break; + + case SPROM_OPCODE_VAR: + error = sprom_opcode_read_opval32(state, immd, &val); + if (error) + return (error); + + if ((error = sprom_opcode_set_var(state, val))) + return (error); + + break; + + case SPROM_OPCODE_VAR_END: + if ((error = sprom_opcode_end_var(state))) + return (error); + break; + + case SPROM_OPCODE_NELEM: + immd = *state->input; + if ((error = sprom_opcode_set_nelem(state, immd))) + return (error); + + state->input++; + break; + + case SPROM_OPCODE_DO_BIND: + case SPROM_OPCODE_DO_BINDN: { + uint8_t count, skip_in, skip_out; + bool skip_in_negative; + + /* Fetch skip arguments */ + skip_in = (immd & SPROM_OP_BIND_SKIP_IN_MASK) >> + SPROM_OP_BIND_SKIP_IN_SHIFT; + + skip_in_negative = + ((immd & SPROM_OP_BIND_SKIP_IN_SIGN) != 0); + + skip_out = (immd & SPROM_OP_BIND_SKIP_OUT_MASK) >> + SPROM_OP_BIND_SKIP_OUT_SHIFT; + + /* Fetch count argument (if any) */ + if (op == SPROM_OPCODE_DO_BINDN) { + /* Count is provided as trailing U8 */ + count = *state->input; + state->input++; + } else { + count = 1; + } + + /* Set BIND state */ + error = sprom_opcode_set_bind(state, count, skip_in, + skip_in_negative, skip_out); + if (error) + return (error); + + break; + } + case SPROM_OPCODE_DO_BINDN_IMM: { + uint8_t count, skip_in, skip_out; + bool skip_in_negative; + + /* Implicit skip_in/skip_out of 1, count encoded as immd + * value */ + count = immd; + skip_in = 1; + skip_out = 1; + skip_in_negative = false; + + error = sprom_opcode_set_bind(state, count, skip_in, + skip_in_negative, skip_out); + if (error) + return (error); + break; + } + + case SPROM_OPCODE_REV_IMM: + if ((error = sprom_opcode_set_revs(state, immd, immd))) + return (error); + break; + + case SPROM_OPCODE_REV_RANGE: { + uint8_t range; + uint8_t rstart, rend; + + /* Revision range is encoded in next byte, as + * { uint8_t start:4, uint8_t end:4 } */ + range = *state->input; + rstart = (range & SPROM_OP_REV_START_MASK) >> + SPROM_OP_REV_START_SHIFT; + rend = (range & SPROM_OP_REV_END_MASK) >> + SPROM_OP_REV_END_SHIFT; + + /* Update revision bitmask */ + error = sprom_opcode_set_revs(state, rstart, rend); + if (error) + return (error); + + /* Advance input */ + state->input++; + break; + } + case SPROM_OPCODE_MASK_IMM: + if ((error = sprom_opcode_set_mask(state, immd))) + return (error); + break; + + case SPROM_OPCODE_MASK: + error = sprom_opcode_read_opval32(state, immd, &val); + if (error) + return (error); + + if ((error = sprom_opcode_set_mask(state, val))) + return (error); + break; + + case SPROM_OPCODE_SHIFT_IMM: + if ((error = sprom_opcode_set_shift(state, immd * 2))) + return (error); + break; + + case SPROM_OPCODE_SHIFT: { + int8_t shift; + + if (immd == SPROM_OP_DATA_I8) { + shift = (int8_t)(*state->input); + } else if (immd == SPROM_OP_DATA_U8) { + val = *state->input; + if (val > INT8_MAX) { + SPROM_OP_BAD(state, "invalid shift " + "value: %#x\n", val); + } + + shift = val; + } else { + SPROM_OP_BAD(state, "unsupported shift data " + "type: %#hhx\n", immd); + return (EINVAL); + } + + if ((error = sprom_opcode_set_shift(state, shift))) + return (error); + + state->input++; + break; + } + case SPROM_OPCODE_OFFSET_REL_IMM: + /* Fetch unscaled relative offset */ + val = immd; + + /* Apply scale */ + if ((error = sprom_opcode_apply_scale(state, &val))) + return (error); + + /* Adding val must not overflow our offset */ + if (UINT32_MAX - state->offset < val) { + BHND_NV_LOG("offset out of range\n"); + return (EINVAL); + } + + /* Adjust offset */ + state->offset += val; + break; + case SPROM_OPCODE_OFFSET: + error = sprom_opcode_read_opval32(state, immd, &val); + if (error) + return (error); + + state->offset = val; + break; + + case SPROM_OPCODE_TYPE: + /* Type follows as U8 */ + immd = *state->input; + state->input++; + + /* fall through */ + case SPROM_OPCODE_TYPE_IMM: + switch (immd) { + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_INT16: + case BHND_NVRAM_TYPE_INT32: + case BHND_NVRAM_TYPE_INT64: + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_STRING: + error = sprom_opcode_set_type(state, + (bhnd_nvram_type)immd); + if (error) + return (error); + break; + default: + BHND_NV_LOG("unrecognized type %#hhx\n", immd); + return (EINVAL); + } + break; + + default: + BHND_NV_LOG("unrecognized opcode %#hhx\n", *opcode); + return (EINVAL); + } + + /* We must keep evaluating until we hit a state applicable to + * the SPROM revision we're parsing */ + if (sprom_opcode_matches_layout_rev(state)) + return (0); + } + + /* End of opcode stream */ + return (ENOENT); +} + +/** + * Reset SPROM opcode evaluation state, seek to the @p indexed position, + * and perform complete evaluation of the variable's opcodes. + * + * @param state The opcode state to be to be evaluated. + * @param indexed The indexed variable location. + * + * @retval 0 success + * @retval non-zero If evaluation fails, a regular unix error code will be + * returned. + */ +static int +sprom_opcode_parse_var(struct sprom_opcode_state *state, + struct sprom_opcode_idx *indexed) +{ + uint8_t opcode; + int error; + + /* Seek to entry */ + if ((error = sprom_opcode_state_seek(state, indexed))) + return (error); + + /* Parse full variable definition */ + while ((error = sprom_opcode_step(state, &opcode)) == 0) { + /* Iterate until VAR_END */ + if (SPROM_OPCODE_OP(opcode) != SPROM_OPCODE_VAR_END) + continue; + + BHND_NV_ASSERT(state->var_state == SPROM_OPCODE_VAR_STATE_DONE, + ("incomplete variable definition")); + + return (0); + } + + /* Error parsing definition */ + return (error); +} + +/** + * Evaluate @p state until the next variable definition is found. + * + * @param state The opcode state to be evaluated. + * + * @retval 0 success + * @retval ENOENT if no additional variable definitions are available. + * @retval non-zero if evaluation otherwise fails, a regular unix error + * code will be returned. + */ +static int +sprom_opcode_next_var(struct sprom_opcode_state *state) +{ + uint8_t opcode; + int error; + + /* Step until we hit a variable opcode */ + while ((error = sprom_opcode_step(state, &opcode)) == 0) { + switch (SPROM_OPCODE_OP(opcode)) { + case SPROM_OPCODE_VAR: + case SPROM_OPCODE_VAR_IMM: + case SPROM_OPCODE_VAR_REL_IMM: + BHND_NV_ASSERT( + state->var_state == SPROM_OPCODE_VAR_STATE_OPEN, + ("missing variable definition")); + + return (0); + default: + continue; + } + } + + /* Reached EOF, or evaluation failed */ + return (error); +} + +/** + * Evaluate @p state until the next binding for the current variable definition + * is found. + * + * @param state The opcode state to be evaluated. + * + * @retval 0 success + * @retval ENOENT if no additional binding opcodes are found prior to reaching + * a new variable definition, or the end of @p state's binding opcodes. + * @retval non-zero if evaluation otherwise fails, a regular unix error + * code will be returned. + */ +static int +sprom_opcode_next_binding(struct sprom_opcode_state *state) +{ + uint8_t opcode; + int error; + + if (state->var_state != SPROM_OPCODE_VAR_STATE_OPEN) + return (EINVAL); + + /* Step until we hit a bind opcode, or a new variable */ + while ((error = sprom_opcode_step(state, &opcode)) == 0) { + switch (SPROM_OPCODE_OP(opcode)) { + case SPROM_OPCODE_DO_BIND: + case SPROM_OPCODE_DO_BINDN: + case SPROM_OPCODE_DO_BINDN_IMM: + /* Found next bind */ + BHND_NV_ASSERT( + state->var_state == SPROM_OPCODE_VAR_STATE_OPEN, + ("missing variable definition")); + BHND_NV_ASSERT(state->var.have_bind, ("missing bind")); + + return (0); + + case SPROM_OPCODE_VAR_END: + /* No further binding opcodes */ + BHND_NV_ASSERT( + state->var_state == SPROM_OPCODE_VAR_STATE_DONE, + ("variable definition still available")); + return (ENOENT); + } + } + + /* Not found, or evaluation failed */ + return (error); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h b/sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h new file mode 100644 index 0000000000000..82a51b13911d7 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h @@ -0,0 +1,150 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/bitstring.h> +#else +#include <bitstring.h> +#endif + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_datavar.h" +#include "bhnd_nvram_io.h" + +/** The maximum number of array elements encoded in a single SPROM variable */ +#define SPROM_ARRAY_MAXLEN 12 + +/** + * SPROM opcode per-bind evaluation state. + */ +struct sprom_opcode_bind { + uint8_t count; + uint32_t skip_in; /**< input element skips */ + bool skip_in_negative; /**< skip_in should be subtracted */ + uint32_t skip_out; /**< output element skip */ +}; + +/** + * SPROM opcode per-variable evaluation state. + */ +struct sprom_opcode_var { + uint8_t nelem; /**< variable array length */ + uint32_t mask; /**< current bind input mask */ + int8_t shift; /**< current bind input shift */ + bhnd_nvram_type base_type; /**< current bind input type */ + uint32_t scale; /**< current scale to apply to scaled encodings */ + struct sprom_opcode_bind bind; /**< current bind state */ + bool have_bind; /**< if bind state is defined */ + size_t bind_total; /**< total count of bind operations performed */ +}; + +/** + * SPROM opcode variable definition states. + * + * Ordered to support inequality comparisons + * (e.g. >= SPROM_OPCODE_VAR_STATE_OPEN) + */ +typedef enum { + SPROM_OPCODE_VAR_STATE_NONE = 1, /**< no variable entry available */ + SPROM_OPCODE_VAR_STATE_OPEN = 2, /**< currently parsing a variable entry */ + SPROM_OPCODE_VAR_STATE_DONE = 3 /**< full variable entry has been parsed */ +} sprom_opcode_var_state; + +/** + * SPROM opcode evaluation state + */ +struct sprom_opcode_state { + const struct bhnd_sprom_layout *layout; /**< SPROM layout */ + + /** Current SPROM revision range */ + bitstr_t bit_decl(revs, SPROM_OP_REV_MAX); + + const uint8_t *input; /**< opcode input position */ + + /* State preserved across variable definitions */ + uint32_t offset; /**< SPROM offset */ + size_t vid; /**< Variable ID */ + + /* State reset after end of each variable definition */ + struct sprom_opcode_var var; /**< variable record (if any) */ + sprom_opcode_var_state var_state; /**< variable record state */ +}; + +/** + * SPROM opcode variable index entry + */ +struct sprom_opcode_idx { + uint16_t vid; /**< SPROM variable ID */ + uint16_t offset; /**< SPROM input offset */ + uint16_t opcodes; /**< SPROM opcode offset */ +}; + +/** + * SPROM value storage. + * + * Sufficient for representing the native encoding of any defined SPROM + * variable. + */ +union bhnd_nvram_sprom_storage { + uint8_t u8[SPROM_ARRAY_MAXLEN]; + uint16_t u16[SPROM_ARRAY_MAXLEN]; + uint32_t u32[SPROM_ARRAY_MAXLEN]; + int8_t i8[SPROM_ARRAY_MAXLEN]; + int16_t i16[SPROM_ARRAY_MAXLEN]; + int32_t i32[SPROM_ARRAY_MAXLEN]; + char ch[SPROM_ARRAY_MAXLEN]; +}; + +/** + * SPROM common integer value representation. + */ +union bhnd_nvram_sprom_intv { + uint32_t u32; + int32_t s32; +}; + +/** + * SPROM data class instance state. + */ +struct bhnd_nvram_sprom { + struct bhnd_nvram_data nv; /**< common instance state */ + struct bhnd_nvram_io *data; /**< backing SPROM image */ + const struct bhnd_sprom_layout *layout; /**< layout definition */ + struct sprom_opcode_state state; /**< opcode eval state */ + struct sprom_opcode_idx *idx; /**< opcode index entries */ + size_t num_idx; /**< opcode index entry count */ +}; + +#endif /* _BHND_NVRAM_BHND_NVRAM_SPROMVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c b/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c new file mode 100644 index 0000000000000..6d1a5700df407 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c @@ -0,0 +1,662 @@ +/*- + * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#ifdef _KERNEL +#include <sys/param.h> +#include <sys/ctype.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#else /* !_KERNEL */ +#include <ctype.h> +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_datavar.h" + +#include "bhnd_nvram_data_tlvreg.h" + +/* + * CFE TLV NVRAM data class. + * + * The CFE-defined TLV NVRAM format is used on the WGT634U. + */ + +struct bhnd_nvram_tlv { + struct bhnd_nvram_data nv; /**< common instance state */ + struct bhnd_nvram_io *data; /**< backing buffer */ + size_t count; /**< variable count */ +}; + +BHND_NVRAM_DATA_CLASS_DEFN(tlv, "WGT634U", sizeof(struct bhnd_nvram_tlv)) + +/** Minimal TLV_ENV record header */ +struct bhnd_nvram_tlv_env_hdr { + uint8_t tag; + uint8_t size; +} __packed; + +/** Minimal TLV_ENV record */ +struct bhnd_nvram_tlv_env { + struct bhnd_nvram_tlv_env_hdr hdr; + uint8_t flags; + char envp[]; +} __packed; + +/* Return the length in bytes of an TLV_ENV's envp data */ +#define NVRAM_TLV_ENVP_DATA_LEN(_env) \ + (((_env)->hdr.size < sizeof((_env)->flags)) ? 0 : \ + ((_env)->hdr.size - sizeof((_env)->flags))) + + +static int bhnd_nvram_tlv_parse_size( + struct bhnd_nvram_io *io, + size_t *size); + +static int bhnd_nvram_tlv_next_record( + struct bhnd_nvram_io *io, + size_t *next, size_t *offset, + uint8_t *tag); + +static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_next_env( + struct bhnd_nvram_tlv *tlv, + size_t *next, void **cookiep); + +static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_get_env( + struct bhnd_nvram_tlv *tlv, + void *cookiep); + +static void *bhnd_nvram_tlv_to_cookie( + struct bhnd_nvram_tlv *tlv, + size_t io_offset); +static size_t bhnd_nvram_tlv_to_offset( + struct bhnd_nvram_tlv *tlv, + void *cookiep); + +static int +bhnd_nvram_tlv_probe(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_tlv_env ident; + size_t nbytes; + int error; + + nbytes = bhnd_nvram_io_getsize(io); + + /* Handle what might be an empty TLV image */ + if (nbytes < sizeof(ident)) { + uint8_t tag; + + /* Fetch just the first tag */ + error = bhnd_nvram_io_read(io, 0x0, &tag, sizeof(tag)); + if (error) + return (error); + + /* This *could* be an empty TLV image, but all we're + * testing for here is a single 0x0 byte followed by EOF */ + if (tag == NVRAM_TLV_TYPE_END) + return (BHND_NVRAM_DATA_PROBE_MAYBE); + + return (ENXIO); + } + + /* Otherwise, look at the initial header for a valid TLV ENV tag, + * plus one byte of the entry data */ + error = bhnd_nvram_io_read(io, 0x0, &ident, + sizeof(ident) + sizeof(ident.envp[0])); + if (error) + return (error); + + /* First entry should be a variable record (which we statically + * assert as being defined to use a single byte size field) */ + if (ident.hdr.tag != NVRAM_TLV_TYPE_ENV) + return (ENXIO); + + _Static_assert(NVRAM_TLV_TYPE_ENV & NVRAM_TLV_TF_U8_LEN, + "TYPE_ENV is not a U8-sized field"); + + /* The entry must be at least 3 characters ('x=\0') in length */ + if (ident.hdr.size < 3) + return (ENXIO); + + /* The first character should be a valid key char (alpha) */ + if (!bhnd_nv_isalpha(ident.envp[0])) + return (ENXIO); + + return (BHND_NVRAM_DATA_PROBE_DEFAULT); +} + +/** + * Initialize @p tlv with the provided NVRAM TLV data mapped by @p src. + * + * @param tlv A newly allocated data instance. + */ +static int +bhnd_nvram_tlv_init(struct bhnd_nvram_tlv *tlv, struct bhnd_nvram_io *src) +{ + struct bhnd_nvram_tlv_env *env; + size_t size; + size_t next; + int error; + + BHND_NV_ASSERT(tlv->data == NULL, ("tlv data already initialized")); + + /* Determine the actual size of the TLV source data */ + if ((error = bhnd_nvram_tlv_parse_size(src, &size))) + return (error); + + /* Copy to our own internal buffer */ + if ((tlv->data = bhnd_nvram_iobuf_copy_range(src, 0x0, size)) == NULL) + return (ENOMEM); + + /* Initialize our backing buffer */ + tlv->count = 0; + next = 0; + while ((env = bhnd_nvram_tlv_next_env(tlv, &next, NULL)) != NULL) { + size_t env_len; + size_t name_len; + + /* TLV_ENV data must not be empty */ + env_len = NVRAM_TLV_ENVP_DATA_LEN(env); + if (env_len == 0) { + BHND_NV_LOG("cannot parse zero-length TLV_ENV record " + "data\n"); + return (EINVAL); + } + + /* Parse the key=value string, and then replace the '=' + * delimiter with '\0' to allow us to provide direct + * name pointers from our backing buffer */ + error = bhnd_nvram_parse_env(env->envp, env_len, '=', NULL, + &name_len, NULL, NULL); + if (error) { + BHND_NV_LOG("error parsing TLV_ENV data: %d\n", error); + return (error); + } + + /* Replace '=' with '\0' */ + *(env->envp + name_len) = '\0'; + + /* Add to variable count */ + tlv->count++; + }; + + return (0); +} + +static int +bhnd_nvram_tlv_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) +{ + + struct bhnd_nvram_tlv *tlv; + int error; + + /* Allocate and initialize the TLV data instance */ + tlv = (struct bhnd_nvram_tlv *)nv; + + /* Parse the TLV input data and initialize our backing + * data representation */ + if ((error = bhnd_nvram_tlv_init(tlv, io))) { + bhnd_nvram_tlv_free(nv); + return (error); + } + + return (0); +} + +static void +bhnd_nvram_tlv_free(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv; + if (tlv->data != NULL) + bhnd_nvram_io_free(tlv->data); +} + +size_t +bhnd_nvram_tlv_count(struct bhnd_nvram_data *nv) +{ + struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv; + return (tlv->count); +} + +static int +bhnd_nvram_tlv_size(struct bhnd_nvram_data *nv, size_t *size) +{ + /* Let the serialization implementation calculate the length */ + return (bhnd_nvram_data_serialize(nv, NULL, size)); +} + +static int +bhnd_nvram_tlv_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len) +{ + struct bhnd_nvram_tlv *tlv; + size_t limit; + size_t next; + uint8_t tag; + int error; + + tlv = (struct bhnd_nvram_tlv *)nv; + + /* Save the buffer capacity */ + if (buf == NULL) + limit = 0; + else + limit = *len; + + /* Write all of our TLV records to the output buffer (or just + * calculate the buffer size that would be required) */ + next = 0; + do { + struct bhnd_nvram_tlv_env *env; + uint8_t *p; + size_t name_len; + size_t rec_offset, rec_size; + + /* Parse the TLV record */ + error = bhnd_nvram_tlv_next_record(tlv->data, &next, + &rec_offset, &tag); + if (error) + return (error); + + rec_size = next - rec_offset; + + /* Calculate our output pointer */ + if (rec_offset > limit || limit - rec_offset < rec_size) { + /* buffer is full; cannot write */ + p = NULL; + } else { + p = (uint8_t *)buf + rec_offset; + } + + /* If not writing, nothing further to do for this record */ + if (p == NULL) + continue; + + /* Copy to the output buffer */ + error = bhnd_nvram_io_read(tlv->data, rec_offset, p, rec_size); + if (error) + return (error); + + /* All further processing is TLV_ENV-specific */ + if (tag != NVRAM_TLV_TYPE_ENV) + continue; + + /* Restore the original key=value format, rewriting '\0' + * delimiter back to '=' */ + env = (struct bhnd_nvram_tlv_env *)p; + name_len = strlen(env->envp); /* skip variable name */ + *(env->envp + name_len) = '='; /* set '=' */ + } while (tag != NVRAM_TLV_TYPE_END); + + /* The 'next' offset should now point at EOF, and represents + * the total length of the serialized output. */ + *len = next; + + if (buf != NULL && limit < *len) + return (ENOMEM); + + return (0); +} + +static uint32_t +bhnd_nvram_tlv_caps(struct bhnd_nvram_data *nv) +{ + return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); +} + +static const char * +bhnd_nvram_tlv_next(struct bhnd_nvram_data *nv, void **cookiep) +{ + struct bhnd_nvram_tlv *tlv; + struct bhnd_nvram_tlv_env *env; + size_t io_offset; + + tlv = (struct bhnd_nvram_tlv *)nv; + + /* Seek past the TLV_ENV record referenced by cookiep */ + io_offset = bhnd_nvram_tlv_to_offset(tlv, *cookiep); + if (bhnd_nvram_tlv_next_env(tlv, &io_offset, NULL) == NULL) + BHND_NV_PANIC("invalid cookiep: %p\n", cookiep); + + /* Fetch the next TLV_ENV record */ + if ((env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep)) == NULL) { + /* No remaining ENV records */ + return (NULL); + } + + /* Return the NUL terminated name */ + return (env->envp); +} + +static void * +bhnd_nvram_tlv_find(struct bhnd_nvram_data *nv, const char *name) +{ + return (bhnd_nvram_data_generic_find(nv, name)); +} + +static int +bhnd_nvram_tlv_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, + size_t *len, bhnd_nvram_type type) +{ + return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); +} + +static const void * +bhnd_nvram_tlv_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type) +{ + struct bhnd_nvram_tlv *tlv; + struct bhnd_nvram_tlv_env *env; + const char *val; + int error; + + tlv = (struct bhnd_nvram_tlv *)nv; + + /* Fetch pointer to the TLV_ENV record */ + if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL) + BHND_NV_PANIC("invalid cookiep: %p", cookiep); + + /* Parse value pointer and length from key\0value data */ + error = bhnd_nvram_parse_env(env->envp, NVRAM_TLV_ENVP_DATA_LEN(env), + '\0', NULL, NULL, &val, len); + if (error) + BHND_NV_PANIC("unexpected error parsing '%s'", env->envp); + + /* Type is always CSTR */ + *type = BHND_NVRAM_TYPE_STRING; + + return (val); +} + +static const char * +bhnd_nvram_tlv_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) +{ + struct bhnd_nvram_tlv *tlv; + const struct bhnd_nvram_tlv_env *env; + + tlv = (struct bhnd_nvram_tlv *)nv; + + /* Fetch pointer to the TLV_ENV record */ + if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL) + BHND_NV_PANIC("invalid cookiep: %p", cookiep); + + /* Return name pointer */ + return (&env->envp[0]); +} + +/** + * Iterate over the records starting at @p next, returning the parsed + * record's @p tag, @p size, and @p offset. + * + * @param io The I/O context to parse. + * @param[in,out] next The next offset to be parsed, or 0x0 + * to begin parsing. Upon successful + * return, will be set to the offset of the + * next record (or EOF, if + * NVRAM_TLV_TYPE_END was parsed). + * @param[out] offset The record's value offset. + * @param[out] tag The record's tag. + * + * @retval 0 success + * @retval EINVAL if parsing @p io as TLV fails. + * @retval non-zero if reading @p io otherwise fails, a regular unix error + * code will be returned. + */ +static int +bhnd_nvram_tlv_next_record(struct bhnd_nvram_io *io, size_t *next, size_t + *offset, uint8_t *tag) +{ + size_t io_offset, io_size; + uint16_t parsed_len; + uint8_t len_hdr[2]; + int error; + + io_offset = *next; + io_size = bhnd_nvram_io_getsize(io); + + /* Save the record offset */ + if (offset != NULL) + *offset = io_offset; + + /* Fetch initial tag */ + error = bhnd_nvram_io_read(io, io_offset, tag, sizeof(*tag)); + if (error) + return (error); + io_offset++; + + /* EOF */ + if (*tag == NVRAM_TLV_TYPE_END) { + *next = io_offset; + return (0); + } + + /* Read length field */ + if (*tag & NVRAM_TLV_TF_U8_LEN) { + error = bhnd_nvram_io_read(io, io_offset, &len_hdr, + sizeof(len_hdr[0])); + if (error) { + BHND_NV_LOG("error reading TLV record size: %d\n", + error); + return (error); + } + + parsed_len = len_hdr[0]; + io_offset++; + } else { + error = bhnd_nvram_io_read(io, io_offset, &len_hdr, + sizeof(len_hdr)); + if (error) { + BHND_NV_LOG("error reading 16-bit TLV record " + "size: %d\n", error); + return (error); + } + + parsed_len = (len_hdr[0] << 8) | len_hdr[1]; + io_offset += 2; + } + + /* Advance to next record */ + if (parsed_len > io_size || io_size - parsed_len < io_offset) { + /* Hit early EOF */ + BHND_NV_LOG("TLV record length %hu truncated by input " + "size of %zu\n", parsed_len, io_size); + return (EINVAL); + } + + *next = io_offset + parsed_len; + + /* Valid record found */ + return (0); +} + +/** + * Parse the TLV data in @p io to determine the total size of the TLV + * data mapped by @p io (which may be less than the size of @p io). + */ +static int +bhnd_nvram_tlv_parse_size(struct bhnd_nvram_io *io, size_t *size) +{ + size_t next; + uint8_t tag; + int error; + + /* We have to perform a minimal parse to determine the actual length */ + next = 0x0; + *size = 0x0; + + /* Iterate over the input until we hit END tag or the read fails */ + do { + error = bhnd_nvram_tlv_next_record(io, &next, NULL, &tag); + if (error) + return (error); + } while (tag != NVRAM_TLV_TYPE_END); + + /* Offset should now point to EOF */ + BHND_NV_ASSERT(next <= bhnd_nvram_io_getsize(io), + ("parse returned invalid EOF offset")); + + *size = next; + return (0); +} + +/** + * Iterate over the records in @p tlv, returning a pointer to the next + * NVRAM_TLV_TYPE_ENV record, or NULL if EOF is reached. + * + * @param tlv The TLV instance. + * @param[in,out] next The next offset to be parsed, or 0x0 + * to begin parsing. Upon successful + * return, will be set to the offset of the + * next record. + */ +static struct bhnd_nvram_tlv_env * +bhnd_nvram_tlv_next_env(struct bhnd_nvram_tlv *tlv, size_t *next, + void **cookiep) +{ + uint8_t tag; + int error; + + /* Find the next TLV_ENV record, starting at @p next */ + do { + void *c; + size_t offset; + + /* Fetch the next TLV record */ + error = bhnd_nvram_tlv_next_record(tlv->data, next, &offset, + &tag); + if (error) { + BHND_NV_LOG("unexpected error in next_record(): %d\n", + error); + return (NULL); + } + + /* Only interested in ENV records */ + if (tag != NVRAM_TLV_TYPE_ENV) + continue; + + /* Map and return TLV_ENV record pointer */ + c = bhnd_nvram_tlv_to_cookie(tlv, offset); + + /* Provide the cookiep value for the returned record */ + if (cookiep != NULL) + *cookiep = c; + + return (bhnd_nvram_tlv_get_env(tlv, c)); + } while (tag != NVRAM_TLV_TYPE_END); + + /* No remaining ENV records */ + return (NULL); +} + +/** + * Return a pointer to the TLV_ENV record for @p cookiep, or NULL + * if none vailable. + */ +static struct bhnd_nvram_tlv_env * +bhnd_nvram_tlv_get_env(struct bhnd_nvram_tlv *tlv, void *cookiep) +{ + struct bhnd_nvram_tlv_env *env; + void *ptr; + size_t navail; + size_t io_offset, io_size; + int error; + + io_size = bhnd_nvram_io_getsize(tlv->data); + io_offset = bhnd_nvram_tlv_to_offset(tlv, cookiep); + + /* At EOF? */ + if (io_offset == io_size) + return (NULL); + + /* Fetch non-const pointer to the record entry */ + error = bhnd_nvram_io_write_ptr(tlv->data, io_offset, &ptr, + sizeof(env->hdr), &navail); + if (error) { + /* Should never occur with a valid cookiep */ + BHND_NV_LOG("error mapping record for cookiep: %d\n", error); + return (NULL); + } + + /* Validate the record pointer */ + env = ptr; + if (env->hdr.tag != NVRAM_TLV_TYPE_ENV) { + /* Should never occur with a valid cookiep */ + BHND_NV_LOG("non-ENV record mapped for %p\n", cookiep); + return (NULL); + } + + /* Is the required variable name data is mapped? */ + if (navail < sizeof(struct bhnd_nvram_tlv_env_hdr) + env->hdr.size || + env->hdr.size == sizeof(env->flags)) + { + /* Should never occur with a valid cookiep */ + BHND_NV_LOG("TLV_ENV variable data not mapped for %p\n", + cookiep); + return (NULL); + } + + return (env); +} + +/** + * Return a cookiep for the given I/O offset. + */ +static void * +bhnd_nvram_tlv_to_cookie(struct bhnd_nvram_tlv *tlv, size_t io_offset) +{ + BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(tlv->data), + ("io_offset %zu out-of-range", io_offset)); + BHND_NV_ASSERT(io_offset < UINTPTR_MAX, + ("io_offset %#zx exceeds UINTPTR_MAX", io_offset)); + + return ((void *)(uintptr_t)(io_offset)); +} + +/* Convert a cookiep back to an I/O offset */ +static size_t +bhnd_nvram_tlv_to_offset(struct bhnd_nvram_tlv *tlv, void *cookiep) +{ + size_t io_size; + uintptr_t cval; + + io_size = bhnd_nvram_io_getsize(tlv->data); + cval = (uintptr_t)cookiep; + + BHND_NV_ASSERT(cval < SIZE_MAX, ("cookie > SIZE_MAX)")); + BHND_NV_ASSERT(cval <= io_size, ("cookie > io_size)")); + + return ((size_t)cval); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_parserreg.h b/sys/dev/bhnd/nvram/bhnd_nvram_data_tlvreg.h index defcf90d1d4bd..0daa745aa565b 100644 --- a/sys/dev/bhnd/nvram/bhnd_nvram_parserreg.h +++ b/sys/dev/bhnd/nvram/bhnd_nvram_data_tlvreg.h @@ -29,47 +29,12 @@ * $FreeBSD$ */ -#ifndef _BHND_NVRAM_BHND_NVRAM_PARSERREG_H_ -#define _BHND_NVRAM_BHND_NVRAM_PARSERREG_H_ - - - -#define NVRAM_GET_BITS(_value, _field) \ - ((_value & _field ## _MASK) >> _field ## _SHIFT) - -/* NVRAM header fields */ -#define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */ -#define NVRAM_VERSION 1 - -#define NVRAM_CRC_SKIP 9 /* skip magic, size, and crc8 */ - -#define NVRAM_CFG0_CRC_MASK 0x000000FF -#define NVRAM_CFG0_CRC_SHIFT 0 -#define NVRAM_CFG0_VER_MASK 0x0000FF00 -#define NVRAM_CFG0_VER_SHIFT 8 -#define NVRAM_CFG0_SDRAM_INIT_MASK 0xFFFF0000 -#define NVRAM_CFG0_SDRAM_INIT_SHIFT 16 -#define NVRAM_CFG0_SDRAM_INIT_VAR "sdram_init" -#define NVRAM_CFG0_SDRAM_INIT_FMT "0x%04x" - -#define NVRAM_CFG1_SDRAM_CFG_MASK 0x0000FFFF -#define NVRAM_CFG1_SDRAM_CFG_SHIFT 0 -#define NVRAM_CFG1_SDRAM_CFG_VAR "sdram_config" -#define NVRAM_CFG1_SDRAM_CFG_FMT "0x%04x" - -#define NVRAM_CFG1_SDRAM_REFRESH_MASK 0xFFFF0000 -#define NVRAM_CFG1_SDRAM_REFRESH_SHIFT 16 -#define NVRAM_CFG1_SDRAM_REFRESH_VAR "sdram_refresh" -#define NVRAM_CFG1_SDRAM_REFRESH_FMT "0x%04x" - -#define NVRAM_SDRAM_NCDL_MASK UINT32_MAX -#define NVRAM_SDRAM_NCDL_SHIFT 0 -#define NVRAM_SDRAM_NCDL_VAR "sdram_ncdl" -#define NVRAM_SDRAM_NCDL_FMT "0x%08x" +#ifndef _BHND_NVRAM_BHND_NVRAM_TLVREG_H_ +#define _BHND_NVRAM_BHND_NVRAM_TLVREG_H_ /* WGT634U-specific TLV encoding */ #define NVRAM_TLV_TF_U8_LEN 0x01 /**< type has 8-bit length */ #define NVRAM_TLV_TYPE_END 0x00 /**< end of table */ #define NVRAM_TLV_TYPE_ENV 0x01 /**< variable record */ -#endif /* _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_ */ +#endif /* _BHND_NVRAM_BHND_NVRAM_TLVREG_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h b/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h new file mode 100644 index 0000000000000..50a9f44338f2b --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h @@ -0,0 +1,188 @@ +/*- + * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/param.h> +#include <sys/linker_set.h> +#include <sys/refcount.h> + +#include "bhnd_nvram_io.h" + +#include "bhnd_nvram_data.h" + +/** Registered NVRAM parser class instances. */ +SET_DECLARE(bhnd_nvram_data_class_set, bhnd_nvram_data_class_t); + +void *bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv, + const char *name); +int bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, + void *cookiep, void *outp, size_t *olen, bhnd_nvram_type otype); + +/** @see bhnd_nvram_data_class_desc() */ +typedef const char *(bhnd_nvram_data_op_class_desc)(void); + +/** @see bhnd_nvram_data_probe() */ +typedef int (bhnd_nvram_data_op_probe)(struct bhnd_nvram_io *io); + +/** @see bhnd_nvram_data_new() */ +typedef int (bhnd_nvram_data_op_new)(struct bhnd_nvram_data *nv, + struct bhnd_nvram_io *io); + +/** Free all resources associated with @p nv. Called by + * bhnd_nvram_data_release() when the reference count reaches zero. */ +typedef void (bhnd_nvram_data_op_free)(struct bhnd_nvram_data *nv); + +/** @see bhnd_nvram_data_count() */ +typedef size_t (bhnd_nvram_data_op_count)(struct bhnd_nvram_data *nv); + +/** @see bhnd_nvram_data_size() */ +typedef int (bhnd_nvram_data_op_size)(struct bhnd_nvram_data *nv, + size_t *len); + +/** @see bhnd_nvram_data_serialize() */ +typedef int (bhnd_nvram_data_op_serialize)( + struct bhnd_nvram_data *nv, void *buf, + size_t *len); + +/** @see bhnd_nvram_data_caps() */ +typedef uint32_t (bhnd_nvram_data_op_caps)(struct bhnd_nvram_data *nv); + +/** @see bhnd_nvram_data_next() */ +typedef const char *(bhnd_nvram_data_op_next)(struct bhnd_nvram_data *nv, + void **cookiep); + +/** @see bhnd_nvram_data_find() */ +typedef void *(bhnd_nvram_data_op_find)(struct bhnd_nvram_data *nv, + const char *name); + +/** @see bhnd_nvram_data_getvar_name() */ +typedef const char *(bhnd_nvram_data_op_getvar_name)( + struct bhnd_nvram_data *nv, void *cookiep); + +/** @see bhnd_nvram_data_getvar() */ +typedef int (bhnd_nvram_data_op_getvar)(struct bhnd_nvram_data *nv, + void *cookiep, void *buf, size_t *len, + bhnd_nvram_type type); + +/** @see bhnd_nvram_data_getvar_ptr() */ +typedef const void *(bhnd_nvram_data_op_getvar_ptr)( + struct bhnd_nvram_data *nv, void *cookiep, + size_t *len, bhnd_nvram_type *type); + +/** + * NVRAM data class. + */ +struct bhnd_nvram_data_class { + const char *desc; /**< description */ + size_t size; /**< instance size */ + bhnd_nvram_data_op_probe *op_probe; + bhnd_nvram_data_op_new *op_new; + bhnd_nvram_data_op_free *op_free; + bhnd_nvram_data_op_count *op_count; + bhnd_nvram_data_op_size *op_size; + bhnd_nvram_data_op_serialize *op_serialize; + bhnd_nvram_data_op_caps *op_caps; + bhnd_nvram_data_op_next *op_next; + bhnd_nvram_data_op_find *op_find; + bhnd_nvram_data_op_getvar *op_getvar; + bhnd_nvram_data_op_getvar_ptr *op_getvar_ptr; + bhnd_nvram_data_op_getvar_name *op_getvar_name; +}; + +/** + * NVRAM data instance. + */ +struct bhnd_nvram_data { + struct bhnd_nvram_data_class *cls; + volatile u_int refs; +}; + +/* + * Helper macro for BHND_NVRAM_DATA_CLASS_DEFN(). + * + * Declares a bhnd_nvram_data_class method implementation with class name + * _cname and method name _mname + */ +#define BHND_NVRAM_DATA_CLASS_DECL_METHOD(_cname, _mname) \ + static bhnd_nvram_data_op_ ## _mname \ + bhnd_nvram_ ## _cname ## _ ## _mname; \ + +/* + * Helper macro for BHND_NVRAM_DATA_CLASS_DEFN(). + * + * Assign a bhnd_nvram_data_class method implementation with class name + * @p _cname and method name @p _mname + */ +#define BHND_NVRAM_DATA_CLASS_ASSIGN_METHOD(_cname, _mname) \ + .op_ ## _mname = bhnd_nvram_ ## _cname ## _ ## _mname, + +/* + * Helper macro for BHND_NVRAM_DATA_CLASS_DEFN(). + * + * Iterate over all bhnd_nvram_data_class method names, calling + * _macro with the class name _cname as the first argument, and + * a bhnd_nvram_data_class method name as the second. + */ +#define BHND_NVRAM_DATA_CLASS_ITER_METHODS(_cname, _macro) \ + _macro(_cname, probe) \ + _macro(_cname, new) \ + _macro(_cname, free) \ + _macro(_cname, count) \ + _macro(_cname, size) \ + _macro(_cname, serialize) \ + _macro(_cname, caps) \ + _macro(_cname, next) \ + _macro(_cname, find) \ + _macro(_cname, getvar) \ + _macro(_cname, getvar_ptr) \ + _macro(_cname, getvar_name) + +/** + * Define a bhnd_nvram_data_class with class name @p _n and description + * @p _desc, and register with bhnd_nvram_data_class_set. + */ +#define BHND_NVRAM_DATA_CLASS_DEFN(_cname, _desc, _size) \ + BHND_NVRAM_DATA_CLASS_ITER_METHODS(_cname, \ + BHND_NVRAM_DATA_CLASS_DECL_METHOD) \ + \ + struct bhnd_nvram_data_class bhnd_nvram_## _cname ## _class = { \ + .desc = (_desc), \ + .size = (_size), \ + BHND_NVRAM_DATA_CLASS_ITER_METHODS(_cname, \ + BHND_NVRAM_DATA_CLASS_ASSIGN_METHOD) \ + }; \ + \ + DATA_SET(bhnd_nvram_data_class_set, \ + bhnd_nvram_## _cname ## _class); + +#endif /* _BHND_NVRAM_BHND_NVRAM_DATAVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_io.c b/sys/dev/bhnd/nvram/bhnd_nvram_io.c new file mode 100644 index 0000000000000..7ceb33e5cbc58 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_io.c @@ -0,0 +1,205 @@ +/*- + * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#ifdef _KERNEL +#include <sys/param.h> +#else /* !_KERNEL */ +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#endif /* _KERNEL */ + +#include "bhnd_nvram_io.h" +#include "bhnd_nvram_iovar.h" + +/** + * Read exactly @p nbytes from @p io at @p offset. + * + * @param io NVRAM I/O context. + * @param offset The offset within @p io at which to perform the read. + * @param[out] buffer Output buffer to which @p nbytes from @p io will be + * written. + * @param nbytes The maximum number of bytes to be read from @p io. + * + * @retval 0 success + * @retval EIO if an input error occured reading @p io. + * @retval ENXIO if the request for @p offset or @p nbytes exceeds the size + * of @p io. + * @retval EFAULT if @p io requires I/O request alignment and @p offset is + * misaligned. + * @retval EFAULT if @p io requires I/O request alignment and @p nbytes is + * misaligned and cannot be rounded down to an aligned non-zero value. + */ +int +bhnd_nvram_io_read(struct bhnd_nvram_io *io, size_t offset, void *buffer, + size_t nbytes) +{ + return (io->iops->read(io, offset, buffer, nbytes)); +} + + +/** + * Attempt to fetch a pointer to @p io's internal read buffer, if + * supported by @p io. + * + * The returned pointer is only gauranteed to be valid until the next I/O + * operation performed on @p io; concrete implementations of bhnd_nvram_io + * may provide stronger gaurantees. + * + * @param io NVRAM I/O context. + * @param offset The offset within @p io for which to return a buffer pointer. + * @param[out] ptr On success, will be initialized with a pointer to @p io's + * internal read buffer. + * @param nbytes The minimum number of bytes that must be readable at @p offset. + * @param[out] navail The actual number of readable bytes, which may be greater + * than @p nbytes. If this value is not required, a NULL pointer may be + * provided. + * + * @retval 0 success + * @retval EIO if an input error occured reading @p io. + * @retval ENODEV if @p io does not support direct access to its backing read + * buffer. + * @retval ENXIO if the request exceeds the size of @p io. + * @retval EFAULT if @p io requires I/O request alignment and @p offset or + * @p nbytes are misaligned. + */ +int +bhnd_nvram_io_read_ptr(struct bhnd_nvram_io *io, size_t offset, + const void **ptr, size_t nbytes, size_t *navail) +{ + return (io->iops->read_ptr(io, offset, ptr, nbytes, navail)); +} + +/** + * Write @p nbytes to @p io at @p offset. + * + * @param io NVRAM I/O context. + * @param offset The offset within @p io at which to perform the write. + * @param buffer Data to be written to @p io. + * @param nbytes The number of bytes to be written from @p buffer. + * + * @retval 0 success + * @retval EIO if an output error occurs writing to @p io. + * @retval ENODEV if @p io does not support writing. + * @retval ENXIO if @p io does not support writes beyond the existing + * end-of-file, and a write at @p offset would exceed the size of the @p io + * backing data store. + * @retval EFAULT if @p io requires I/O request alignment and @p offset or + * @p nbytes are misaligned. + */ +int +bhnd_nvram_io_write(struct bhnd_nvram_io *io, size_t offset, void *buffer, + size_t nbytes) +{ + return (io->iops->write(io, offset, buffer, nbytes)); +} + +/** + * Attempt to fetch a writable pointer to @p io's internal write buffer, if + * supported by @p io. + * + * The returned pointer is only gauranteed to be valid until the next I/O + * operation performed on @p io; concrete implementations of bhnd_nvram_io + * may provide stronger gaurantees. + * + * @param io NVRAM I/O context. + * @param offset The offset within @p io for which to return a buffer pointer. + * @param[in,out] ptr On success, will be initialized with a pointer to @p io's + * internal buffer at which up to @p nbytes may be written. + * @param nbytes The minimum number of bytes that must be writable at @p offset. + * @param[out] navail The actual number of writable bytes, which may be greater + * than @p nbytes. If this value is not required, a NULL pointer may be + * provided. + * + * @retval 0 success + * @retval EIO if an output error occurs preparing @p io's write buffer. + * @retval ENODEV if @p io does not support direct access to its backing write + * buffer. + * @retval ENXIO if @p io does not support writes beyond the existing + * end-of-file, and a write at @p offset of @p nbytes would exceed the size of + * the @p io backing data store. + * @retval EFAULT if @p io requires I/O request alignment and @p offset or + * @p nbytes are misaligned. + */ +int +bhnd_nvram_io_write_ptr(struct bhnd_nvram_io *io, size_t offset, void **ptr, + size_t nbytes, size_t *navail) +{ + return (io->iops->write_ptr(io, offset, ptr, nbytes, navail)); +} + +/** + * Return the total number of bytes readable via @p io. + * + * @param io NVRAM I/O context. + */ +size_t +bhnd_nvram_io_getsize(struct bhnd_nvram_io *io) +{ + return (io->iops->getsize(io)); +} + +/** + * Attempt to set the size of @p io to @p size. + * + * If the total size of @p io is increased, the contents of the newly mapped + * bytes are undefined; concrete implementations of bhnd_nvram_io may + * provide stronger gaurantees. + * + * @param io NVRAM I/O context. + * @param size The new size. + * + * @retval 0 success + * @retval EIO if an I/O error occurs resizing @p io. + * @retval ENODEV if @p io does not support resizing. + * @retval ENXIO if @p size exceeds the capacity or other limits of @p io. + * @retval EFAULT if @p io requires I/O request alignment and @p size is + * misaligned. + */ +int +bhnd_nvram_io_setsize(struct bhnd_nvram_io *io, size_t size) +{ + return (io->iops->setsize(io, size)); +} + +/** + * Free a previously allocated I/O context, releasing all associated + * resources. + * + * @param io The I/O context to be freed. + */ +void +bhnd_nvram_io_free(struct bhnd_nvram_io *io) +{ + return (io->iops->free(io)); +} + diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_io.h b/sys/dev/bhnd/nvram/bhnd_nvram_io.h new file mode 100644 index 0000000000000..fcec2d7b1dd66 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_io.h @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/param.h> + +#include <dev/bhnd/bhnd.h> +#else /* !_KERNEL */ +#include <errno.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#endif /* _KERNEL */ + +struct bhnd_nvram_io; + +struct bhnd_nvram_io *bhnd_nvram_iobuf_new(const void *buffer, size_t size); +struct bhnd_nvram_io *bhnd_nvram_iobuf_empty(size_t size, size_t capacity); +struct bhnd_nvram_io *bhnd_nvram_iobuf_copy(struct bhnd_nvram_io *src); +struct bhnd_nvram_io *bhnd_nvram_iobuf_copy_range(struct bhnd_nvram_io *src, + size_t offset, size_t size); + +#ifdef _KERNEL +struct bhnd_nvram_io *bhnd_nvram_iores_new(struct bhnd_resource *r, + bus_size_t offset, bus_size_t size, + u_int bus_width); +#endif /* _KERNEL */ + +size_t bhnd_nvram_io_getsize(struct bhnd_nvram_io *io); +int bhnd_nvram_io_setsize(struct bhnd_nvram_io *io, + size_t size); + +int bhnd_nvram_io_read(struct bhnd_nvram_io *io, + size_t offset, void *buffer, size_t nbytes); +int bhnd_nvram_io_read_ptr(struct bhnd_nvram_io *io, + size_t offset, const void **ptr, size_t nbytes, + size_t *navail); + +int bhnd_nvram_io_write(struct bhnd_nvram_io *io, + size_t offset, void *buffer, size_t nbytes); +int bhnd_nvram_io_write_ptr(struct bhnd_nvram_io *io, + size_t offset, void **ptr, size_t nbytes, + size_t *navail); + +void bhnd_nvram_io_free(struct bhnd_nvram_io *io); + +#endif /* _BHND_NVRAM_BHND_NVRAM_IO_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_iobuf.c b/sys/dev/bhnd/nvram/bhnd_nvram_iobuf.c new file mode 100644 index 0000000000000..a36cbb36c63fd --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_iobuf.c @@ -0,0 +1,341 @@ +/*- + * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#ifdef _KERNEL +#include <sys/param.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#else /* !_KERNEL */ +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_io.h" +#include "bhnd_nvram_iovar.h" + +/** + * Buffer-backed NVRAM I/O context. + * + * iobuf instances are gauranteed to provide persistent references to its + * backing contigious buffer via bhnd_nvram_io_read_ptr() and + * bhnd_nvram_io_write_ptr(). + */ +struct bhnd_nvram_iobuf { + struct bhnd_nvram_io io; /**< common I/O instance state */ + void *buf; /**< backing buffer. if inline-allocated, will + be a reference to data[]. */ + size_t size; /**< size of @p buf */ + size_t capacity; /**< capacity of @p buf */ + uint8_t data[]; /**< inline buffer allocation */ +}; + +BHND_NVRAM_IOPS_DEFN(iobuf) + +/** + * Allocate and return a new I/O context with an uninitialized + * buffer of @p size and @p capacity. + * + * The caller is responsible for deallocating the returned I/O context via + * bhnd_nvram_io_free(). + * + * If @p capacity is less than @p size, a capacity of @p size will be used. + * + * @param size The initial size of the I/O context. + * @param capacity The total capacity of the I/O context buffer; + * the returned I/O context may be resized up to + * @p capacity via bhnd_nvram_io_setsize(). + * + * @retval bhnd_nvram_iobuf success. + * @retval NULL allocation failed. + * @retval NULL the requested @p capacity is less than + * @p size. + */ +struct bhnd_nvram_io * +bhnd_nvram_iobuf_empty(size_t size, size_t capacity) +{ + struct bhnd_nvram_iobuf *iobuf; + size_t iosz; + bool inline_alloc; + + /* Sanity check the capacity */ + if (size > capacity) + return (NULL); + + /* Would sizeof(iobuf)+capacity overflow? */ + if (SIZE_MAX - sizeof(*iobuf) < capacity) { + inline_alloc = false; + iosz = sizeof(*iobuf); + } else { + inline_alloc = true; + iosz = sizeof(*iobuf) + capacity; + } + + /* Allocate I/O context */ + iobuf = bhnd_nv_malloc(iosz); + if (iobuf == NULL) + return (NULL); + + iobuf->io.iops = &bhnd_nvram_iobuf_ops; + iobuf->buf = NULL; + iobuf->size = size; + iobuf->capacity = capacity; + + /* Either allocate our backing buffer, or initialize the + * backing buffer with a reference to our inline allocation. */ + if (inline_alloc) + iobuf->buf = &iobuf->data; + else + iobuf->buf = bhnd_nv_malloc(iobuf->capacity); + + + if (iobuf->buf == NULL) { + bhnd_nv_free(iobuf); + return (NULL); + } + + return (&iobuf->io); +} + +/** + * Allocate and return a new I/O context, copying @p size from @p buffer. + * + * The caller is responsible for deallocating the returned I/O context via + * bhnd_nvram_io_free(). + * + * @param buffer The buffer data be copied by the returned I/O context. + * @param size The size of @p buffer, in bytes. + * + * @retval bhnd_nvram_io success. + * @retval NULL allocation failed. + */ +struct bhnd_nvram_io * +bhnd_nvram_iobuf_new(const void *buffer, size_t size) +{ + struct bhnd_nvram_io *io; + struct bhnd_nvram_iobuf *iobuf; + + /* Allocate the iobuf */ + if ((io = bhnd_nvram_iobuf_empty(size, size)) == NULL) + return (NULL); + + /* Copy the input to our new iobuf instance */ + iobuf = (struct bhnd_nvram_iobuf *)io; + memcpy(iobuf->buf, buffer, iobuf->size); + + return (io); +} + +/** + * Allocate and return a new I/O context providing an in-memory copy + * of the data mapped by @p src. + * + * The caller is responsible for deallocating the returned I/O context via + * bhnd_nvram_io_free(). + * + * @param src The I/O context to be copied. + * + * @retval bhnd_nvram_io success. + * @retval NULL allocation failed. + * @retval NULL copying @p src failed. + */ +struct bhnd_nvram_io * +bhnd_nvram_iobuf_copy(struct bhnd_nvram_io *src) +{ + return (bhnd_nvram_iobuf_copy_range(src, 0x0, + bhnd_nvram_io_getsize(src))); +} + +/** + * Allocate and return a new I/O context providing an in-memory copy + * of @p size bytes mapped at @p offset by @p src. + * + * The caller is responsible for deallocating the returned I/O context via + * bhnd_nvram_io_free(). + * + * @param src The I/O context to be copied. + * @param offset The offset of the bytes to be copied from @p src. + * @param size The number of bytes to copy at @p offset from @p src. + * + * @retval bhnd_nvram_io success. + * @retval NULL allocation failed. + * @retval NULL copying @p src failed. + */ +struct bhnd_nvram_io * +bhnd_nvram_iobuf_copy_range(struct bhnd_nvram_io *src, size_t offset, + size_t size) +{ + struct bhnd_nvram_io *io; + struct bhnd_nvram_iobuf *iobuf; + int error; + + /* Check if offset+size would overflow */ + if (SIZE_MAX - size < offset) + return (NULL); + + /* Allocate the iobuf instance */ + if ((io = bhnd_nvram_iobuf_empty(size, size)) == NULL) + return (NULL); + + /* Copy the input I/O context */ + iobuf = (struct bhnd_nvram_iobuf *)io; + if ((error = bhnd_nvram_io_read(src, offset, iobuf->buf, size))) { + bhnd_nvram_io_free(&iobuf->io); + return (NULL); + } + + return (io); +} + + +static void +bhnd_nvram_iobuf_free(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io; + + /* Free the backing buffer if it wasn't allocated inline */ + if (iobuf->buf != &iobuf->data) + bhnd_nv_free(iobuf->buf); + + bhnd_nv_free(iobuf); +} + +static size_t +bhnd_nvram_iobuf_getsize(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io; + return (iobuf->size); +} + +static int +bhnd_nvram_iobuf_setsize(struct bhnd_nvram_io *io, size_t size) +{ + struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io; + + /* Can't exceed the actual capacity */ + if (size > iobuf->capacity) + return (ENXIO); + + iobuf->size = size; + return (0); +} + +/* Common iobuf_(read|write)_ptr implementation */ +static int +bhnd_nvram_iobuf_ptr(struct bhnd_nvram_iobuf *iobuf, size_t offset, void **ptr, + size_t nbytes, size_t *navail) +{ + size_t avail; + + /* Verify offset+nbytes fall within the buffer range */ + if (offset > iobuf->size) + return (ENXIO); + + avail = iobuf->size - offset; + if (avail < nbytes) + return (ENXIO); + + /* Valid I/O range, provide a pointer to the buffer and the + * total count of available bytes */ + *ptr = ((uint8_t *)iobuf->buf) + offset; + if (navail != NULL) + *navail = avail; + + return (0); +} + +static int +bhnd_nvram_iobuf_read_ptr(struct bhnd_nvram_io *io, size_t offset, + const void **ptr, size_t nbytes, size_t *navail) +{ + struct bhnd_nvram_iobuf *iobuf; + void *ioptr; + int error; + + iobuf = (struct bhnd_nvram_iobuf *) io; + + /* Return a pointer into our backing buffer */ + error = bhnd_nvram_iobuf_ptr(iobuf, offset, &ioptr, nbytes, navail); + if (error) + return (error); + + *ptr = ioptr; + + return (0); +} + +static int +bhnd_nvram_iobuf_write_ptr(struct bhnd_nvram_io *io, size_t offset, + void **ptr, size_t nbytes, size_t *navail) +{ + struct bhnd_nvram_iobuf *iobuf; + + iobuf = (struct bhnd_nvram_iobuf *) io; + + /* Return a pointer into our backing buffer */ + return (bhnd_nvram_iobuf_ptr(iobuf, offset, ptr, nbytes, navail)); +} + +static int +bhnd_nvram_iobuf_read(struct bhnd_nvram_io *io, size_t offset, void *buffer, + size_t nbytes) +{ + const void *ptr; + int error; + + /* Try to fetch a direct pointer for at least nbytes */ + if ((error = bhnd_nvram_io_read_ptr(io, offset, &ptr, nbytes, NULL))) + return (error); + + /* Copy out the requested data */ + memcpy(buffer, ptr, nbytes); + return (0); +} + +static int +bhnd_nvram_iobuf_write(struct bhnd_nvram_io *io, size_t offset, + void *buffer, size_t nbytes) +{ + void *ptr; + int error; + + /* Try to fetch a direct pointer for at least nbytes */ + if ((error = bhnd_nvram_io_write_ptr(io, offset, &ptr, nbytes, NULL))) + return (error); + + /* Copy in the provided data */ + memcpy(ptr, buffer, nbytes); + return (0); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_iores.c b/sys/dev/bhnd/nvram/bhnd_nvram_iores.c new file mode 100644 index 0000000000000..70d2d398cde33 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_iores.c @@ -0,0 +1,302 @@ +/*- + * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/malloc.h> +#include <sys/rman.h> + +#include <machine/bus.h> + +#include <dev/bhnd/bhnd.h> + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_io.h" +#include "bhnd_nvram_iovar.h" + +/** + * BHND resource-backed NVRAM I/O context. + */ +struct bhnd_nvram_iores { + struct bhnd_nvram_io io; /**< common I/O instance state */ + struct bhnd_resource *res; /**< backing resource (borrowed ref) */ + size_t offset; /**< offset within res */ + size_t size; /**< size relative to the base offset */ + u_int bus_width; /**< data type byte width to be used + when performing bus operations + on res. (1, 2, or 4 bytes) */ +}; + +BHND_NVRAM_IOPS_DEFN(iores); + +/** + * Allocate and return a new I/O context backed by a borrowed reference to @p r. + * + * The caller is responsible for deallocating the returned I/O context via + * bhnd_nvram_io_free(). + * + * @param r The resource to be mapped by the returned I/O + * context. + * @param offset Offset + * @param bus_width The required I/O width (1, 2, or 4 bytes) to be + * used when reading from @p r. + * + * @retval bhnd_nvram_io success. + * @retval NULL if allocation fails, or an invalid argument + * is supplied. + */ +struct bhnd_nvram_io * +bhnd_nvram_iores_new(struct bhnd_resource *r, bus_size_t offset, + bus_size_t size, u_int bus_width) +{ + struct bhnd_nvram_iores *iores; + rman_res_t r_start, r_size; + + /* Verify the bus width */ + switch (bus_width) { + case 1: + case 2: + case 4: + /* valid */ + break; + default: + BHND_NV_LOG("invalid bus width %u\n", bus_width); + return (NULL); + } + + /* offset/size must not exceed our internal size_t representation, + * or our bus_size_t usage (note that BUS_SPACE_MAXSIZE may be less + * than 2^(sizeof(bus_size_t) * 32). */ + if (size > SIZE_MAX || offset > SIZE_MAX) { + BHND_NV_LOG("offset %#jx+%#jx exceeds SIZE_MAX\n", + (uintmax_t)offset, (uintmax_t)offset); + return (NULL); + } + + if (size > BUS_SPACE_MAXSIZE || offset > BUS_SPACE_MAXSIZE) + { + BHND_NV_LOG("offset %#jx+%#jx exceeds BUS_SPACE_MAXSIZE\n", + (uintmax_t)offset, (uintmax_t)offset); + return (NULL); + } + + /* offset/size fall within the resource's mapped range */ + r_size = rman_get_size(r->res); + r_start = rman_get_start(r->res); + if (r_size < offset || r_size < size || r_size - size < offset) + return (NULL); + + /* offset/size must be bus_width aligned */ + if ((r_start + offset) % bus_width != 0) { + BHND_NV_LOG("base address %#jx+%#jx not aligned to bus width " + "%u\n", (uintmax_t)r_start, (uintmax_t)offset, bus_width); + return (NULL); + } + + if (size % bus_width != 0) { + BHND_NV_LOG("size %#jx not aligned to bus width %u\n", + (uintmax_t)size, bus_width); + return (NULL); + } + + /* Allocate and return the I/O context */ + iores = malloc(sizeof(*iores), M_BHND_NVRAM, M_WAITOK); + iores->io.iops = &bhnd_nvram_iores_ops; + iores->res = r; + iores->offset = offset; + iores->size = size; + iores->bus_width = bus_width; + + return (&iores->io); +} + +static void +bhnd_nvram_iores_free(struct bhnd_nvram_io *io) +{ + free(io, M_BHND_NVRAM); +} + +static size_t +bhnd_nvram_iores_getsize(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_iores *iores = (struct bhnd_nvram_iores *)io; + return (iores->size); +} + +static int +bhnd_nvram_iores_setsize(struct bhnd_nvram_io *io, size_t size) +{ + /* unsupported */ + return (ENODEV); +} + +static int +bhnd_nvram_iores_read_ptr(struct bhnd_nvram_io *io, size_t offset, + const void **ptr, size_t nbytes, size_t *navail) +{ + /* unsupported */ + return (ENODEV); +} + +static int +bhnd_nvram_iores_write_ptr(struct bhnd_nvram_io *io, size_t offset, + void **ptr, size_t nbytes, size_t *navail) +{ + /* unsupported */ + return (ENODEV); +} + +/** + * Validate @p offset and @p nbytes: + * + * - Verify that @p offset is mapped by the backing resource. + * - If less than @p nbytes are available at @p offset, write the actual number + * of bytes available to @p nbytes. + * - Verify that @p offset + @p nbytes are correctly aligned. + */ +static int +bhnd_nvram_iores_validate_req(struct bhnd_nvram_iores *iores, size_t offset, + size_t *nbytes) +{ + /* Verify offset falls within the resource range */ + if (offset > iores->size) + return (ENXIO); + + /* Check for eof */ + if (offset == iores->size) { + *nbytes = 0; + return (0); + } + + /* Verify offset alignment */ + if (offset % iores->bus_width != 0) + return (EFAULT); + + /* Limit nbytes to available range and verify size alignment */ + *nbytes = ummin(*nbytes, iores->size - offset); + if (*nbytes < iores->bus_width && *nbytes % iores->bus_width != 0) + return (EFAULT); + + return (0); +} + + +static int +bhnd_nvram_iores_read(struct bhnd_nvram_io *io, size_t offset, void *buffer, + size_t nbytes) +{ + struct bhnd_nvram_iores *iores; + bus_size_t r_offset; + size_t navail; + int error; + + iores = (struct bhnd_nvram_iores *)io; + + /* Validate the request and determine the actual number of readable + * bytes */ + navail = nbytes; + if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail))) + return (error); + + /* At least nbytes must be readable */ + if (navail < nbytes) + return (ENXIO); + + /* Handle zero length read */ + if (nbytes == 0) + return (0); + + /* Determine actual resource offset and perform the read */ + r_offset = iores->offset + offset; + switch (iores->bus_width) { + case 1: + bhnd_bus_read_region_stream_1(iores->res, r_offset, buffer, + nbytes); + break; + case 2: + bhnd_bus_read_region_stream_2(iores->res, r_offset, buffer, + nbytes / 2); + break; + case 4: + bhnd_bus_read_region_stream_4(iores->res, r_offset, buffer, + nbytes / 4); + break; + default: + panic("unreachable!"); + } + + return (0); +} + +static int +bhnd_nvram_iores_write(struct bhnd_nvram_io *io, size_t offset, + void *buffer, size_t nbytes) +{ + struct bhnd_nvram_iores *iores; + size_t navail; + bus_size_t r_offset; + int error; + + iores = (struct bhnd_nvram_iores *)io; + + /* Validate the request and determine the actual number of writable + * bytes */ + navail = nbytes; + if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail))) + return (error); + + /* At least nbytes must be writable */ + if (navail < nbytes) + return (ENXIO); + + /* Determine actual resource offset and perform the write */ + r_offset = iores->offset + offset; + switch (iores->bus_width) { + case 1: + bhnd_bus_write_region_stream_1(iores->res, r_offset, buffer, + nbytes); + break; + case 2: + bhnd_bus_write_region_stream_2(iores->res, r_offset, buffer, + nbytes / 2); + break; + case 4: + bhnd_bus_write_region_stream_4(iores->res, r_offset, buffer, + nbytes / 4); + break; + default: + panic("unreachable!"); + } + + return (0); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_iovar.h b/sys/dev/bhnd/nvram/bhnd_nvram_iovar.h new file mode 100644 index 0000000000000..137f505dddb4e --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_iovar.h @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/param.h> + +#include "bhnd_nvram_io.h" + +/** @see bhnd_nvram_io_read() */ +typedef int (bhnd_nvram_iop_read)(struct bhnd_nvram_io *io, size_t offset, + void *buffer, size_t nbytes); + +/** @see bhnd_nvram_io_read_ptr() */ +typedef int (bhnd_nvram_iop_read_ptr)(struct bhnd_nvram_io *io, size_t offset, + const void **ptr, size_t nbytes, size_t *navail); + +/** @see bhnd_nvram_io_write() */ +typedef int (bhnd_nvram_iop_write)(struct bhnd_nvram_io *io, size_t offset, + void *buffer, size_t nbytes); + +/** @see bhnd_nvram_io_write_ptr() */ +typedef int (bhnd_nvram_iop_write_ptr)(struct bhnd_nvram_io *io, size_t offset, + void **ptr, size_t nbytes, size_t *navail); + +/** @see bhnd_nvram_io_getsize() */ +typedef size_t (bhnd_nvram_iop_getsize)(struct bhnd_nvram_io *io); + +/** @see bhnd_nvram_io_setsize() */ +typedef int (bhnd_nvram_iop_setsize)(struct bhnd_nvram_io *io, size_t size); + +/** @see bhnd_nvram_io_free() */ +typedef void (bhnd_nvram_iop_free)(struct bhnd_nvram_io *io); + +/** + * NVRAM abstract I/O operations. + */ +struct bhnd_nvram_iops { + bhnd_nvram_iop_read *read; /**< read() implementation */ + bhnd_nvram_iop_read_ptr *read_ptr; /**< read_ptr() implementation */ + bhnd_nvram_iop_getsize *getsize; /**< getsize() implementation */ + bhnd_nvram_iop_setsize *setsize; /**< setsize() implementation */ + bhnd_nvram_iop_write *write; /**< write() implementation */ + bhnd_nvram_iop_write_ptr *write_ptr; /**< write_ptr() implementation */ + bhnd_nvram_iop_free *free; /**< free() implementation */ +}; + +/** + * NVRAM abstract I/O context. + */ +struct bhnd_nvram_io { + const struct bhnd_nvram_iops *iops; +}; + +/** + * Declare a bhnd_nvram_iops class with name @p _n. + */ +#define BHND_NVRAM_IOPS_DEFN(_n) \ + static bhnd_nvram_iop_read bhnd_nvram_ ## _n ## _read; \ + static bhnd_nvram_iop_read_ptr bhnd_nvram_ ## _n ## _read_ptr; \ + static bhnd_nvram_iop_write bhnd_nvram_ ## _n ## _write; \ + static bhnd_nvram_iop_write_ptr bhnd_nvram_ ## _n ## _write_ptr;\ + static bhnd_nvram_iop_getsize bhnd_nvram_ ## _n ## _getsize; \ + static bhnd_nvram_iop_setsize bhnd_nvram_ ## _n ## _setsize; \ + static bhnd_nvram_iop_free bhnd_nvram_ ## _n ## _free; \ + \ + static struct bhnd_nvram_iops bhnd_nvram_ ## _n ## _ops = { \ + .read = bhnd_nvram_ ## _n ## _read, \ + .read_ptr = bhnd_nvram_ ## _n ## _read_ptr, \ + .write = bhnd_nvram_ ## _n ## _write, \ + .write_ptr = bhnd_nvram_ ## _n ## _write_ptr, \ + .getsize = bhnd_nvram_ ## _n ## _getsize, \ + .setsize = bhnd_nvram_ ## _n ## _setsize, \ + .free = bhnd_nvram_ ## _n ## _free \ + }; + +#endif /* _BHND_NVRAM_BHND_NVRAM_IOVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_parser.c b/sys/dev/bhnd/nvram/bhnd_nvram_parser.c deleted file mode 100644 index a23efae074fd9..0000000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram_parser.c +++ /dev/null @@ -1,1578 +0,0 @@ -/*- - * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> - * 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 <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#include <sys/bus.h> -#include <sys/ctype.h> -#include <sys/endian.h> -#include <sys/malloc.h> -#include <sys/queue.h> -#include <sys/rman.h> -#include <sys/systm.h> - -#include <machine/bus.h> -#include <machine/resource.h> - -#include "bhnd_nvram_parserreg.h" -#include "bhnd_nvram_parservar.h" - -/* - * BHND NVRAM Parser - * - * Provides identification, decoding, and encoding of BHND NVRAM data. - */ - -static const struct bhnd_nvram_ops *bhnd_nvram_find_ops(bhnd_nvram_format fmt); - -static int bhnd_nvram_find_var(struct bhnd_nvram *sc, const char *name, - const char **value, size_t *value_len); - -static int bhnd_nvram_keycmp(const char *lhs, size_t lhs_len, - const char *rhs, size_t rhs_len); -static int bhnd_nvram_sort_idx(void *ctx, const void *lhs, - const void *rhs); -static int bhnd_nvram_generate_index(struct bhnd_nvram *sc); - -static int bhnd_nvram_index_lookup(struct bhnd_nvram *sc, - struct bhnd_nvram_idx *idx, const char *name, - const char **env, size_t *len, const char **value, - size_t *value_len); -static int bhnd_nvram_buffer_lookup(struct bhnd_nvram *sc, - const char *name, const char **env, size_t *env_len, - const char **value, size_t *value_len); - -static bool bhnd_nvram_bufptr_valid(struct bhnd_nvram *sc, const void *ptr, - size_t nbytes, bool log_error); - -static int bhnd_nvram_parse_env(struct bhnd_nvram *sc, const char *env, - size_t len, const char **key, size_t *key_len, - const char **val, size_t *val_len); - -/** - * Calculate the size of the NVRAM data in @p data. - * - * @param data Pointer to NVRAM data to be parsed. - * @param[in,out] size On input, the total size of @p data. On - * successful parsing of @p data, will be set to - * the parsed size (which may be larger). - */ -typedef int (*bhnd_nvram_op_getsize)(const void *data, size_t *size); - -/** Perform format-specific initialization. */ -typedef int (*bhnd_nvram_op_init)(struct bhnd_nvram *sc); - -/** Initialize any format-specific default values. */ -typedef int (*bhnd_nvram_op_init_defaults)(struct bhnd_nvram *sc); -typedef int (*bhnd_nvram_op_enum_buf)(struct bhnd_nvram *sc, - const char **env, size_t *len, const uint8_t *p, - uint8_t const **next); - -/* FMT_BCM ops */ -static int bhnd_nvram_bcm_getsize(const void *data, size_t *size); -static int bhnd_nvram_bcm_init(struct bhnd_nvram *sc); -static int bhnd_nvram_bcm_init_defaults(struct bhnd_nvram *sc); -static int bhnd_nvram_bcm_enum_buf(struct bhnd_nvram *sc, const char **env, - size_t *len, const uint8_t *p, uint8_t const **next); - -/* FMT_TLV ops */ -static int bhnd_nvram_tlv_getsize(const void *data, size_t *size); -static int bhnd_nvram_tlv_init(struct bhnd_nvram *sc); -static int bhnd_nvram_tlv_enum_buf(struct bhnd_nvram *sc, const char **env, - size_t *len, const uint8_t *p, uint8_t const **next); -/* FMT_TXT ops */ -static int bhnd_nvram_txt_getsize(const void *data, size_t *size); -static int bhnd_nvram_txt_init(struct bhnd_nvram *sc); -static int bhnd_nvram_txt_enum_buf(struct bhnd_nvram *sc, const char **env, - size_t *len, const uint8_t *p, uint8_t const **next); - -/** - * Format-specific operations. - */ -struct bhnd_nvram_ops { - bhnd_nvram_format fmt; /**< nvram format */ - bhnd_nvram_op_getsize getsize; /**< determine actual NVRAM size */ - bhnd_nvram_op_init init; /**< format-specific initialization */ - bhnd_nvram_op_enum_buf enum_buf; /**< enumerate backing buffer */ - bhnd_nvram_op_init_defaults init_defaults; /**< populate any default values */ -}; - -static const struct bhnd_nvram_ops bhnd_nvram_ops_table[] = { - { - BHND_NVRAM_FMT_BCM, - bhnd_nvram_bcm_getsize, - bhnd_nvram_bcm_init, - bhnd_nvram_bcm_enum_buf, - bhnd_nvram_bcm_init_defaults - }, - { - BHND_NVRAM_FMT_TLV, - bhnd_nvram_tlv_getsize, - bhnd_nvram_tlv_init, - bhnd_nvram_tlv_enum_buf, - NULL - }, - { - BHND_NVRAM_FMT_BTXT, - bhnd_nvram_txt_getsize, - bhnd_nvram_txt_init, - bhnd_nvram_txt_enum_buf, - NULL - }, -}; - -#define NVRAM_LOG(sc, fmt, ...) do { \ - if (sc->dev != NULL) \ - device_printf(sc->dev, fmt, ##__VA_ARGS__); \ - else \ - printf("bhnd_nvram: " fmt, ##__VA_ARGS__); \ -} while (0) - -/* Limit a size_t value to a suitable range for use as a printf string field - * width */ -#define NVRAM_PRINT_WIDTH(_len) \ - ((_len) > NVRAM_VAL_MAX ? NVRAM_VAL_MAX : (int)(_len)) - -/* Is _c a field terminating/delimiting character? */ -#define nvram_is_ftermc(_c) ((_c) == '\0' || nvram_is_fdelim(_c)) - -/* Is _c a field delimiting character? */ -#define nvram_is_fdelim(_c) ((_c) == ',') - -/** - * Identify @p ident. - * - * @param ident Initial header data to be used for identification. - * @param expected Expected format against which @p ident will be tested. - * - * @retval 0 If @p ident has the @p expected format. - * @retval ENODEV If @p ident does not match @p expected. - */ -int -bhnd_nvram_parser_identify(const union bhnd_nvram_ident *ident, - bhnd_nvram_format expected) -{ - uint32_t bcm_magic = le32toh(ident->bcm.magic); - - switch (expected) { - case BHND_NVRAM_FMT_BCM: - if (bcm_magic == NVRAM_MAGIC) - return (0); - - return (ENODEV); - case BHND_NVRAM_FMT_TLV: - if (bcm_magic == NVRAM_MAGIC) - return (ENODEV); - - if (ident->tlv.tag != NVRAM_TLV_TYPE_ENV) - return (ENODEV); - - return (0); - case BHND_NVRAM_FMT_BTXT: - for (size_t i = 0; i < nitems(ident->btxt); i++) { - char c = ident->btxt[i]; - if (!isprint(c) && !isspace(c)) - return (ENODEV); - } - return (0); - break; - default: - printf("%s: unknown format: %d\n", __FUNCTION__, expected); - return (ENODEV); - } -} - -/** Return the set of operations for @p fmt, if any */ -static const struct bhnd_nvram_ops * -bhnd_nvram_find_ops(bhnd_nvram_format fmt) -{ - const struct bhnd_nvram_ops *ops; - - /* Fetch format-specific operation callbacks */ - for (size_t i = 0; i < nitems(bhnd_nvram_ops_table); i++) { - ops = &bhnd_nvram_ops_table[i]; - - if (ops->fmt != fmt) - continue; - - /* found */ - return (ops); - } - - return (NULL); -} - -/** - * Identify the NVRAM format at @p offset within @p r, verify the - * CRC (if applicable), and allocate a local shadow copy of the NVRAM data. - * - * After initialization, no reference to @p input will be held by the - * NVRAM parser, and @p input may be safely deallocated. - * - * @param[out] sc The NVRAM parser state to be initialized. - * @param dev The parser's parent device, or NULL if none. - * @param data NVRAM data to be parsed. - * @param size Size of @p data. - * @param fmt Required format of @p input. - * - * @retval 0 success - * @retval ENOMEM If internal allocation of NVRAM state fails. - * @retval EINVAL If @p input parsing fails. - */ -int -bhnd_nvram_parser_init(struct bhnd_nvram *sc, device_t dev, const void *data, - size_t size, bhnd_nvram_format fmt) -{ - int error; - - /* Initialize NVRAM state */ - memset(sc, 0, sizeof(*sc)); - - sc->dev = dev; - LIST_INIT(&sc->devpaths); - - /* Verify data format and init operation callbacks */ - if (size < sizeof(union bhnd_nvram_ident)) - return (EINVAL); - - error = bhnd_nvram_parser_identify( - (const union bhnd_nvram_ident *)data, fmt); - if (error) - return (error); - - if ((sc->ops = bhnd_nvram_find_ops(fmt)) == NULL) { - NVRAM_LOG(sc, "unsupported format: %d\n", fmt); - return (error); - } - - /* Determine appropriate size for backing buffer */ - sc->buf_size = size; - if ((error = sc->ops->getsize(data, &sc->buf_size))) - return (error); - - if (sc->buf_size > size) { - NVRAM_LOG(sc, "cannot parse %zu NVRAM bytes, would overrun " - "%zu byte input buffer\n", sc->buf_size, size); - return (EINVAL); - } - - /* Allocate and populate backing buffer */ - sc->buf = malloc(sc->buf_size, M_BHND_NVRAM, M_NOWAIT); - if (sc->buf == NULL) - return (ENOMEM); - memcpy(sc->buf, data, sc->buf_size); - - /* Allocate default/pending variable hash tables */ - error = bhnd_nvram_varmap_init(&sc->defaults, NVRAM_SMALL_HASH_SIZE, - M_NOWAIT); - if (error) - goto cleanup; - - error = bhnd_nvram_varmap_init(&sc->pending, NVRAM_SMALL_HASH_SIZE, - M_NOWAIT); - if (error) - goto cleanup; - - /* Perform format-specific initialization */ - if ((error = sc->ops->init(sc))) - goto cleanup; - - /* Generate all indices */ - if ((error = bhnd_nvram_generate_index(sc))) - goto cleanup; - - /* Add any format-specific default values */ - if (sc->ops->init_defaults != NULL) { - if ((error = sc->ops->init_defaults(sc))) - goto cleanup; - } - - return (0); - -cleanup: - bhnd_nvram_parser_fini(sc); - return (error); -} - - -/** - * Release all resources held by @p sc. - * - * @param sc A NVRAM instance previously initialized via - * bhnd_nvram_parser_init(). - */ -void -bhnd_nvram_parser_fini(struct bhnd_nvram *sc) -{ - struct bhnd_nvram_devpath *dpath, *dnext; - - LIST_FOREACH_SAFE(dpath, &sc->devpaths, dp_link, dnext) { - free(dpath->path, M_BHND_NVRAM); - free(dpath, M_BHND_NVRAM); - } - - if (sc->defaults.table != NULL) - bhnd_nvram_varmap_free(&sc->defaults); - - if (sc->pending.table != NULL) - bhnd_nvram_varmap_free(&sc->pending); - - if (sc->idx != NULL) - free(sc->idx, M_BHND_NVRAM); - - if (sc->buf != NULL) - free(sc->buf, M_BHND_NVRAM); - -} - -/** - * Identify the integer format of @p field. - * - * @param field Field to be identified. - * @param field_len Length of @p field. - * @param[out] base Integer base, or 0 if integer format unrecognized. - * @param[out] negated True if integer is prefixed with negation sign. - * - * @retval true if parsed successfully - * @retval false if the format of @p field cannot be determined. - */ -static bool -bhnd_nvram_identify_intfmt(const char *field, size_t field_len, int *base, - bool *negated) -{ - const char *p; - - /* Hex? */ - p = field; - if (field_len > 2 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { - bool valid; - - /* Check all field characters */ - valid = true; - for (p = field + 2; p - field < field_len; p++) { - if (isxdigit(*p)) - continue; - - valid = false; - break; - } - - if (valid) { - *base = 16; - *negated = false; - return (true); - } - } - - /* Decimal? */ - p = field; - if (field_len >= 1 && (*p == '-' || isdigit(*p))) { - bool valid; - - valid = true; - *negated = false; - for (p = field; p - field < field_len; p++) { - if (p == field && *p == '-') { - *negated = true; - continue; - } - - if (isdigit(*p)) - continue; - - valid = false; - break; - } - - if (valid) { - *base = 10; - return (true); - } - } - - /* No match */ - *base = 0; - *negated = false; - return (false); -} - -/** - * Search for a field delimiter or '\0' in @p value, returning the - * size of the first field (not including its terminating character). - * - * If no terminating character is found, @p value_len is returned. - * - * @param value The value to be searched. - * @param value_size The size of @p value. - */ -static size_t -bhnd_nvram_parse_field_len(const char *value, size_t value_size) -{ - for (const char *p = value; p - value < value_size; p++) { - if (nvram_is_ftermc(*p)) - return (p - value); - } - - return (value_size); -} - -/* Parse a string NVRAM variable, writing the NUL-terminated result - * to buf (if non-NULL). */ -static int -bhnd_nvram_parse_strvar(const char *value, size_t value_len, char *buf, - size_t *size) -{ - size_t str_len; - size_t req_size; - size_t max_size; - - if (buf != NULL) - max_size = *size; - else - max_size = 0; - - - /* Determine input and output sizes, including whether additional space - * is required for a trailing NUL */ - str_len = strnlen(value, value_len); - if (str_len == value_len) - req_size = str_len + 1; - else - req_size = value_len; - - /* Provide actual size to caller */ - *size = req_size; - if (max_size < req_size) { - if (buf != NULL) - return (ENOMEM); - else - return (0); - } - - /* Copy and NUL terminate output */ - memcpy(buf, value, str_len); - buf[str_len] = '\0'; - return (0); -} - -/** - * Read an NVRAM variable. - * - * @param sc The NVRAM parser state. - * @param name The NVRAM variable name. - * @param[out] buf On success, the requested value will be written - * to this buffer. This argment may be NULL if - * the value is not desired. - * @param[in,out] len The capacity of @p buf. On success, will be set - * to the actual size of the requested value. - * @param type The requested data type to be written to @p buf. - * - * @retval 0 success - * @retval ENOENT The requested variable was not found. - * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too - * small to hold the requested value. - * @retval non-zero If reading @p name otherwise fails, a regular unix - * error code will be returned. - */ -int -bhnd_nvram_parser_getvar(struct bhnd_nvram *sc, const char *name, void *buf, - size_t *len, bhnd_nvram_type type) -{ - char *cstr, cstr_buf[NVRAM_VAL_MAX+1]; - const char *val; - size_t cstr_size; - size_t limit, nbytes; - size_t field_len, val_len; - int error; - - /* Verify name validity */ - if (!bhnd_nvram_validate_name(name, strlen(name))) - return (EINVAL); - - /* Fetch variable's string value */ - if ((error = bhnd_nvram_find_var(sc, name, &val, &val_len))) - return (error); - - nbytes = 0; - if (buf != NULL) - limit = *len; - else - limit = 0; - - /* Populate C string requests directly from the fetched value */ - if (type == BHND_NVRAM_TYPE_CSTR) - return (bhnd_nvram_parse_strvar(val, val_len, buf, len)); - - /* Determine actual string length. */ - val_len = strnlen(val, val_len); - - /* Try parsing as an octet string value (e.g. a MAC address) */ - if (bhnd_nvram_parse_octet_string(val, val_len, buf, len, type) == 0) - return (0); - - /* Otherwise, we need a NUL-terminated copy of the string value - * for parsing */ - cstr_size = val_len + 1; - if (cstr_size <= sizeof(cstr)) { - /* prefer stack allocated buffer */ - cstr = cstr_buf; - } else { - cstr = malloc(cstr_size, M_BHND_NVRAM, M_NOWAIT); - if (cstr == NULL) - return (EFBIG); - } - - /* Copy and NUL terminate */ - strncpy(cstr, val, val_len); - cstr[val_len] = '\0'; - - /* Parse */ - for (char *p = cstr; *p != '\0';) { - char *endp; - int base; - bool is_int, is_negated; - union { - unsigned long u32; - long s32; - } intv; - - /* Determine the field length */ - field_len = val_len - (p - cstr); - field_len = bhnd_nvram_parse_field_len(p, field_len); - - /* Skip any leading whitespace */ - while (field_len > 0 && isspace(*p)) { - p++; - field_len--; - } - - /* Empty field values cannot be parsed as a fixed - * data type */ - if (field_len == 0) { - NVRAM_LOG(sc, "error: cannot parse empty string in " - "'%s'\n", cstr); - return (EFTYPE); - } - - /* Attempt to identify the integer format */ - is_int = bhnd_nvram_identify_intfmt(p, field_len, &base, - &is_negated); - - /* Extract the field data */ -#define NV_READ_INT(_ctype, _max, _min, _dest, _strto) do { \ - if (!is_int) { \ - error = EFTYPE; \ - goto finished; \ - } \ - \ - if (is_negated && _min == 0) { \ - error = ERANGE; \ - goto finished; \ - } \ - \ - _dest = _strto(p, &endp, base); \ - if (endp == p || !nvram_is_ftermc(*endp)) { \ - error = ERANGE; \ - goto finished; \ - } \ - \ - if (_dest > _max || _dest < _min) { \ - error = ERANGE; \ - goto finished; \ - } \ - \ - if (limit > nbytes && limit - nbytes >= sizeof(_ctype)) \ - *((_ctype *)((uint8_t *)buf + nbytes)) = _dest; \ - \ - nbytes += sizeof(_ctype); \ -} while(0) - - switch (type) { - case BHND_NVRAM_TYPE_CHAR: - /* Copy out the characters directly */ - for (size_t i = 0; i < field_len; i++) { - if (limit > nbytes) - *((char *)buf + nbytes) = p[i]; - nbytes++; - } - break; - - case BHND_NVRAM_TYPE_UINT8: - NV_READ_INT(uint8_t, UINT8_MAX, 0, intv.u32, strtoul); - break; - - case BHND_NVRAM_TYPE_UINT16: - NV_READ_INT(uint16_t, UINT16_MAX, 0, intv.u32, strtoul); - break; - - case BHND_NVRAM_TYPE_UINT32: - NV_READ_INT(uint32_t, UINT32_MAX, 0, intv.u32, strtoul); - break; - - case BHND_NVRAM_TYPE_INT8: - NV_READ_INT(int8_t, INT8_MAX, INT8_MIN, intv.s32, - strtol); - break; - - case BHND_NVRAM_TYPE_INT16: - NV_READ_INT(int16_t, INT16_MAX, INT16_MIN, intv.s32, - strtol); - break; - - case BHND_NVRAM_TYPE_INT32: - NV_READ_INT(int32_t, INT32_MAX, INT32_MIN, intv.s32, - strtol); - break; - - case BHND_NVRAM_TYPE_CSTR: /* Must be handled above */ - /* fallthrough */ - default: - NVRAM_LOG(sc, "unhandled NVRAM type: %d\n", type); - error = ENXIO; - goto finished; - } - - /* Advance to next field, skip any trailing delimiter */ - p += field_len; - if (nvram_is_fdelim(*p)) - p++; - } - - error = 0; - -finished: - if (cstr != cstr_buf) - free(cstr, M_BHND_NVRAM); - - return (error); -} - -/** - * Set an NVRAM variable. - * - * @param sc The NVRAM parser state. - * @param name The NVRAM variable name. - * @param[out] buf The new value. - * @param[in,out] len The size of @p buf. - * @param type The data type of @p buf. - * - * @retval 0 success - * @retval ENOENT The requested variable was not found. - * @retval EINVAL If @p len does not match the expected variable size. - */ -int -bhnd_nvram_parser_setvar(struct bhnd_nvram *sc, const char *name, - const void *buf, size_t len, bhnd_nvram_type type) -{ - /* Verify name validity */ - if (!bhnd_nvram_validate_name(name, strlen(name))) - return (EINVAL); - - /* Verify buffer size alignment for the given type. If this is a - * variable width type, a width of 0 will always pass this check */ - if (len % bhnd_nvram_type_width(type) != 0) - return (EINVAL); - - /* Determine string format (or directly add variable, if a C string) */ - switch (type) { - case BHND_NVRAM_TYPE_UINT8: - case BHND_NVRAM_TYPE_UINT16: - case BHND_NVRAM_TYPE_UINT32: - case BHND_NVRAM_TYPE_INT8: - case BHND_NVRAM_TYPE_INT16: - case BHND_NVRAM_TYPE_INT32: - // TODO: primitive type value support - return (EOPNOTSUPP); - - case BHND_NVRAM_TYPE_CHAR: - case BHND_NVRAM_TYPE_CSTR: - return (bhnd_nvram_varmap_add(&sc->pending, name, buf, len)); - } - - return (0); -} - -/** - * Return true if @p ptr + nbytes falls within our backing buffer, false - * otherwise. - */ -static bool -bhnd_nvram_bufptr_valid(struct bhnd_nvram *sc, const void *ptr, size_t nbytes, - bool log_error) -{ - const uint8_t *p = ptr; - - if (p < sc->buf) - goto failed; - - if (nbytes > sc->buf_size) - goto failed; - - if (p - sc->buf > sc->buf_size - nbytes) - goto failed; - - return (true); - -failed: - if (log_error) - NVRAM_LOG(sc, "NVRAM record not readable at %p+%#zx (base=%p, " - "len=%zu)\n", p, nbytes, sc->buf, sc->buf_size); - return (false); -} - -/** - * Parse a 'key=value' env string. - */ -static int -bhnd_nvram_parse_env(struct bhnd_nvram *sc, const char *env, size_t len, - const char **key, size_t *key_len, const char **val, size_t *val_len) -{ - const char *p; - - /* Key */ - if ((p = memchr(env, '=', len)) == NULL) { - NVRAM_LOG(sc, "missing delim in '%.*s'\n", - NVRAM_PRINT_WIDTH(len), env); - return (EINVAL); - } - - *key = env; - *key_len = p - env; - - /* Skip '=' */ - p++; - - /* Vaue */ - *val = p; - *val_len = len - (p - env); - - return (0); -} - -/** - * Fetch a string pointer to @p name's value, if any. - * - * @param sc The NVRAM parser state. - * @param name The NVRAM variable name. - * @param[out] value On success, a pointer to the variable's value - * string. The string may not be NUL terminated. - * @param[out] value_len On success, the length of @p value, not - * including a terminating NUL (if any exists). - * - * @retval 0 success - * @retval ENOENT The requested variable was not found. - * @retval non-zero If reading @p name otherwise fails, a regular unix - * error code will be returned. - */ -static int -bhnd_nvram_find_var(struct bhnd_nvram *sc, const char *name, const char **value, - size_t *value_len) -{ - struct bhnd_nvram_tuple *t; - bhnd_nvram_op_enum_buf enum_fn; - const char *env; - size_t env_len; - size_t name_len; - int error; - - enum_fn = sc->ops->enum_buf; - name_len = strlen(name); - - /* - * Search path: - * - * - uncommitted changes - * - index lookup OR buffer scan - * - registered defaults - */ - - /* Search uncommitted changes */ - t = bhnd_nvram_varmap_find(&sc->pending, name, name_len); - if (t != NULL) { - if (t->value != NULL) { - /* Uncommited value exists, is not a deletion */ - *value = t->value; - *value_len = t->value_len; - return (0); - } else { - /* Value is marked for deletion. */ - error = ENOENT; - goto failed; - } - } - - /* Search backing buffer. We the index if available; otherwise, - * perform a buffer scan */ - if (sc->idx != NULL) { - error = bhnd_nvram_index_lookup(sc, sc->idx, name, &env, - &env_len, value, value_len); - } else { - error = bhnd_nvram_buffer_lookup(sc, name, &env, &env_len, - value, value_len); - } - -failed: - /* If a parse error occured, we don't want to hide the issue by - * returning a default NVRAM value. Otherwise, look for a matching - * default. */ - if (error != ENOENT) - return (error); - - t = bhnd_nvram_varmap_find(&sc->defaults, name, name_len); - if (t != NULL) { - *value = t->value; - *value_len = t->value_len; - return (0); - } - - /* Not found, and no default value available */ - return (ENOENT); -} - -/* - * An strcmp()-compatible lexical comparison implementation that - * handles non-NUL-terminated strings. - */ -static int -bhnd_nvram_keycmp(const char *lhs, size_t lhs_len, const char *rhs, - size_t rhs_len) -{ - int order; - - order = strncmp(lhs, rhs, ulmin(lhs_len, rhs_len)); - if (order == 0) { - if (lhs_len < rhs_len) - order = -1; - else if (lhs_len > rhs_len) - order = 1; - } - - return (order); -} - -/* sort function for bhnd_nvram_idx_entry values */ -static int -bhnd_nvram_sort_idx(void *ctx, const void *lhs, const void *rhs) -{ - struct bhnd_nvram *sc; - const struct bhnd_nvram_idx_entry *l_idx, *r_idx; - const char *l_str, *r_str; - - sc = ctx; - l_idx = lhs; - r_idx = rhs; - - /* Fetch string pointers */ - l_str = (char *)(sc->buf + l_idx->env_offset); - r_str = (char *)(sc->buf + r_idx->env_offset); - - /* Perform comparison */ - return (bhnd_nvram_keycmp(l_str, l_idx->key_len, r_str, - r_idx->key_len)); -} - - -/** - * Generate all indices for the NVRAM data backing @p nvram. - * - * @param sc The NVRAM parser state. - * - * @retval 0 success - * @retval non-zero If indexing @p nvram fails, a regular unix - * error code will be returned. - */ -static int -bhnd_nvram_generate_index(struct bhnd_nvram *sc) -{ - bhnd_nvram_op_enum_buf enum_fn; - const char *key, *val; - const char *env; - const uint8_t *p; - size_t env_len; - size_t idx_bytes; - size_t key_len, val_len; - size_t num_records; - int error; - - enum_fn = sc->ops->enum_buf; - num_records = 0; - - /* Parse and register all device path aliases */ - p = NULL; - while ((error = enum_fn(sc, &env, &env_len, p, &p)) == 0) { - struct bhnd_nvram_devpath *devpath; - char *eptr; - char suffix[NVRAM_KEY_MAX+1]; - size_t suffix_len; - u_long index; - - /* Hit EOF */ - if (env == NULL) - break; - - num_records++; - - /* Skip string comparison if env_len < strlen(devpath) */ - if (env_len < NVRAM_DEVPATH_LEN) - continue; - - /* Check for devpath prefix */ - if (strncmp(env, NVRAM_DEVPATH_STR, NVRAM_DEVPATH_LEN) != 0) - continue; - - /* Split key and value */ - error = bhnd_nvram_parse_env(sc, env, env_len, &key, - &key_len, &val, &val_len); - if (error) - return (error); - - /* NUL terminate the devpath's suffix */ - if (key_len >= sizeof(suffix)) { - NVRAM_LOG(sc, "variable '%.*s' exceeds NVRAM_KEY_MAX, " - "skipping devpath parsing\n", - NVRAM_PRINT_WIDTH(key_len), key); - continue; - } else { - suffix_len = key_len - NVRAM_DEVPATH_LEN; - if (suffix_len == 0) - continue; - - strcpy(suffix, key+NVRAM_DEVPATH_LEN); - suffix[suffix_len] = '\0'; - } - - /* Parse the index value */ - index = strtoul(suffix, &eptr, 10); - if (eptr == suffix || *eptr != '\0') { - NVRAM_LOG(sc, "invalid devpath variable '%.*s'\n", - NVRAM_PRINT_WIDTH(key_len), key); - continue; - } - - /* Register path alias */ - devpath = malloc(sizeof(*devpath), M_BHND_NVRAM, M_NOWAIT); - if (devpath == NULL) - return (ENOMEM); - - devpath->index = index; - devpath->path = strndup(val, val_len, M_BHND_NVRAM); - LIST_INSERT_HEAD(&sc->devpaths, devpath, dp_link); - } - - if (error) - return (error); - - /* Save record count */ - sc->num_buf_vars = num_records; - - /* Skip generating variable index if threshold is not met */ - if (sc->num_buf_vars < NVRAM_IDX_VAR_THRESH) - return (0); - - /* Allocate and populate variable index */ - idx_bytes = sizeof(struct bhnd_nvram_idx) + - (sizeof(struct bhnd_nvram_idx_entry) * sc->num_buf_vars); - sc->idx = malloc(idx_bytes, M_BHND_NVRAM, M_NOWAIT); - if (sc->idx == NULL) { - NVRAM_LOG(sc, "error allocating %zu byte index\n", idx_bytes); - goto bad_index; - } - - sc->idx->num_entries = sc->num_buf_vars; - - if (bootverbose) { - NVRAM_LOG(sc, "allocated %zu byte index for %zu variables " - "in %zu bytes\n", idx_bytes, sc->num_buf_vars, - sc->buf_size); - } - - p = NULL; - for (size_t i = 0; i < sc->idx->num_entries; i++) { - struct bhnd_nvram_idx_entry *idx; - size_t env_offset; - size_t key_len, val_len; - - /* Fetch next record */ - if ((error = enum_fn(sc, &env, &env_len, p, &p))) - return (error); - - /* Early EOF */ - if (env == NULL) { - NVRAM_LOG(sc, "indexing failed, expected %zu records " - "(got %zu)\n", sc->idx->num_entries, i+1); - goto bad_index; - } - - /* Calculate env offset */ - env_offset = (const uint8_t *)env - (const uint8_t *)sc->buf; - if (env_offset > NVRAM_IDX_OFFSET_MAX) { - NVRAM_LOG(sc, "'%.*s' offset %#zx exceeds maximum " - "indexable value\n", NVRAM_PRINT_WIDTH(env_len), - env, env_offset); - goto bad_index; - } - - /* Split key and value */ - error = bhnd_nvram_parse_env(sc, env, env_len, &key, &key_len, - &val, &val_len); - if (error) - return (error); - - if (key_len > NVRAM_IDX_LEN_MAX) { - NVRAM_LOG(sc, "key length %#zx at %#zx exceeds maximum " - "indexable value\n", key_len, env_offset); - goto bad_index; - } - - if (val_len > NVRAM_IDX_LEN_MAX) { - NVRAM_LOG(sc, "value length %#zx for key '%.*s' " - "exceeds maximum indexable value\n", val_len, - NVRAM_PRINT_WIDTH(key_len), key); - goto bad_index; - } - - idx = &sc->idx->entries[i]; - idx->env_offset = env_offset; - idx->key_len = key_len; - idx->val_len = val_len; - } - - /* Sort the index table */ - qsort_r(sc->idx->entries, sc->idx->num_entries, - sizeof(sc->idx->entries[0]), sc, bhnd_nvram_sort_idx); - - return (0); - -bad_index: - /* Fall back on non-indexed access */ - NVRAM_LOG(sc, "reverting to non-indexed variable lookup\n"); - if (sc->idx != NULL) { - free(sc->idx, M_BHND_NVRAM); - sc->idx = NULL; - } - - return (0); -} - - -/** - * Perform an index lookup of @p name. - * - * @param sc The NVRAM parser state. - * @param idx The index to search. - * @param name The variable to search for. - * @param[out] env On success, the pointer to @p name within the - * backing buffer. - * @param[out] env_len On success, the length of @p env. - * @param[out] value On success, the pointer to @p name's value - * within the backing buffer. - * @param[out] value_len On success, the length of @p value. - * - * @retval 0 If @p name was found in the index. - * @retval ENOENT If @p name was not found in the index. - * @retval ENODEV If no index has been generated. - */ -static int -bhnd_nvram_index_lookup(struct bhnd_nvram *sc, struct bhnd_nvram_idx *idx, - const char *name, const char **env, size_t *env_len, const char **value, - size_t *value_len) -{ - struct bhnd_nvram_idx_entry *idxe; - const char *idxe_key; - size_t min, mid, max; - size_t name_len; - int order; - - if (idx->num_entries == 0) - return (ENOENT); - - /* - * Locate the requested variable using a binary search. - */ - min = 0; - mid = 0; - max = idx->num_entries - 1; - name_len = strlen(name); - - while (max >= min) { - /* Select midpoint */ - mid = (min + max) / 2; - idxe = &idx->entries[mid]; - - /* Determine which side of the partition to search */ - idxe_key = (const char *) (sc->buf + idxe->env_offset); - order = bhnd_nvram_keycmp(idxe_key, idxe->key_len, name, - name_len); - - if (order < 0) { - /* Search upper partition */ - min = mid + 1; - } else if (order > 0) { - /* Search lower partition */ - max = mid - 1; - } else if (order == 0) { - /* Match found */ - *env = sc->buf + idxe->env_offset; - *env_len = idxe->key_len + idxe->val_len + 1 /* '=' */; - - *value = *env + idxe->key_len + 1 /* '=' */; - *value_len = idxe->val_len; - - return (0); - } - } - - /* Not found */ - return (ENOENT); -} - - -/** - * Perform a unindexed search for an entry matching @p name in the backing - * NVRAM data buffer. - * - * @param sc The NVRAM parser state. - * @param name The variable to search for. - * @param[out] env On success, the pointer to @p name within the - * backing buffer. - * @param[out] env_len On success, the length of @p env. - * @param[out] value On success, the pointer to @p name's value - * within the backing buffer. - * @param[out] value_len On success, the length of @p value. - * - * @retval 0 If @p name was found in the index. - * @retval ENOENT If @p name was not found in the index. - * @retval ENODEV If no index has been generated. - */ -static int -bhnd_nvram_buffer_lookup(struct bhnd_nvram *sc, const char *name, - const char **env, size_t *env_len, const char **value, size_t *value_len) -{ - bhnd_nvram_op_enum_buf enum_fn; - const uint8_t *p; - size_t name_len; - int error; - - enum_fn = sc->ops->enum_buf; - name_len = strlen(name); - - /* Iterate over all records in the backing buffer */ - p = NULL; - while ((error = enum_fn(sc, env, env_len, p, &p)) == 0) { - /* Hit EOF, not found */ - if (*env == NULL) - return (ENOENT); - - /* Skip string comparison if env_len < strlen('key=') */ - if (*env_len < name_len + 1) - continue; - - /* Skip string comparison if delimiter isn't found at - * expected position */ - if (*(*env + name_len) != '=') - continue; - - /* Check for match */ - if (strncmp(*env, name, name_len) == 0) { - /* Found */ - *value = *env + name_len + 1; - *value_len = *env_len - name_len - 1; - return (0); - }; - } - - return (error); -} - -/* FMT_BCM NVRAM data size calculation */ -static int -bhnd_nvram_bcm_getsize(const void *data, size_t *size) -{ - const struct bhnd_nvram_header *hdr; - - if (*size < sizeof(*hdr)) - return (EINVAL); - - hdr = (const struct bhnd_nvram_header *) data; - *size = le32toh(hdr->size); - return (0); -} - -/* FMT_BCM-specific parser initialization */ -static int -bhnd_nvram_bcm_init(struct bhnd_nvram *sc) -{ - const uint8_t *p; - uint32_t cfg0; - uint8_t crc, valid; - - /* Validate CRC */ - if (sc->buf_size < NVRAM_CRC_SKIP) - return (EINVAL); - - if (sc->buf_size < sizeof(struct bhnd_nvram_header)) - return (EINVAL); - - cfg0 = ((struct bhnd_nvram_header *)sc->buf)->cfg0; - valid = (cfg0 & NVRAM_CFG0_CRC_MASK) >> NVRAM_CFG0_CRC_SHIFT; - - p = sc->buf; - crc = bhnd_nvram_crc8(p + NVRAM_CRC_SKIP, sc->buf_size-NVRAM_CRC_SKIP, - BHND_NVRAM_CRC8_INITIAL); - - if (crc != valid) { - NVRAM_LOG(sc, "warning: NVRAM CRC error (crc=%#hhx, " - "expected=%hhx)\n", crc, valid); - } - - return (0); -} - -/* Populate FMT_BCM-specific default values */ -static int -bhnd_nvram_bcm_init_defaults(struct bhnd_nvram *sc) -{ - struct bhnd_nvram_header *header; - char vbuf[NVRAM_VAL_MAX]; - uint32_t value; - int error; - int nwrite; - - /* Verify that our header is readable */ - header = (struct bhnd_nvram_header *) sc->buf; - if (!bhnd_nvram_bufptr_valid(sc, header, sizeof(*header), true)) - return (EINVAL); - - /* Extract a value value from the NVRAM header, format it, and - * register a new default variable tuple */ -#define NVRAM_BCM_HEADER_DEFAULT(_field, _name) do { \ - value = NVRAM_GET_BITS(le32toh(header->_field), _name); \ - nwrite = snprintf(vbuf, sizeof(vbuf), _name ##_FMT, value); \ - if (nwrite < 0 || nwrite >= sizeof(vbuf)) { \ - NVRAM_LOG(sc, "%s: formatting '%s' failed: %d\n", \ - __FUNCTION__, _name ## _VAR, nwrite); \ - return (ENXIO); \ - } \ - error = bhnd_nvram_varmap_add(&sc->defaults, \ - _name ##_VAR, vbuf, strlen(vbuf)); \ - \ - if (error) \ - return (error); \ -} while(0) - - NVRAM_BCM_HEADER_DEFAULT(cfg0, NVRAM_CFG0_SDRAM_INIT); - NVRAM_BCM_HEADER_DEFAULT(cfg1, NVRAM_CFG1_SDRAM_CFG); - NVRAM_BCM_HEADER_DEFAULT(cfg1, NVRAM_CFG1_SDRAM_REFRESH); - NVRAM_BCM_HEADER_DEFAULT(sdram_ncdl, NVRAM_SDRAM_NCDL); - -#undef NVRAM_BCM_HEADER_DEFAULT - - return (0); -} - - -/* FMT_BCM record parsing */ -static int -bhnd_nvram_bcm_enum_buf(struct bhnd_nvram *sc, const char **env, size_t *len, - const uint8_t *p, uint8_t const **next) -{ - /* First record is found following the NVRAM header */ - if (p == NULL) - p = sc->buf + sizeof(struct bhnd_nvram_header); - - if (!bhnd_nvram_bufptr_valid(sc, p, 1, true)) - return (EINVAL); - - /* EOF */ - if (*p == '\0') { - *env = NULL; - *len = 0; - *next = p; - return (0); - } - - /* Provide pointer to env data */ - *env = p; - *len = strnlen(p, sc->buf_size - (p - sc->buf)); - - /* Advance to next entry and skip terminating NUL */ - p += *len; - if (bhnd_nvram_bufptr_valid(sc, p, 1, false)) { - p++; - } else { - NVRAM_LOG(sc, "warning: missing NVRAM termination record"); - } - - *next = p; - return (0); -} - -/* FMT_TLV NVRAM data size calculation */ -static int -bhnd_nvram_tlv_getsize(const void *data, size_t *size) -{ - const uint8_t *const start = data; - size_t offset; - uint16_t rlen; - - offset = 0; - while (offset < *size) { - uint8_t type; - - /* Fetch type */ - type = *(start+offset); - - /* EOF */ - if (type == NVRAM_TLV_TYPE_END) { - *size = offset + 1; - return (0); - } - - if ((offset++) == *size) - return (EINVAL); - - /* Determine record length */ - if (type & NVRAM_TLV_TF_U8_LEN) { - rlen = *(start+offset); - } else { - rlen = *(start+offset) << 8; - if ((offset++) == *size) - return (EINVAL); - rlen |= *(start+offset); - } - - if ((offset++) >= *size) - return (EINVAL); - - /* Advance to next entry */ - if (rlen > *size || *size - rlen < offset) - return (EINVAL); - - offset += rlen; - } - - /* EOF not found */ - return (EINVAL); -} - -/* FMT_TLV-specific parser initialization */ -static int -bhnd_nvram_tlv_init(struct bhnd_nvram *sc) -{ - return (0); -} - -/* FMT_TLV record parsing */ -static int -bhnd_nvram_tlv_enum_buf(struct bhnd_nvram *sc, const char **env, size_t *len, - const uint8_t *p, uint8_t const **next) -{ - size_t rlen; - uint8_t type; - - if (p == NULL) - p = sc->buf; - - /* Fetch type */ - if (!bhnd_nvram_bufptr_valid(sc, p, 1, true)) - return (EINVAL); - - type = *p; - - /* EOF */ - if (type == NVRAM_TLV_TYPE_END) { - *env = NULL; - *len = 0; - *next = p; - return (0); - } - - /* Determine record length */ - p++; - if (type & NVRAM_TLV_TF_U8_LEN) { - if (!bhnd_nvram_bufptr_valid(sc, p, 1, true)) - return (EINVAL); - - rlen = *p; - p += 1; - } else { - if (!bhnd_nvram_bufptr_valid(sc, p, 2, true)) - return (EINVAL); - rlen = (p[0] << 8) | (p[1]); - p += 2; - } - - /* Verify record readability */ - if (!bhnd_nvram_bufptr_valid(sc, p, rlen, true)) - return (EINVAL); - - /* Error on non-env records */ - if (type != NVRAM_TLV_TYPE_ENV) { - NVRAM_LOG(sc, "unsupported NVRAM TLV tag: %#hhx\n", type); - return (EINVAL); - } - - /* Skip flag field */ - if (rlen < 1) - return (EINVAL); - p++; - rlen--; - - /* Provide pointer to env data */ - *env = p; - *len = strnlen(*env, rlen); - - /* Advance to next entry */ - *next = p + rlen; - - return (0); -} - -/* FMT_BTXT NVRAM data size calculation */ -static int -bhnd_nvram_txt_getsize(const void *data, size_t *size) -{ - *size = (strnlen(data, *size)); - return (0); -} - -/* FMT_BTXT-specific parser initialization */ -static int -bhnd_nvram_txt_init(struct bhnd_nvram *sc) -{ - return (0); -} - -/* Seek past the next line ending (\r, \r\n, or \n) */ -static const uint8_t * -bhnd_nvram_txt_seek_eol(struct bhnd_nvram *sc, const uint8_t *p) -{ - while (p < sc->buf + sc->buf_size) { - switch (*p) { - case '\r': - /* \r\n */ - if (bhnd_nvram_bufptr_valid(sc, p, 1, false)) { - if (*(p+1) == '\n') - p++; - } - - return (p+1); - case '\n': - return (p+1); - default: - p++; - break; - } - } - - return (p); -} - -/* Seek to the next valid key=value entry (or EOF) */ -static const uint8_t * -bhnd_nvram_txt_seek_nextline(struct bhnd_nvram *sc, const uint8_t *p) -{ - /* Skip leading whitespace and comments */ - while (p < sc->buf + sc->buf_size) { - if (isspace(*p)) { - p++; - continue; - } - - if (*p == '#') { - p = bhnd_nvram_txt_seek_eol(sc, p); - continue; - } - - break; - } - - return (p); -} - -/* FMT_BTXT record parsing */ -static int -bhnd_nvram_txt_enum_buf(struct bhnd_nvram *sc, const char **env, size_t *len, - const uint8_t *p, uint8_t const **next) -{ - const uint8_t *startp; - size_t line_len; - - if (p == NULL) - p = sc->buf; - - /* Skip any leading whitespace and comments */ - p = bhnd_nvram_txt_seek_nextline(sc, p); - - /* EOF? */ - if (!bhnd_nvram_bufptr_valid(sc, p, 1, false)) { - *env = NULL; - *len = 0; - *next = p; - return (0); - } - - /* Find record termination (EOL, or '#') */ - startp = p; - while (p < sc->buf + sc->buf_size) { - if (*p == '#' || *p == '\n' || *p == '\r') - break; - - p++; - } - - /* Calculate line length, check for EOF */ - line_len = p - startp; - if (!bhnd_nvram_bufptr_valid(sc, p, 1, false)) { - *env = NULL; - *len = 0; - *next = p; - return (0); - } - - /* Got env data; trim any tailing whitespace */ - *env = startp; - *len = line_len; - - for (size_t i = 0; i < line_len && line_len > 0; i++) { - char c = startp[line_len - i - 1]; - if (!isspace(c)) - break; - - *len -= 1; - } - - /* Advance to next entry */ - p = bhnd_nvram_txt_seek_nextline(sc, p); - - *next = p; - return (0); -} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_parser.h b/sys/dev/bhnd/nvram/bhnd_nvram_parser.h deleted file mode 100644 index 27abd556a31f9..0000000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram_parser.h +++ /dev/null @@ -1,101 +0,0 @@ -/*- - * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> - * 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 <sys/param.h> -#include <sys/bus.h> - -#include "bhnd_nvram_common.h" - -union bhnd_nvram_ident; - -struct bhnd_nvram_idx; -struct bhnd_nvram_ops; -struct bhnd_nvram_devpath; - -struct bhnd_nvram; - -LIST_HEAD(bhnd_nvram_devpaths, bhnd_nvram_devpath); - -int bhnd_nvram_parser_identify(const union bhnd_nvram_ident *ident, - bhnd_nvram_format expected); -int bhnd_nvram_parser_init(struct bhnd_nvram *sc, device_t owner, - const void *data, size_t len, bhnd_nvram_format fmt); -void bhnd_nvram_parser_fini(struct bhnd_nvram *sc); - -int bhnd_nvram_parser_getvar(struct bhnd_nvram *sc, const char *name, - void *buf, size_t *len, bhnd_nvram_type type); -int bhnd_nvram_parser_setvar(struct bhnd_nvram *sc, const char *name, - const void *buf, size_t len, bhnd_nvram_type type); - -/** BCM NVRAM header */ -struct bhnd_nvram_header { - uint32_t magic; - uint32_t size; - uint32_t cfg0; /**< crc:8, version:8, sdram_init:16 */ - uint32_t cfg1; /**< sdram_config:16, sdram_refresh:16 */ - uint32_t sdram_ncdl; /**< sdram_ncdl */ -} __packed; - -/** - * NVRAM format identification. - * - * To perform identification of the NVRAM format using bhnd_nvram_identify(), - * read `sizeof(bhnd_nvram_indent)` bytes from the head of the NVRAM data. - */ -union bhnd_nvram_ident { - struct bhnd_nvram_header bcm; - char btxt[4]; - struct bhnd_tlv_ident { - uint8_t tag; - uint8_t size[2]; - uint8_t flags; - } __packed tlv; -}; - -/** bhnd nvram parser instance state */ -struct bhnd_nvram { - device_t dev; /**< parent device, or NULL */ - const struct bhnd_nvram_ops *ops; - uint8_t *buf; /**< nvram data */ - size_t buf_size; - size_t num_buf_vars; /**< number of records in @p buf (0 if not yet calculated) */ - - struct bhnd_nvram_idx *idx; /**< key index */ - - struct bhnd_nvram_devpaths devpaths; /**< device paths */ - struct bhnd_nvram_varmap defaults; /**< default values */ - struct bhnd_nvram_varmap pending; /**< uncommitted writes */ -}; - -#endif /* _BHND_NVRAM_BHND_NVRAM_PARSER_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_parservar.h b/sys/dev/bhnd/nvram/bhnd_nvram_parservar.h deleted file mode 100644 index a02dfe0d21b0f..0000000000000 --- a/sys/dev/bhnd/nvram/bhnd_nvram_parservar.h +++ /dev/null @@ -1,86 +0,0 @@ -/*- - * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> - * 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 <sys/types.h> - -#include "bhnd_nvram_common.h" - -#include "bhnd_nvram_parser.h" - -#define NVRAM_IDX_VAR_THRESH 15 /**< index is generated if minimum variable count is met */ -#define NVRAM_IDX_OFFSET_MAX UINT16_MAX /**< maximum indexable offset */ -#define NVRAM_IDX_LEN_MAX UINT8_MAX /**< maximum indexable key/value length */ - -#define NVRAM_KEY_MAX 64 /**< maximum key length (not incl. NUL) */ -#define NVRAM_VAL_MAX 255 /**< maximum value length (not incl. NUL) */ - -#define NVRAM_DEVPATH_STR "devpath" /**< name prefix of device path aliases */ -#define NVRAM_DEVPATH_LEN (sizeof(NVRAM_DEVPATH_STR) - 1) - -#define NVRAM_SMALL_HASH_SIZE 16 /**< hash table size for pending/default tuples */ - -/** - * NVRAM devpath record. - * - * Aliases index values to full device paths. - */ -struct bhnd_nvram_devpath { - u_long index; /** alias index */ - char *path; /** aliased path */ - - LIST_ENTRY(bhnd_nvram_devpath) dp_link; -}; - -/** - * NVRAM index record. - * - * Provides entry offsets into a backing NVRAM buffer. - */ -struct bhnd_nvram_idx_entry { - uint16_t env_offset; /**< offset to env string */ - uint8_t key_len; /**< key length */ - uint8_t val_len; /**< value length */ -}; - -/** - * NVRAM index. - * - * Provides a compact binary search index into the backing NVRAM buffer. - */ -struct bhnd_nvram_idx { - size_t num_entries; /**< entry count */ - struct bhnd_nvram_idx_entry entries[]; /**< index entries */ -}; - -#endif /* _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_private.h b/sys/dev/bhnd/nvram/bhnd_nvram_private.h new file mode 100644 index 0000000000000..e7b33732acc17 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_private.h @@ -0,0 +1,402 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/param.h> + +#ifdef _KERNEL +#include <sys/malloc.h> + +#include <machine/stdarg.h> +#else +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#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 <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +/* ASCII-specific ctype variants that work consistently regardless + * of current locale */ +#define bhnd_nv_isupper(c) ((c) >= 'A' && (c) <= 'Z') +#define bhnd_nv_islower(c) ((c) >= 'a' && (c) <= 'z') +#define bhnd_nv_isalpha(c) (bhnd_nv_isupper(c) || bhnd_nv_islower(c)) +#define bhnd_nv_isprint(c) ((c) >= ' ' && (c) <= '~') +#define bhnd_nv_isspace(c) ((c) == ' ' || ((c) >= '\t' && (c) <= '\r')) +#define bhnd_nv_isdigit(c) isdigit(c) +#define bhnd_nv_isxdigit(c) isxdigit(c) +#define bhnd_nv_toupper(c) ((c) - \ + (('a' - 'A') * ((c) >= 'a' && (c) <= 'z'))) + +#define bhnd_nv_malloc(size) malloc((size)) +#define bhnd_nv_calloc(n, size) calloc((n), (size)) +#define bhnd_nv_reallocf(buf, size) reallocf((buf), (size)) +#define bhnd_nv_free(buf) free((buf)) +#define bhnd_nv_strndup(str, len) strndup(str, len) + +#ifndef NDEBUG +#define BHND_NV_INVARIANTS +#endif + +#define BHND_NV_ASSERT(expr, ...) assert(expr) + +#define BHND_NV_VERBOSE (0) +#define BHND_NV_PANIC(fmt, ...) do { \ + fprintf(stderr, "panic: " fmt "\n", ##__VA_ARGS__); \ + abort(); \ +} while(0) +#define BHND_NV_LOG(fmt, ...) \ + fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) + +static inline uintmax_t +bhnd_nv_ummax(uintmax_t a, uintmax_t b) +{ + return (a > b ? a : b); +} + +static inline uintmax_t +bhnd_nv_ummin(uintmax_t a, uintmax_t b) +{ + + return (a < b ? a : b); +} + +#endif /* _KERNEL */ + +#ifdef BHND_NV_VERBOSE +#define BHND_NV_DEBUG(...) BHND_NV_LOG(__VA_ARGS__) +#else /* !BHND_NV_VERBOSE */ +#define BHND_NV_DEBUG(...) +#endif /* BHND_NV_VERBOSE */ + +/* Limit a size_t value to a suitable range for use as a printf string field + * width */ +#define BHND_NV_PRINT_WIDTH(_len) \ + ((_len) > (INT_MAX) ? (INT_MAX) : (int)(_len)) + +int bhnd_nvram_value_coerce(const void *inp, + size_t ilen, bhnd_nvram_type itype, + void *outp, size_t *olen, + bhnd_nvram_type otype); + +int bhnd_nvram_value_nelem(bhnd_nvram_type type, + const void *data, size_t len, + size_t *nelem); +size_t bhnd_nvram_value_size(bhnd_nvram_type type, + const void *data, size_t nbytes, + size_t nelem); + +int bhnd_nvram_value_printf(const char *fmt, + const void *inp, size_t ilen, + bhnd_nvram_type itype, char *outp, + size_t *olen, ...); +int bhnd_nvram_value_vprintf(const char *fmt, + const void *inp, size_t ilen, + bhnd_nvram_type itype, char *outp, + size_t *olen, va_list ap); + +const struct bhnd_nvram_vardefn *bhnd_nvram_find_vardefn(const char *varname); +const struct bhnd_nvram_vardefn *bhnd_nvram_get_vardefn(size_t id); +size_t bhnd_nvram_get_vardefn_id( + const struct bhnd_nvram_vardefn *defn); + +int bhnd_nvram_parse_int(const char *s, + size_t maxlen, u_int base, size_t *nbytes, + void *outp, size_t *olen, + bhnd_nvram_type otype); + +int bhnd_nvram_parse_env(const char *env, + size_t env_len, char delim, + const char **name, size_t *name_len, + const char **value, size_t *value_len); + +size_t bhnd_nvram_parse_field(const char **inp, + size_t ilen, char delim); +size_t bhnd_nvram_trim_field(const char **inp, + size_t ilen, char delim); + +bool bhnd_nvram_validate_name(const char *name, + size_t name_len); + +/** + * Calculate CRC-8 over @p buf using the Broadcom SPROM/NVRAM CRC-8 + * polynomial. + * + * @param buf input buffer + * @param size buffer size + * @param crc last computed crc, or BHND_NVRAM_CRC8_INITIAL + */ +static inline uint8_t +bhnd_nvram_crc8(const void *buf, size_t size, uint8_t crc) +{ + const uint8_t *p = (const uint8_t *)buf; + while (size--) + crc = bhnd_nvram_crc8_tab[(crc ^ *p++)]; + + return (crc); +} + +#define BHND_NVRAM_CRC8_INITIAL 0xFF /**< Initial bhnd_nvram_crc8 value */ +#define BHND_NVRAM_CRC8_VALID 0x9F /**< Valid CRC-8 checksum */ + +/** NVRAM variable flags */ +enum { + BHND_NVRAM_VF_MFGINT = 1<<0, /**< mfg-internal variable; should not + be externally visible */ + BHND_NVRAM_VF_IGNALL1 = 1<<1 /**< hide variable if its value has all + bits set. */ +}; + +/** + * SPROM layout flags + */ +enum { + /** + * SPROM layout does not have magic identification value. + * + * This applies to SPROM revisions 1-3, where the actual + * layout must be determined by looking for a matching sromrev + * at the expected offset, and then verifying the CRC to ensure + * that the match was not a false positive. + */ + SPROM_LAYOUT_MAGIC_NONE = (1<<0), +}; + +/** NVRAM variable definition */ +struct bhnd_nvram_vardefn { + const char *name; /**< variable name */ + const char *desc; /**< human readable description, + or NULL */ + const char *help; /**< human readable help text, + or NULL */ + bhnd_nvram_type type; /**< variable type */ + uint8_t nelem; /**< element count, or 1 if not + an array-typed variable */ + const bhnd_nvram_val_fmt_t *fmt; /**< value format, or NULL */ + uint32_t flags; /**< flags (BHND_NVRAM_VF_*) */ +}; + +/* + * NVRAM variable definitions generated from nvram_map. + */ +extern const struct bhnd_nvram_vardefn bhnd_nvram_vardefns[]; +extern const size_t bhnd_nvram_num_vardefns; + +/** + * SPROM layout descriptor. + */ +struct bhnd_sprom_layout { + size_t size; /**< SPROM image size, in bytes */ + uint8_t rev; /**< SPROM revision */ + uint8_t flags; /**< layout flags (SPROM_LAYOUT_*) */ + size_t srev_offset; /**< offset to SROM revision */ + size_t magic_offset; /**< offset to magic value */ + uint16_t magic_value; /**< expected magic value */ + const uint8_t *bindings; /**< SPROM binding opcode table */ + size_t bindings_size; /**< SPROM binding opcode table size */ + uint16_t num_vars; /**< total number of variables defined + for this layout by the binding + table */ +}; + +/* + * SPROM layout descriptions generated from nvram_map. + */ +extern const struct bhnd_sprom_layout bhnd_sprom_layouts[]; +extern const size_t bhnd_sprom_num_layouts; + +/* + * SPROM binding opcodes. + * + * Most opcodes are provided with two variants: + * + * - Standard: The opcode's data directly follows the opcode. The data type + * (SPROM_OPCODE_DATA_*) is encoded in the opcode immediate (IMM). + * - Immediate: The opcode's data is encoded directly in the opcode immediate + * (IMM). + */ +#define SPROM_OPC_MASK 0xF0 /**< operation mask */ +#define SPROM_IMM_MASK 0x0F /**< immediate value mask */ +#define SPROM_IMM_MAX SPROM_IMM_MASK +#define SPROM_OP_DATA_U8 0x00 /**< data is u8 */ +#define SPROM_OP_DATA_U8_SCALED 0x01 /**< data is u8; multiply by + type width */ +#define SPROM_OP_DATA_U16 0x02 /**< data is u16-le */ +#define SPROM_OP_DATA_U32 0x03 /**< data is u32-le */ +#define SPROM_OP_DATA_I8 0x04 /**< data is i8 */ +#define SPROM_OPCODE_EXT 0x00 /**< extended opcodes defined + in IMM */ +#define SPROM_OPCODE_EOF 0x00 /**< marks end of opcode + stream */ +#define SPROM_OPCODE_NELEM 0x01 /**< variable array element + count follows as U8 */ +#define SPROM_OPCODE_VAR_END 0x02 /**< marks end of variable + definition */ +#define SPROM_OPCODE_TYPE 0x03 /**< input type follows as U8 + (see BHND_NVRAM_TYPE_*) */ +#define SPROM_OPCODE_VAR_IMM 0x10 /**< variable ID (imm) */ +#define SPROM_OPCODE_VAR_REL_IMM 0x20 /**< relative variable ID + (last ID + imm) */ +#define SPROM_OPCODE_VAR 0x30 /**< variable ID */ +#define SPROM_OPCODE_REV_IMM 0x40 /**< revision range (imm) */ +#define SPROM_OPCODE_REV_RANGE 0x50 /**< revision range (8-bit range)*/ +#define SPROM_OP_REV_RANGE_MAX 0x0F /**< maximum representable SROM + revision */ +#define SPROM_OP_REV_START_MASK 0xF0 +#define SPROM_OP_REV_START_SHIFT 4 +#define SPROM_OP_REV_END_MASK 0x0F +#define SPROM_OP_REV_END_SHIFT 0 +#define SPROM_OPCODE_MASK_IMM 0x60 /**< value mask (imm) */ +#define SPROM_OPCODE_MASK 0x70 /**< value mask */ +#define SPROM_OPCODE_SHIFT_IMM 0x80 /**< value shift (unsigned + imm, multipled by 2) */ +#define SPROM_OPCODE_SHIFT 0x90 /**< value shift */ +#define SPROM_OPCODE_OFFSET_REL_IMM 0xA0 /**< relative input offset + (last offset + + (imm * type width)) */ +#define SPROM_OPCODE_OFFSET 0xB0 /**< input offset */ +#define SPROM_OPCODE_TYPE_IMM 0xC0 /**< input type (imm, + see BHND_NVRAM_TYPE_*) */ +#define SPROM_OPCODE_DO_BIND 0xD0 /**< bind current value, + advance input/output + offsets as per IMM */ +#define SPROM_OP_BIND_SKIP_IN_MASK 0x03 /**< the number of input + elements to advance after + the bind */ +#define SPROM_OP_BIND_SKIP_IN_SHIFT 0 +#define SPROM_OP_BIND_SKIP_IN_SIGN (1<<2) /**< SKIP_IN sign bit */ +#define SPROM_OP_BIND_SKIP_OUT_MASK 0x08 /**< the number of output + elements to advance after + the bind */ +#define SPROM_OP_BIND_SKIP_OUT_SHIFT 3 +#define SPROM_OPCODE_DO_BINDN_IMM 0xE0 /**< bind IMM times, advancing + input/output offsets by one + element each time */ +#define SPROM_OPCODE_DO_BINDN 0xF0 /**< bind N times, advancing + input/output offsets as per + SPROM_OP_BIND_SKIP_IN/SPROM_OP_BIND_SKIP_OUT + IMM values. The U8 element + count follows. */ + +/** Evaluates to true if opcode is an extended opcode */ +#define SPROM_OPCODE_IS_EXT(_opcode) \ + (((_opcode) & SPROM_OPC_MASK) == SPROM_OPCODE_EXT) + +/** Return the opcode constant for a simple or extended opcode */ +#define SPROM_OPCODE_OP(_opcode) \ + (SPROM_OPCODE_IS_EXT(_opcode) ? (_opcode) : ((_opcode) & SPROM_OPC_MASK)) + +/** Return the opcode immediate for a simple opcode, or zero if this is + * an extended opcode */ +#define SPROM_OPCODE_IMM(_opcode) \ + (SPROM_OPCODE_IS_EXT(_opcode) ? 0 : ((_opcode) & SPROM_IMM_MASK)) + +/** Evaluates to true if the given opcode produces an implicit + * SPROM_OPCODE_VAR_END instruction for any open variable */ +#define SPROM_OP_IS_IMPLICIT_VAR_END(_opcode) \ + (((_opcode) == SPROM_OPCODE_VAR_IMM) || \ + ((_opcode) == SPROM_OPCODE_VAR_REL_IMM) || \ + ((_opcode) == SPROM_OPCODE_VAR) || \ + ((_opcode) == SPROM_OPCODE_REV_IMM) || \ + ((_opcode) == SPROM_OPCODE_REV_RANGE)) + +/** Evaluates to true if the given opcode is either an explicit + * SPROM_OPCODE_VAR_END instruction, or is an opcode that produces an + * implicit terminatation of any open variable */ +#define SPROM_OP_IS_VAR_END(_opcode) \ + (((_opcode) == SPROM_OPCODE_VAR_END) || \ + SPROM_OP_IS_IMPLICIT_VAR_END(_opcode)) + +/** maximum representable immediate value */ +#define SPROM_OP_IMM_MAX SPROM_IMM_MASK + +/** maximum representable SROM revision */ +#define SPROM_OP_REV_MAX MAX(SPROM_OP_REV_RANGE_MAX, SPROM_IMM_MAX) + +#endif /* _BHND_NVRAM_BHND_NVRAM_PRIVATE_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_store.c b/sys/dev/bhnd/nvram/bhnd_nvram_store.c new file mode 100644 index 0000000000000..1b4770aae16b0 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_store.c @@ -0,0 +1,572 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/queue.h> + +#ifdef _KERNEL + +#include <sys/param.h> +#include <sys/systm.h> + +#else /* !_KERNEL */ + +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" +#include "bhnd_nvram_datavar.h" + +#include "bhnd_nvram_storevar.h" + +/* + * BHND NVRAM Store + * + * Manages in-memory and persistent representations of NVRAM data. + */ + +static int bhnd_nvram_sort_idx(void *ctx, const void *lhs, + const void *rhs); +static int bhnd_nvram_generate_index(struct bhnd_nvram_store *sc); +static void *bhnd_nvram_index_lookup(struct bhnd_nvram_store *sc, + const char *name); + +/** + * Allocate and initialize a new NVRAM data store instance. + * + * The caller is responsible for deallocating the instance via + * bhnd_nvram_store_free(). + * + * @param[out] store On success, a pointer to the newly allocated NVRAM data + * instance. + * @param data The NVRAM data to be managed by the returned NVRAM data store + * instance. + * + * @retval 0 success + * @retval non-zero if an error occurs during allocation or initialization, a + * regular unix error code will be returned. + */ +int +bhnd_nvram_store_new(struct bhnd_nvram_store **store, + struct bhnd_nvram_data *data) +{ + struct bhnd_nvram_store *sc; + int error; + + /* Allocate new instance */ + sc = bhnd_nv_calloc(1, sizeof(*sc)); + if (sc == NULL) + return (ENOMEM); + + LIST_INIT(&sc->paths); + + /* Retain the NVRAM data */ + sc->nv = bhnd_nvram_data_retain(data); + + /* Allocate uncommitted change list */ + sc->pending = nvlist_create(NV_FLAG_IGNORE_CASE); + if (sc->pending == NULL) { + error = ENOMEM; + goto cleanup; + } + + /* Generate all indices */ + if ((error = bhnd_nvram_generate_index(sc))) + goto cleanup; + + BHND_NVSTORE_LOCK_INIT(sc); + + *store = sc; + return (0); + +cleanup: + bhnd_nvram_store_free(sc); + return (error); +} + +/** + * Allocate and initialize a new NVRAM data store instance, parsing the + * NVRAM data from @p io. + * + * The caller is responsible for deallocating the instance via + * bhnd_nvram_store_free(). + * + * The NVRAM data mapped by @p io will be copied, and @p io may be safely + * deallocated after bhnd_nvram_store_new() returns. + * + * @param[out] store On success, a pointer to the newly allocated NVRAM data + * instance. + * @param io An I/O context mapping the NVRAM data to be copied and parsed. + * @param cls The NVRAM data class to be used when parsing @p io, or NULL + * to perform runtime identification of the appropriate data class. + * + * @retval 0 success + * @retval non-zero if an error occurs during allocation or initialization, a + * regular unix error code will be returned. + */ +int +bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store, + struct bhnd_nvram_io *io, bhnd_nvram_data_class_t *cls) +{ + struct bhnd_nvram_data *data; + int error; + + + /* Try to parse the data */ + if ((error = bhnd_nvram_data_new(cls, &data, io))) + return (error); + + /* Try to create our new store instance */ + error = bhnd_nvram_store_new(store, data); + bhnd_nvram_data_release(data); + + return (error); +} + +/** + * Free an NVRAM store instance, releasing all associated resources. + * + * @param sc A store instance previously allocated via + * bhnd_nvram_store_new(). + */ +void +bhnd_nvram_store_free(struct bhnd_nvram_store *sc) +{ + struct bhnd_nvstore_path *dpath, *dnext; + + LIST_FOREACH_SAFE(dpath, &sc->paths, dp_link, dnext) { + bhnd_nv_free(dpath->path); + bhnd_nv_free(dpath); + } + + if (sc->pending != NULL) + nvlist_destroy(sc->pending); + + if (sc->idx != NULL) + bhnd_nv_free(sc->idx); + + if (sc->nv != NULL) + bhnd_nvram_data_release(sc->nv); + + BHND_NVSTORE_LOCK_DESTROY(sc); + bhnd_nv_free(sc); +} + +/** + * Read an NVRAM variable. + * + * @param sc The NVRAM parser state. + * @param name The NVRAM variable name. + * @param[out] buf On success, the requested value will be written + * to this buffer. This argment may be NULL if + * the value is not desired. + * @param[in,out] len The capacity of @p buf. On success, will be set + * to the actual size of the requested value. + * @param type The requested data type to be written to @p buf. + * + * @retval 0 success + * @retval ENOENT The requested variable was not found. + * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too + * small to hold the requested value. + * @retval non-zero If reading @p name otherwise fails, a regular unix + * error code will be returned. + */ +int +bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name, + void *buf, size_t *len, bhnd_nvram_type type) +{ + void *cookiep; + const void *inp; + size_t ilen; + bhnd_nvram_type itype; + int error; + + /* + * Search order: + * + * - uncommitted changes + * - index lookup OR buffer scan + */ + + BHND_NVSTORE_LOCK(sc); + + /* Is variable marked for deletion? */ + if (nvlist_exists_null(sc->pending, name)) { + BHND_NVSTORE_UNLOCK(sc); + return (ENOENT); + } + + /* Does an uncommitted value exist? */ + if (nvlist_exists_string(sc->pending, name)) { + /* Uncommited value exists, is not a deletion */ + inp = nvlist_get_string(sc->pending, name); + ilen = strlen(inp) + 1; + itype = BHND_NVRAM_TYPE_STRING; + + /* Coerce borrowed data reference before releasing + * our lock. */ + error = bhnd_nvram_value_coerce(inp, ilen, itype, buf, len, + type); + + BHND_NVSTORE_UNLOCK(sc); + + return (error); + } else if (nvlist_exists(sc->pending, name)) { + BHND_NV_PANIC("invalid value type for pending change %s", name); + } + + /* Fetch variable from parsed NVRAM data. */ + if ((cookiep = bhnd_nvram_index_lookup(sc, name)) == NULL) { + BHND_NVSTORE_UNLOCK(sc); + return (ENOENT); + } + + /* Let the parser itself perform value coercion */ + error = bhnd_nvram_data_getvar(sc->nv, cookiep, buf, len, type); + BHND_NVSTORE_UNLOCK(sc); + + return (error); +} + +/** + * Set an NVRAM variable. + * + * @param sc The NVRAM parser state. + * @param name The NVRAM variable name. + * @param[out] buf The new value. + * @param[in,out] len The size of @p buf. + * @param type The data type of @p buf. + * + * @retval 0 success + * @retval ENOENT The requested variable was not found. + * @retval EINVAL If @p len does not match the expected variable size. + */ +int +bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name, + const void *buf, size_t len, bhnd_nvram_type type) +{ + const char *inp; + char vbuf[512]; + + /* Verify name validity */ + if (!bhnd_nvram_validate_name(name, strlen(name))) + return (EINVAL); + + /* Verify buffer size alignment for the given type. If this is a + * variable width type, a width of 0 will always pass this check */ + if (len % bhnd_nvram_value_size(type, buf, len, 1) != 0) + return (EINVAL); + + /* Determine string format (or directly add variable, if a C string) */ + switch (type) { + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_INT16: + case BHND_NVRAM_TYPE_INT32: + case BHND_NVRAM_TYPE_INT64: + case BHND_NVRAM_TYPE_UINT8_ARRAY: + case BHND_NVRAM_TYPE_UINT16_ARRAY: + case BHND_NVRAM_TYPE_UINT32_ARRAY: + case BHND_NVRAM_TYPE_UINT64_ARRAY: + case BHND_NVRAM_TYPE_INT8_ARRAY: + case BHND_NVRAM_TYPE_INT16_ARRAY: + case BHND_NVRAM_TYPE_INT32_ARRAY: + case BHND_NVRAM_TYPE_INT64_ARRAY: + case BHND_NVRAM_TYPE_CHAR_ARRAY: + case BHND_NVRAM_TYPE_STRING_ARRAY: + // TODO: non-char/string value support + return (EOPNOTSUPP); + + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_STRING: + inp = buf; + + /* Must not exceed buffer size */ + if (len > sizeof(vbuf)) + return (EINVAL); + + /* Must have room for a trailing NUL */ + if (len == sizeof(vbuf) && inp[len-1] != '\0') + return (EINVAL); + + /* Copy out the string value and append trailing NUL */ + strlcpy(vbuf, buf, len); + + /* Add to pending change list */ + BHND_NVSTORE_LOCK(sc); + nvlist_add_string(sc->pending, name, vbuf); + BHND_NVSTORE_UNLOCK(sc); + } + + return (0); +} + +/* sort function for bhnd_nvstore_index cookie values */ +static int +bhnd_nvram_sort_idx(void *ctx, const void *lhs, const void *rhs) +{ + struct bhnd_nvram_store *sc; + const char *l_str, *r_str; + + sc = ctx; + + /* Fetch string pointers from the cookiep values */ + l_str = bhnd_nvram_data_getvar_name(sc->nv, *(void * const *)lhs); + r_str = bhnd_nvram_data_getvar_name(sc->nv, *(void * const *)rhs); + + /* Perform comparison */ + return (strcasecmp(l_str, r_str)); +} + +/** + * Parse and register all device paths and path aliases in @p nvram. + * + * @param sc The NVRAM parser state. + * + * @retval 0 success + * @retval non-zero If registering device paths fails, a regular unix + * error code will be returned. + */ +static int +bhnd_nvram_register_devpaths(struct bhnd_nvram_store *sc) +{ + const char *name; + void *cookiep; + int error; + + /* Skip if backing parser does not support device paths */ + if (!(bhnd_nvram_data_caps(sc->nv) & BHND_NVRAM_DATA_CAP_DEVPATHS)) + return (0); + + /* Parse and register all device path aliases */ + cookiep = NULL; + while ((name = bhnd_nvram_data_next(sc->nv, &cookiep))) { + struct bhnd_nvstore_path *devpath; + const char *suffix; + char *eptr; + char *path; + size_t path_len; + u_long index; + + path = NULL; + + /* Check for devpath prefix */ + if (strncmp(name, "devpath", strlen("devpath")) != 0) + continue; + + /* Parse index value that should follow a 'devpath' prefix */ + suffix = name + strlen("devpath"); + index = strtoul(suffix, &eptr, 10); + if (eptr == suffix || *eptr != '\0') { + BHND_NV_LOG("invalid devpath variable '%s'\n", name); + continue; + } + + /* Determine path value length */ + error = bhnd_nvram_data_getvar(sc->nv, cookiep, NULL, &path_len, + BHND_NVRAM_TYPE_STRING); + if (error) + return (error); + + /* Allocate path buffer */ + if ((path = bhnd_nv_malloc(path_len)) == NULL) + return (ENOMEM); + + /* Decode to our new buffer */ + error = bhnd_nvram_data_getvar(sc->nv, cookiep, path, &path_len, + BHND_NVRAM_TYPE_STRING); + if (error) { + bhnd_nv_free(path); + return (error); + } + + /* Register path alias */ + devpath = bhnd_nv_malloc(sizeof(*devpath)); + if (devpath == NULL) { + bhnd_nv_free(path); + return (ENOMEM); + } + + devpath->index = index; + devpath->path = path; + LIST_INSERT_HEAD(&sc->paths, devpath, dp_link); + } + + return (0); +} + +/** + * Generate all indices for the NVRAM data backing @p nvram. + * + * @param sc The NVRAM parser state. + * + * @retval 0 success + * @retval non-zero If indexing @p nvram fails, a regular unix + * error code will be returned. + */ +static int +bhnd_nvram_generate_index(struct bhnd_nvram_store *sc) +{ + const char *name; + void *cookiep; + size_t idx_bytes; + size_t num_vars; + int error; + + /* Parse and register all device path aliases */ + if ((error = bhnd_nvram_register_devpaths(sc))) + return (error); + + /* Skip generating a variable index if threshold is not met ... */ + num_vars = bhnd_nvram_data_count(sc->nv); + if (num_vars < NVRAM_IDX_VAR_THRESH) + return (0); + + /* ... or if the backing data instance implements indexed lookup + * internally */ + if (bhnd_nvram_data_caps(sc->nv) & BHND_NVRAM_DATA_CAP_INDEXED) + return (0); + + /* Allocate and populate variable index */ + idx_bytes = sizeof(struct bhnd_nvstore_index) + + (sizeof(void *) * num_vars); + sc->idx = bhnd_nv_malloc(idx_bytes); + if (sc->idx == NULL) { + BHND_NV_LOG("error allocating %zu byte index\n", idx_bytes); + goto bad_index; + } + + sc->idx->num_cookiep = num_vars; + +#ifdef _KERNEL + if (bootverbose) { + BHND_NV_LOG("allocated %zu byte index for %zu variables\n", + idx_bytes, num_vars); + } +#endif /* _KERNEL */ + + cookiep = NULL; + for (size_t i = 0; i < sc->idx->num_cookiep; i++) { + /* Fetch next entry */ + name = bhnd_nvram_data_next(sc->nv, &cookiep); + + /* Early EOF */ + if (name == NULL) { + BHND_NV_LOG("indexing failed, expected %zu records " + "(got %zu)\n", sc->idx->num_cookiep, i+1); + goto bad_index; + } + + /* Save the variable's cookiep */ + sc->idx->cookiep[i] = cookiep; + } + + /* Sort the index table */ + qsort_r(sc->idx->cookiep, sc->idx->num_cookiep, + sizeof(sc->idx->cookiep[0]), sc, bhnd_nvram_sort_idx); + + return (0); + +bad_index: + /* Fall back on non-indexed access */ + BHND_NV_LOG("reverting to non-indexed variable lookup\n"); + if (sc->idx != NULL) { + bhnd_nv_free(sc->idx); + sc->idx = NULL; + } + + return (0); +} + + +/** + * Perform an index lookup of @p name, returning the associated cookie + * value, or NULL if the variable does not exist. + * + * @param sc The NVRAM parser state. + * @param name The variable to search for. + */ +static void * +bhnd_nvram_index_lookup(struct bhnd_nvram_store *sc, const char *name) +{ + void *cookiep; + const char *indexed_name; + size_t min, mid, max; + int order; + + BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); + + if (sc->idx == NULL || sc->idx->num_cookiep == 0) + return (bhnd_nvram_data_find(sc->nv, name)); + + /* + * Locate the requested variable using a binary search. + */ + BHND_NV_ASSERT(sc->idx->num_cookiep > 0, + ("empty array causes underflow")); + min = 0; + max = sc->idx->num_cookiep - 1; + + while (max >= min) { + /* Select midpoint */ + mid = (min + max) / 2; + cookiep = sc->idx->cookiep[mid]; + + /* Determine which side of the partition to search */ + indexed_name = bhnd_nvram_data_getvar_name(sc->nv, cookiep); + order = strcasecmp(indexed_name, name); + + if (order < 0) { + /* Search upper partition */ + min = mid + 1; + } else if (order > 0) { + /* Search (non-empty) lower partition */ + if (mid == 0) + break; + max = mid - 1; + } else if (order == 0) { + /* Match found */ + return (cookiep); + } + } + + /* Not found */ + return (NULL); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvramvar.h b/sys/dev/bhnd/nvram/bhnd_nvram_store.h index 890bd2e31741f..28274e20ca652 100644 --- a/sys/dev/bhnd/nvram/bhnd_nvramvar.h +++ b/sys/dev/bhnd/nvram/bhnd_nvram_store.h @@ -29,40 +29,40 @@ * $FreeBSD$ */ -#ifndef _BHND_NVRAM_BHND_NVRAMVAR_H_ -#define _BHND_NVRAM_BHND_NVRAMVAR_H_ +#ifndef _BHND_NVRAM_BHND_NVRAM_STORE_H_ +#define _BHND_NVRAM_BHND_NVRAM_STORE_H_ +#ifdef _KERNEL #include <sys/param.h> #include <sys/bus.h> +#include <sys/nv.h> +#else /* !_KERNEL */ +#include <errno.h> -#include "bhnd_nvram_parser.h" +#include <nv.h> -DECLARE_CLASS(bhnd_nvram_driver); +#include <stdint.h> +#include <stdlib.h> +#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 <sys/queue.h> -/** - * 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_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h b/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h new file mode 100644 index 0000000000000..9dcccfe4dff53 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h @@ -0,0 +1,111 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/types.h> + +#ifndef _KERNEL +#include <pthread.h> +#endif + +#include "bhnd_nvram_store.h" + +/** Index is only generated if minimum variable count is met */ +#define NVRAM_IDX_VAR_THRESH 15 + +#define BHND_NVSTORE_PATH_ALIAS_NONE ULONG_MAX + +LIST_HEAD(bhnd_nvstore_paths, bhnd_nvstore_path); + +/** + * NVRAM store path. + */ +struct bhnd_nvstore_path { + char *path; /** relative path */ + u_long index; /** aliased path index, or + BHND_NVSTORE_PATH_IDX_INVALID */ + + LIST_ENTRY(bhnd_nvstore_path) dp_link; +}; + +/** + * NVRAM store index. + * + * Provides effecient name-based lookup by maintaining an array of cached + * cookiep values, sorted lexicographically by variable name. + */ +struct bhnd_nvstore_index { + size_t num_cookiep; /**< cookiep count */ + void *cookiep[]; /**< cookiep values */ +}; + + +/** bhnd nvram store instance state */ +struct bhnd_nvram_store { +#ifdef _KERNEL + struct mtx mtx; +#else + pthread_mutex_t mtx; +#endif + struct bhnd_nvram_data *nv; /**< backing data */ + struct bhnd_nvstore_index *idx; /**< index, or NULL */ + struct bhnd_nvstore_paths paths; /**< paths */ + nvlist_t *pending; /**< uncommitted writes */ +}; + +#ifdef _KERNEL + +#define BHND_NVSTORE_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, "BHND NVRAM store lock", NULL, MTX_DEF) +#define BHND_NVSTORE_LOCK(sc) mtx_lock(&(sc)->mtx) +#define BHND_NVSTORE_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define BHND_NVSTORE_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what) +#define BHND_NVSTORE_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx) + +#else /* !_KERNEL */ + +#define BHND_NVSTORE_LOCK_INIT(sc) do { \ + int error = pthread_mutex_init(&(sc)->mtx, NULL); \ + if (error) \ + BHND_NV_PANIC("pthread_mutex_init() failed: %d", \ + error); \ +} while(0) + +#define BHND_NVSTORE_LOCK(sc) pthread_mutex_lock(&(sc)->mtx) +#define BHND_NVSTORE_UNLOCK(sc) pthread_mutex_unlock(&(sc)->mtx) +#define BHND_NVSTORE_LOCK_DESTROY(sc) pthread_mutex_destroy(&(sc)->mtx) +#define BHND_NVSTORE_LOCK_ASSERT(sc, what) + +#endif /* _KERNEL */ + +#endif /* _BHND_NVRAM_BHND_NVRAM_STOREVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_subr.c b/sys/dev/bhnd/nvram/bhnd_nvram_subr.c new file mode 100644 index 0000000000000..7fcb4828103cd --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_subr.c @@ -0,0 +1,1271 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> + +#ifdef _KERNEL + +#include <sys/ctype.h> +#include <sys/kernel.h> +#include <sys/limits.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#include <machine/_inttypes.h> + +#else /* !_KERNEL */ + +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#endif /* _KERNEL */ + +#include "bhnd_nvram_io.h" +#include "bhnd_nvram_private.h" +#include "bhnd_nvram_value.h" + +#include "bhnd_nvram_map_data.h" + +/* + * Common NVRAM/SPROM support, including NVRAM variable map + * lookup. + */ + +#ifdef _KERNEL +MALLOC_DEFINE(M_BHND_NVRAM, "bhnd_nvram", "bhnd nvram data"); +#endif + +/** signed/unsigned 32-bit integer value storage */ +union bhnd_nvram_int_storage { + uint32_t u32; + int32_t s32; +}; + +/* + * CRC-8 lookup table used to checksum SPROM and NVRAM data via + * bhnd_nvram_crc8(). + * + * Generated with following parameters: + * polynomial: CRC-8 (x^8 + x^7 + x^6 + x^4 + x^2 + 1) + * reflected bits: false + * reversed: true + */ +const uint8_t bhnd_nvram_crc8_tab[] = { + 0x00, 0xf7, 0xb9, 0x4e, 0x25, 0xd2, 0x9c, 0x6b, 0x4a, 0xbd, 0xf3, + 0x04, 0x6f, 0x98, 0xd6, 0x21, 0x94, 0x63, 0x2d, 0xda, 0xb1, 0x46, + 0x08, 0xff, 0xde, 0x29, 0x67, 0x90, 0xfb, 0x0c, 0x42, 0xb5, 0x7f, + 0x88, 0xc6, 0x31, 0x5a, 0xad, 0xe3, 0x14, 0x35, 0xc2, 0x8c, 0x7b, + 0x10, 0xe7, 0xa9, 0x5e, 0xeb, 0x1c, 0x52, 0xa5, 0xce, 0x39, 0x77, + 0x80, 0xa1, 0x56, 0x18, 0xef, 0x84, 0x73, 0x3d, 0xca, 0xfe, 0x09, + 0x47, 0xb0, 0xdb, 0x2c, 0x62, 0x95, 0xb4, 0x43, 0x0d, 0xfa, 0x91, + 0x66, 0x28, 0xdf, 0x6a, 0x9d, 0xd3, 0x24, 0x4f, 0xb8, 0xf6, 0x01, + 0x20, 0xd7, 0x99, 0x6e, 0x05, 0xf2, 0xbc, 0x4b, 0x81, 0x76, 0x38, + 0xcf, 0xa4, 0x53, 0x1d, 0xea, 0xcb, 0x3c, 0x72, 0x85, 0xee, 0x19, + 0x57, 0xa0, 0x15, 0xe2, 0xac, 0x5b, 0x30, 0xc7, 0x89, 0x7e, 0x5f, + 0xa8, 0xe6, 0x11, 0x7a, 0x8d, 0xc3, 0x34, 0xab, 0x5c, 0x12, 0xe5, + 0x8e, 0x79, 0x37, 0xc0, 0xe1, 0x16, 0x58, 0xaf, 0xc4, 0x33, 0x7d, + 0x8a, 0x3f, 0xc8, 0x86, 0x71, 0x1a, 0xed, 0xa3, 0x54, 0x75, 0x82, + 0xcc, 0x3b, 0x50, 0xa7, 0xe9, 0x1e, 0xd4, 0x23, 0x6d, 0x9a, 0xf1, + 0x06, 0x48, 0xbf, 0x9e, 0x69, 0x27, 0xd0, 0xbb, 0x4c, 0x02, 0xf5, + 0x40, 0xb7, 0xf9, 0x0e, 0x65, 0x92, 0xdc, 0x2b, 0x0a, 0xfd, 0xb3, + 0x44, 0x2f, 0xd8, 0x96, 0x61, 0x55, 0xa2, 0xec, 0x1b, 0x70, 0x87, + 0xc9, 0x3e, 0x1f, 0xe8, 0xa6, 0x51, 0x3a, 0xcd, 0x83, 0x74, 0xc1, + 0x36, 0x78, 0x8f, 0xe4, 0x13, 0x5d, 0xaa, 0x8b, 0x7c, 0x32, 0xc5, + 0xae, 0x59, 0x17, 0xe0, 0x2a, 0xdd, 0x93, 0x64, 0x0f, 0xf8, 0xb6, + 0x41, 0x60, 0x97, 0xd9, 0x2e, 0x45, 0xb2, 0xfc, 0x0b, 0xbe, 0x49, + 0x07, 0xf0, 0x9b, 0x6c, 0x22, 0xd5, 0xf4, 0x03, 0x4d, 0xba, 0xd1, + 0x26, 0x68, 0x9f +}; + +/** + * Return a human readable name for @p type. + * + * @param type The type to query. + */ +const char * +bhnd_nvram_type_name(bhnd_nvram_type type) +{ + switch (type) { + case BHND_NVRAM_TYPE_UINT8: + return ("uint8"); + case BHND_NVRAM_TYPE_UINT16: + return ("uint16"); + case BHND_NVRAM_TYPE_UINT32: + return ("uint32"); + case BHND_NVRAM_TYPE_UINT64: + return ("uint64"); + case BHND_NVRAM_TYPE_CHAR: + return ("char"); + case BHND_NVRAM_TYPE_INT8: + return ("int8"); + case BHND_NVRAM_TYPE_INT16: + return ("int16"); + case BHND_NVRAM_TYPE_INT32: + return ("int32"); + case BHND_NVRAM_TYPE_INT64: + return ("int64"); + case BHND_NVRAM_TYPE_STRING: + return ("string"); + case BHND_NVRAM_TYPE_UINT8_ARRAY: + return ("uint8[]"); + case BHND_NVRAM_TYPE_UINT16_ARRAY: + return ("uint16[]"); + case BHND_NVRAM_TYPE_UINT32_ARRAY: + return ("uint32[]"); + case BHND_NVRAM_TYPE_UINT64_ARRAY: + return ("uint64[]"); + case BHND_NVRAM_TYPE_INT8_ARRAY: + return ("int8[]"); + case BHND_NVRAM_TYPE_INT16_ARRAY: + return ("int16[]"); + case BHND_NVRAM_TYPE_INT32_ARRAY: + return ("int32[]"); + case BHND_NVRAM_TYPE_INT64_ARRAY: + return ("int64[]"); + case BHND_NVRAM_TYPE_CHAR_ARRAY: + return ("char[]"); + case BHND_NVRAM_TYPE_STRING_ARRAY: + return ("string[]"); + } + + /* Quiesce gcc4.2 */ + BHND_NV_PANIC("bhnd nvram type %u unknown", type); +} + +/** + * Return true if @p type is a signed integer type, false otherwise. + * + * Will return false for all array types. + * + * @param type The type to query. + */ +bool +bhnd_nvram_is_signed_type(bhnd_nvram_type type) +{ + switch (type) { + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_INT16: + case BHND_NVRAM_TYPE_INT32: + case BHND_NVRAM_TYPE_INT64: + BHND_NV_ASSERT(bhnd_nvram_is_int_type(type), ("non-int type?")); + return (true); + + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_UINT8_ARRAY: + case BHND_NVRAM_TYPE_UINT16_ARRAY: + case BHND_NVRAM_TYPE_UINT32_ARRAY: + case BHND_NVRAM_TYPE_UINT64_ARRAY: + case BHND_NVRAM_TYPE_INT8_ARRAY: + case BHND_NVRAM_TYPE_INT16_ARRAY: + case BHND_NVRAM_TYPE_INT32_ARRAY: + case BHND_NVRAM_TYPE_INT64_ARRAY: + case BHND_NVRAM_TYPE_CHAR_ARRAY: + case BHND_NVRAM_TYPE_STRING_ARRAY: + return (false); + } + + /* Quiesce gcc4.2 */ + BHND_NV_PANIC("bhnd nvram type %u unknown", type); +} + +/** + * Return true if @p type is an unsigned integer type, false otherwise. + * + * @param type The type to query. + * + * @return Will return false for all array types. + * @return Will return true for BHND_NVRAM_TYPE_CHAR. + */ +bool +bhnd_nvram_is_unsigned_type(bhnd_nvram_type type) +{ + /* If an integer type, must be either signed or unsigned */ + if (!bhnd_nvram_is_int_type(type)) + return (false); + + return (!bhnd_nvram_is_signed_type(type)); +} + +/** + * Return true if bhnd_nvram_is_signed_type() or bhnd_nvram_is_unsigned_type() + * returns true for @p type. + * + * @param type The type to query. + */ +bool +bhnd_nvram_is_int_type(bhnd_nvram_type type) +{ + switch (type) { + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_INT16: + case BHND_NVRAM_TYPE_INT32: + case BHND_NVRAM_TYPE_INT64: + return (true); + + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_UINT8_ARRAY: + case BHND_NVRAM_TYPE_UINT16_ARRAY: + case BHND_NVRAM_TYPE_UINT32_ARRAY: + case BHND_NVRAM_TYPE_UINT64_ARRAY: + case BHND_NVRAM_TYPE_INT8_ARRAY: + case BHND_NVRAM_TYPE_INT16_ARRAY: + case BHND_NVRAM_TYPE_INT32_ARRAY: + case BHND_NVRAM_TYPE_INT64_ARRAY: + case BHND_NVRAM_TYPE_CHAR_ARRAY: + case BHND_NVRAM_TYPE_STRING_ARRAY: + return (false); + } + + /* Quiesce gcc4.2 */ + BHND_NV_PANIC("bhnd nvram type %u unknown", type); +} + +/** + * Return true if @p type is an array type, false otherwise. + * + * @param type The type to query. + */ +bool +bhnd_nvram_is_array_type(bhnd_nvram_type type) +{ + switch (type) { + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_INT16: + case BHND_NVRAM_TYPE_INT32: + case BHND_NVRAM_TYPE_INT64: + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_STRING: + return (false); + + case BHND_NVRAM_TYPE_UINT8_ARRAY: + case BHND_NVRAM_TYPE_UINT16_ARRAY: + case BHND_NVRAM_TYPE_UINT32_ARRAY: + case BHND_NVRAM_TYPE_UINT64_ARRAY: + case BHND_NVRAM_TYPE_INT8_ARRAY: + case BHND_NVRAM_TYPE_INT16_ARRAY: + case BHND_NVRAM_TYPE_INT32_ARRAY: + case BHND_NVRAM_TYPE_INT64_ARRAY: + case BHND_NVRAM_TYPE_CHAR_ARRAY: + case BHND_NVRAM_TYPE_STRING_ARRAY: + return (true); + } + + /* Quiesce gcc4.2 */ + BHND_NV_PANIC("bhnd nvram type %u unknown", type); +} + +/** + * If @p type is an array type, return the base element type. Otherwise, + * returns @p type. + * + * @param type The type to query. + */ +bhnd_nvram_type +bhnd_nvram_base_type(bhnd_nvram_type type) +{ + switch (type) { + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_INT16: + case BHND_NVRAM_TYPE_INT32: + case BHND_NVRAM_TYPE_INT64: + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_STRING: + return (type); + + case BHND_NVRAM_TYPE_UINT8_ARRAY: return (BHND_NVRAM_TYPE_UINT8); + case BHND_NVRAM_TYPE_UINT16_ARRAY: return (BHND_NVRAM_TYPE_UINT16); + case BHND_NVRAM_TYPE_UINT32_ARRAY: return (BHND_NVRAM_TYPE_UINT32); + case BHND_NVRAM_TYPE_UINT64_ARRAY: return (BHND_NVRAM_TYPE_UINT64); + case BHND_NVRAM_TYPE_INT8_ARRAY: return (BHND_NVRAM_TYPE_INT8); + case BHND_NVRAM_TYPE_INT16_ARRAY: return (BHND_NVRAM_TYPE_INT16); + case BHND_NVRAM_TYPE_INT32_ARRAY: return (BHND_NVRAM_TYPE_INT32); + case BHND_NVRAM_TYPE_INT64_ARRAY: return (BHND_NVRAM_TYPE_INT64); + case BHND_NVRAM_TYPE_CHAR_ARRAY: return (BHND_NVRAM_TYPE_CHAR); + case BHND_NVRAM_TYPE_STRING_ARRAY: return (BHND_NVRAM_TYPE_STRING); + } + + /* Quiesce gcc4.2 */ + BHND_NV_PANIC("bhnd nvram type %u unknown", type); +} + +/** + * Calculate the number of elements represented by a value of @p len bytes + * with @p type. + * + * @param type The value type. + * @param data The actual data to be queried, or NULL if unknown. + * @param len The length in bytes of @p data, or if @p data is NULL, + * the expected length in bytes. + * @param[out] nelem On success, the number of elements. If @p type is not + * a fixed width type (e.g. BHND_NVRAM_TYPE_STRING_ARRAY), + * and @p data is NULL, an @p nelem value of 0 will be + * returned. + * + * @retval 0 success + * @retval EFTYPE if @p type is not an array type, and @p len is not + * equal to the size of a single element of @p type. + * @retval EFAULT if @p len is not correctly aligned for elements of + * @p type. + */ +int +bhnd_nvram_value_nelem(bhnd_nvram_type type, const void *data, size_t len, + size_t *nelem) +{ + bhnd_nvram_type base_type; + size_t base_size; + + /* Length must be aligned to the element size */ + base_type = bhnd_nvram_base_type(type); + base_size = bhnd_nvram_value_size(base_type, NULL, 0, 1); + if (base_size != 0 && len % base_size != 0) + return (EFAULT); + + switch (type) { + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_STRING_ARRAY: { + const char *p; + size_t nleft; + + /* Cannot determine the element count without parsing + * the actual data */ + if (data == NULL) { + *nelem = 0; + return (0); + } + + /* Iterate over the NUL-terminated strings to calculate + * total element count */ + p = data; + nleft = len; + *nelem = 0; + while (nleft > 0) { + size_t slen; + + /* Increment element count */ + (*nelem)++; + + /* If not a string array, data must not contain more + * than one entry. */ + if (!bhnd_nvram_is_array_type(type) && *nelem > 1) + return (EFTYPE); + + /* Determine string length */ + slen = strnlen(p, nleft); + nleft -= slen; + + /* Advance input */ + p += slen; + + /* Account for trailing NUL, if we haven't hit the end + * of the input */ + if (nleft > 0) { + nleft--; + p++; + } + } + + return (0); + } + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_INT16: + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_INT32: + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_INT64: + case BHND_NVRAM_TYPE_UINT64: + /* Length must be equal to the size of exactly one + * element (arrays can represent zero elements -- non-array + * types cannot) */ + if (len != base_size) + return (EFTYPE); + *nelem = 1; + return (0); + + case BHND_NVRAM_TYPE_UINT8_ARRAY: + case BHND_NVRAM_TYPE_UINT16_ARRAY: + case BHND_NVRAM_TYPE_UINT32_ARRAY: + case BHND_NVRAM_TYPE_UINT64_ARRAY: + case BHND_NVRAM_TYPE_INT8_ARRAY: + case BHND_NVRAM_TYPE_INT16_ARRAY: + case BHND_NVRAM_TYPE_INT32_ARRAY: + case BHND_NVRAM_TYPE_INT64_ARRAY: + case BHND_NVRAM_TYPE_CHAR_ARRAY: + BHND_NV_ASSERT(base_size != 0, ("invalid base size")); + *nelem = len / base_size; + return (0); + } + + /* Quiesce gcc4.2 */ + BHND_NV_PANIC("bhnd nvram type %u unknown", type); +} + +/** + * Return the size, in bytes, of a value of @p type with @p nelem elements. + * + * @param type The value type. + * @param data The actual data to be queried, or NULL if unknown. If + * NULL and the base type is not a fixed width type + * (e.g. BHND_NVRAM_TYPE_STRING), 0 will be returned. + * @param nbytes The size of @p data, in bytes, or 0 if @p data is NULL. + * @param nelem The number of elements. If @p type is not an array type, + * this value must be 1. + * + * @retval 0 If @p type has a variable width, and @p data is NULL. + * @retval 0 If a @p nelem value greater than 1 is provided for a + * non-array @p type. + * @retval 0 If a @p nelem value of 0 is provided. + * @retval 0 If the result would exceed the maximum value + * representable by size_t. + * @retval non-zero The size, in bytes, of @p type with @p nelem elements. + */ +size_t +bhnd_nvram_value_size(bhnd_nvram_type type, const void *data, size_t nbytes, + size_t nelem) +{ + /* If nelem 0, nothing to do */ + if (nelem == 0) + return (0); + + /* Non-array types must have an nelem value of 1 */ + if (!bhnd_nvram_is_array_type(type) && nelem != 1) + return (0); + + switch (type) { + case BHND_NVRAM_TYPE_UINT8_ARRAY: + case BHND_NVRAM_TYPE_UINT16_ARRAY: + case BHND_NVRAM_TYPE_UINT32_ARRAY: + case BHND_NVRAM_TYPE_UINT64_ARRAY: + case BHND_NVRAM_TYPE_INT8_ARRAY: + case BHND_NVRAM_TYPE_INT16_ARRAY: + case BHND_NVRAM_TYPE_INT32_ARRAY: + case BHND_NVRAM_TYPE_INT64_ARRAY: + case BHND_NVRAM_TYPE_CHAR_ARRAY: { + bhnd_nvram_type base_type; + size_t base_size; + + base_type = bhnd_nvram_base_type(type); + base_size = bhnd_nvram_value_size(base_type, NULL, 0, 1); + + /* Would nelem * base_size overflow? */ + if (SIZE_MAX / nelem < base_size) { + BHND_NV_LOG("cannot represent size %s * %zu\n", + bhnd_nvram_type_name(base_type), nelem); + return (0); + } + + return (nelem * base_size); + } + + case BHND_NVRAM_TYPE_STRING_ARRAY: { + const char *p; + size_t total_size; + + if (data == NULL) + return (0); + + /* Iterate over the NUL-terminated strings to calculate + * total byte length */ + p = data; + total_size = 0; + for (size_t i = 0; i < nelem; i++) { + size_t elem_size; + + elem_size = strnlen(p, nbytes - total_size); + p += elem_size; + + /* Check for (and skip) terminating NUL */ + if (total_size < nbytes && *p == '\0') { + elem_size++; + p++; + } + + /* Would total_size + elem_size overflow? + * + * A memory range larger than SIZE_MAX shouldn't be, + * possible, but include the check for completeness */ + if (SIZE_MAX - total_size < elem_size) + return (0); + + total_size += elem_size; + } + + return (total_size); + } + + case BHND_NVRAM_TYPE_STRING: { + size_t size; + + if (data == NULL) + return (0); + + /* Find length */ + size = strnlen(data, nbytes); + + /* Is there a terminating NUL, or did we just hit the + * end of the string input */ + if (size < nbytes) + size++; + + return (size); + } + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_CHAR: + return (sizeof(uint8_t)); + + case BHND_NVRAM_TYPE_INT16: + case BHND_NVRAM_TYPE_UINT16: + return (sizeof(uint16_t)); + + case BHND_NVRAM_TYPE_INT32: + case BHND_NVRAM_TYPE_UINT32: + return (sizeof(uint32_t)); + + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_INT64: + return (sizeof(uint64_t)); + } + + /* Quiesce gcc4.2 */ + BHND_NV_PANIC("bhnd nvram type %u unknown", type); +} + +/** + * Iterate over all strings in the @p inp string array. + * + * @param inp The string array to be iterated. This must be a buffer + * of one or more NUL-terminated strings -- + * @see BHND_NVRAM_TYPE_STRING_ARRAY. + * @param ilen The size, in bytes, of @p inp, including any + * terminating NUL character(s). + * @param prev The value previously returned by + * bhnd_nvram_string_array_next(), or NULL to begin + * iteration. + * + * @retval non-NULL A reference to the next NUL-terminated string + * @retval NULL If the end of the string array is reached. + */ +const char * +bhnd_nvram_string_array_next(const char *inp, size_t ilen, const char *prev) +{ + size_t nremain, plen; + + if (ilen == 0) + return (NULL); + + if (prev == NULL) + return (inp); + + /* Advance to next value */ + BHND_NV_ASSERT(prev >= inp, ("invalid prev pointer")); + BHND_NV_ASSERT(prev < (inp+ilen), ("invalid prev pointer")); + + nremain = ilen - (size_t)(prev - inp); + plen = strnlen(prev, nremain); + nremain -= plen; + + /* Only a trailing NUL remains? */ + if (nremain <= 1) + return (NULL); + + return (prev + plen + 1); +} + +/** + * Format a string representation of @p inp using @p fmt, with, writing the + * result to @p outp. + * + * Refer to bhnd_nvram_val_vprintf() for full format string documentation. + * + * @param fmt The format string. + * @param inp The value to be formatted. + * @param ilen The size of @p inp, in bytes. + * @param itype The type of @p inp. + * @param[out] outp On success, the string value will be written to + * this buffer. This argment may be NULL if the + * value is not desired. + * @param[in,out] olen The capacity of @p outp. On success, will be set + * to the actual size of the formatted string. + * + * @retval 0 success + * @retval EINVAL If @p fmt contains unrecognized format string + * specifiers. + * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen + * is too small to hold the encoded value. + * @retval EFTYPE If value coercion from @p inp to a string value via + * @p fmt is unsupported. + * @retval ERANGE If value coercion of @p value would overflow (or + * underflow) the representation defined by @p fmt. + */ +int +bhnd_nvram_value_printf(const char *fmt, const void *inp, size_t ilen, + bhnd_nvram_type itype, char *outp, size_t *olen, ...) +{ + va_list ap; + int error; + + va_start(ap, olen); + error = bhnd_nvram_value_vprintf(fmt, inp, ilen, itype, outp, olen, ap); + va_end(ap); + + return (error); +} + +/** + * Format a string representation of @p inp using @p fmt, with, writing the + * result to @p outp. + * + * Refer to bhnd_nvram_val_vprintf() for full format string documentation. + * + * @param fmt The format string. + * @param inp The value to be formatted. + * @param ilen The size of @p inp, in bytes. + * @param itype The type of @p inp. + * @param[out] outp On success, the string value will be written to + * this buffer. This argment may be NULL if the + * value is not desired. + * @param[in,out] olen The capacity of @p outp. On success, will be set + * to the actual size of the formatted string. + * @param ap Argument list. + * + * @retval 0 success + * @retval EINVAL If @p fmt contains unrecognized format string + * specifiers. + * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen + * is too small to hold the encoded value. + * @retval EFTYPE If value coercion from @p inp to a string value via + * @p fmt is unsupported. + * @retval ERANGE If value coercion of @p value would overflow (or + * underflow) the representation defined by @p fmt. + */ +int +bhnd_nvram_value_vprintf(const char *fmt, const void *inp, size_t ilen, + bhnd_nvram_type itype, char *outp, size_t *olen, va_list ap) +{ + bhnd_nvram_val_t val; + int error; + + /* Map input buffer as a value instance */ + error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype, + BHND_NVRAM_VAL_BORROW_DATA); + if (error) + return (error); + + /* Attempt to format the value */ + error = bhnd_nvram_val_vprintf(&val, fmt, outp, olen, ap); + + /* Clean up */ + bhnd_nvram_val_release(&val); + return (error); +} + +/* used by bhnd_nvram_find_vardefn() */ +static int +bhnd_nvram_find_vardefn_compare(const void *key, const void *rhs) +{ + const struct bhnd_nvram_vardefn *r = rhs; + + return (strcmp((const char *)key, r->name)); +} + +/** + * Find and return the variable definition for @p varname, if any. + * + * @param varname variable name + * + * @retval bhnd_nvram_vardefn If a valid definition for @p varname is found. + * @retval NULL If no definition for @p varname is found. + */ +const struct bhnd_nvram_vardefn * +bhnd_nvram_find_vardefn(const char *varname) +{ + return (bsearch(varname, bhnd_nvram_vardefns, bhnd_nvram_num_vardefns, + sizeof(bhnd_nvram_vardefns[0]), bhnd_nvram_find_vardefn_compare)); +} + +/** + * Return the variable ID for a variable definition. + * + * @param defn Variable definition previously returned by + * bhnd_nvram_find_vardefn() or bhnd_nvram_get_vardefn(). + */ +size_t +bhnd_nvram_get_vardefn_id(const struct bhnd_nvram_vardefn *defn) +{ + BHND_NV_ASSERT( + defn >= bhnd_nvram_vardefns && + defn <= &bhnd_nvram_vardefns[bhnd_nvram_num_vardefns-1], + ("invalid variable definition pointer %p", defn)); + + return (defn - bhnd_nvram_vardefns); +} + +/** + * Return the variable definition with the given @p id, or NULL + * if no such variable ID is defined. + * + * @param id variable ID. + * + * @retval bhnd_nvram_vardefn If a valid definition for @p id is found. + * @retval NULL If no definition for @p id is found. + */ +const struct bhnd_nvram_vardefn * +bhnd_nvram_get_vardefn(size_t id) +{ + if (id >= bhnd_nvram_num_vardefns) + return (NULL); + + return (&bhnd_nvram_vardefns[id]); +} + +/** + * Validate an NVRAM variable name. + * + * Scans for special characters (path delimiters, value delimiters, path + * alias prefixes), returning false if the given name cannot be used + * as a relative NVRAM key. + * + * @param name A relative NVRAM variable name to validate. + * @param name_len The length of @p name, in bytes. + * + * @retval true If @p name is a valid relative NVRAM key. + * @retval false If @p name should not be used as a relative NVRAM key. + */ +bool +bhnd_nvram_validate_name(const char *name, size_t name_len) +{ + size_t limit; + + limit = strnlen(name, name_len); + if (limit == 0) + return (false); + + /* Disallow path alias prefixes ([0-9]+:.*) */ + if (limit >= 2 && bhnd_nv_isdigit(*name)) { + for (const char *p = name; (size_t)(p - name) < limit; p++) { + if (bhnd_nv_isdigit(*p)) + continue; + else if (*p == ':') + return (false); + else + break; + } + } + + /* Scan for special characters */ + for (const char *p = name; (size_t)(p - name) < limit; p++) { + switch (*p) { + case '/': /* path delimiter */ + case '=': /* key=value delimiter */ + return (false); + + default: + if (!isascii(*p) || bhnd_nv_isspace(*p)) + return (false); + } + } + + return (true); +} + +/** + * Coerce value @p inp of type @p itype to @p otype, writing the + * result to @p outp. + * + * @param inp The value to be coerced. + * @param ilen The size of @p inp, in bytes. + * @param itype The base data type of @p inp. + * @param[out] outp On success, the value will be written to this + * buffer. This argment may be NULL if the value + * is not desired. + * @param[in,out] olen The capacity of @p outp. On success, will be set + * to the actual size of the requested value. + * @param otype The data type to be written to @p outp. + * + * @retval 0 success + * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too + * small to hold the requested value. + * @retval EFTYPE If the variable data cannot be coerced to @p otype. + * @retval ERANGE If value coercion would overflow @p otype. + */ +int +bhnd_nvram_value_coerce(const void *inp, size_t ilen, bhnd_nvram_type itype, + void *outp, size_t *olen, bhnd_nvram_type otype) +{ + bhnd_nvram_val_t val; + int error; + + /* Wrap input buffer in a value instance */ + error = bhnd_nvram_val_init(&val, NULL, inp, ilen, + itype, BHND_NVRAM_VAL_BORROW_DATA|BHND_NVRAM_VAL_FIXED); + if (error) + return (error); + + /* Try to encode as requested type */ + error = bhnd_nvram_val_encode(&val, outp, olen, otype); + + /* Clean up and return error */ + bhnd_nvram_val_release(&val); + return (error); +} + +/** + * Parses the string in the optionally NUL-terminated @p str to as an integer + * value of @p otype, accepting any integer format supported by the standard + * strtoul(). + * + * - Any leading whitespace in @p str -- as defined by the equivalent of + * calling isspace_l() with an ASCII locale -- will be ignored. + * - A @p str may be prefixed with a single optional '+' or '-' sign denoting + * signedness. + * - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a + * base 16 integer follows. + * - An octal @p str may include a '0' prefix, denoting that an octal integer + * follows. + * + * If a @p base of 0 is specified, the base will be determined according + * to the string's initial prefix, as per strtoul()'s documented behavior. + * + * When parsing a base 16 integer to a signed representation, if no explicit + * sign prefix is given, the string will be parsed as the raw two's complement + * representation of the signed integer value. + * + * @param str The string to be parsed. + * @param maxlen The maximum number of bytes to be read in + * @p str. + * @param base The input string's base (2-36), or 0. + * @param[out] nbytes On success or failure, will be set to the total + * number of parsed bytes. If the total number of + * bytes is not desired, a NULL pointer may be + * provided. + * @param[out] outp On success, the parsed integer value will be + * written to @p outp. This argment may be NULL if + * the value is not desired. + * @param[in,out] olen The capacity of @p outp. On success, will be set + * to the actual size of the requested value. + * @param otype The integer type to be parsed. + * + * @retval 0 success + * @retval EINVAL if an invalid @p base is specified. + * @retval EINVAL if an unsupported (or non-integer) @p otype is + * specified. + * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too + * small to hold the requested value. + * @retval EFTYPE if @p str cannot be parsed as an integer of @p base. + * @retval ERANGE If the integer parsed from @p str is too large to be + * represented as a value of @p otype. + */ +int +bhnd_nvram_parse_int(const char *str, size_t maxlen, u_int base, + size_t *nbytes, void *outp, size_t *olen, bhnd_nvram_type otype) +{ + uint64_t value; + uint64_t carry_max, value_max; + uint64_t type_max; + size_t limit, local_nbytes; + size_t ndigits; + bool negative, sign, twos_compl; + + /* Must be an integer type */ + if (!bhnd_nvram_is_int_type(otype)) + return (EINVAL); + + /* Determine output byte limit */ + if (outp != NULL) + limit = *olen; + else + limit = 0; + + /* We always need a byte count. If the caller provides a NULL nbytes, + * track our position in a stack variable */ + if (nbytes == NULL) + nbytes = &local_nbytes; + + value = 0; + ndigits = 0; + *nbytes = 0; + negative = false; + sign = false; + + /* Validate the specified base */ + if (base != 0 && !(base >= 2 && base <= 36)) + return (EINVAL); + + /* Skip any leading whitespace */ + for (; *nbytes < maxlen; (*nbytes)++) { + if (!bhnd_nv_isspace(str[*nbytes])) + break; + } + + /* Empty string? */ + if (*nbytes == maxlen) + return (EFTYPE); + + /* Parse and skip sign */ + if (str[*nbytes] == '-') { + negative = true; + sign = true; + (*nbytes)++; + } else if (str[*nbytes] == '+') { + sign = true; + (*nbytes)++; + } + + /* Truncated after sign character? */ + if (*nbytes == maxlen) + return (EFTYPE); + + /* Identify (or validate) hex base, skipping 0x/0X prefix */ + if (base == 16 || base == 0) { + /* Check for (and skip) 0x/0X prefix */ + if (maxlen - *nbytes >= 2 && str[*nbytes] == '0' && + (str[*nbytes+1] == 'x' || str[*nbytes+1] == 'X')) + { + base = 16; + (*nbytes) += 2; + } + } + + /* Truncated after hex prefix? */ + if (*nbytes == maxlen) + return (EFTYPE); + + /* Differentiate decimal/octal by looking for a leading 0 */ + if (base == 0) { + if (str[*nbytes] == '0') { + base = 8; + } else { + base = 10; + } + } + + /* Only enable twos-compliment signed integer parsing enabled if the + * input is base 16, and no explicit sign prefix was provided */ + if (!sign && base == 16) + twos_compl = true; + else + twos_compl = false; + + /* Determine the maximum value representable by the requested type */ + switch (otype) { + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_UINT8: + type_max = (uint64_t)UINT8_MAX; + break; + case BHND_NVRAM_TYPE_UINT16: + type_max = (uint64_t)UINT16_MAX; + break; + case BHND_NVRAM_TYPE_UINT32: + type_max = (uint64_t)UINT32_MAX; + break; + case BHND_NVRAM_TYPE_UINT64: + type_max = (uint64_t)UINT64_MAX; + break; + + case BHND_NVRAM_TYPE_INT8: + if (twos_compl) + type_max = (uint64_t)UINT8_MAX; + else if (negative) + type_max = -(uint64_t)INT8_MIN; + else + type_max = (uint64_t)INT8_MAX; + break; + + case BHND_NVRAM_TYPE_INT16: + if (twos_compl) + type_max = (uint64_t)UINT16_MAX; + else if (negative) + type_max = -(uint64_t)INT16_MIN; + else + type_max = (uint64_t)INT16_MAX; + break; + + case BHND_NVRAM_TYPE_INT32: + if (twos_compl) + type_max = (uint64_t)UINT32_MAX; + else if (negative) + type_max = -(uint64_t)INT32_MIN; + else + type_max = (uint64_t)INT32_MAX; + break; + + case BHND_NVRAM_TYPE_INT64: + if (twos_compl) + type_max = (uint64_t)UINT64_MAX; + else if (negative) + type_max = -(uint64_t)INT64_MIN; + else + type_max = (uint64_t)INT64_MAX; + break; + + default: + BHND_NV_LOG("unsupported integer type: %d\n", otype); + return (EINVAL); + } + + /* The maximum value after which an additional carry would overflow */ + value_max = type_max / (uint64_t)base; + + /* The maximum carry value given a value equal to value_max */ + carry_max = type_max % (uint64_t)base; + + /* Consume input until we hit maxlen or a non-digit character */ + for (; *nbytes < maxlen; (*nbytes)++) { + u_long carry; + char c; + + /* Parse carry value */ + c = str[*nbytes]; + if (bhnd_nv_isdigit(c)) { + carry = c - '0'; + } else if (bhnd_nv_isxdigit(c)) { + if (bhnd_nv_isupper(c)) + carry = (c - 'A') + 10; + else + carry = (c - 'a') + 10; + } else { + /* Hit first non-digit character */ + break; + } + + /* If carry is outside the base, it's not a valid digit + * in the current parse context; consider it a non-digit + * character */ + if (carry >= (uint64_t)base) + break; + + /* Increment count of parsed digits */ + ndigits++; + + if (value > value_max) { + /* -Any- carry value would overflow */ + return (ERANGE); + } else if (value == value_max && carry > carry_max) { + /* -This- carry value would overflow */ + return (ERANGE); + } + + value *= (uint64_t)base; + value += carry; + } + + /* If we hit a non-digit character before parsing the first digit, + * we hit an empty integer string. */ + if (ndigits == 0) + return (EFTYPE); + + if (negative) + value = -value; + + /* Provide (and verify) required length */ + *olen = bhnd_nvram_value_size(otype, NULL, 0, 1); + if (outp == NULL) + return (0); + else if (limit < *olen) + return (ENOMEM); + + /* Provide result */ + switch (otype) { + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_UINT8: + *(uint8_t *)outp = (uint8_t)value; + break; + case BHND_NVRAM_TYPE_UINT16: + *(uint16_t *)outp = (uint16_t)value; + break; + case BHND_NVRAM_TYPE_UINT32: + *(uint32_t *)outp = (uint32_t)value; + break; + case BHND_NVRAM_TYPE_UINT64: + *(uint64_t *)outp = (uint64_t)value; + break; + + case BHND_NVRAM_TYPE_INT8: + *(int8_t *)outp = (int8_t)(int64_t)value; + break; + case BHND_NVRAM_TYPE_INT16: + *(int16_t *)outp = (int16_t)(int64_t)value; + break; + case BHND_NVRAM_TYPE_INT32: + *(int32_t *)outp = (int32_t)(int64_t)value; + break; + case BHND_NVRAM_TYPE_INT64: + *(int64_t *)outp = (int64_t)value; + break; + default: + /* unreachable */ + BHND_NV_PANIC("unhandled type %d\n", otype); + } + + return (0); +} + +/** + * Parse a 'name=value' string. + * + * @param env The string to be parsed. + * @param env_len The length of @p envp. + * @param delim The delimiter used in @p envp. This will generally be '='. + * @param[out] name If not NULL, a pointer to the name string. This argument + * may be NULL. + * @param[out] name_len On success, the length of the name substring. This + * argument may be NULL. + * @param[out] value On success, a pointer to the value substring. This argument + * may be NULL. + * @param[out] value_len On success, the length of the value substring. This + * argument may be NULL. + * + * @retval 0 success + * @retval EINVAL if parsing @p envp fails. + */ +int +bhnd_nvram_parse_env(const char *env, size_t env_len, char delim, + const char **name, size_t *name_len, const char **value, size_t *value_len) +{ + const char *p; + + /* Name */ + if ((p = memchr(env, delim, env_len)) == NULL) { + BHND_NV_LOG("delimiter '%c' not found in '%.*s'\n", delim, + BHND_NV_PRINT_WIDTH(env_len), env); + return (EINVAL); + } + + /* Name */ + if (name != NULL) + *name = env; + if (name_len != NULL) + *name_len = p - env; + + /* Skip delim */ + p++; + + /* Value */ + if (value != NULL) + *value = p; + if (value_len != NULL) + *value_len = env_len - (p - env); + + return (0); +} + + +/** + * Parse a field value, returning the actual pointer to the first + * non-whitespace character and the total size of the field. + * + * @param[in,out] inp The field string to parse. Will be updated to point + * at the first non-whitespace character found. + * @param ilen The length of @p inp, in bytes. + * @param delim The field delimiter to search for. + * + * @return Returns the actual size of the field data. + */ +size_t +bhnd_nvram_parse_field(const char **inp, size_t ilen, char delim) +{ + const char *p, *sp; + + /* Skip any leading whitespace */ + for (sp = *inp; (size_t)(sp-*inp) < ilen && bhnd_nv_isspace(*sp); sp++) + continue; + + *inp = sp; + + /* Find the last field character */ + for (p = *inp; (size_t)(p - *inp) < ilen; p++) { + if (*p == delim || *p == '\0') + break; + } + + return (p - *inp); +} + +/** + * Parse a field value, returning the actual pointer to the first + * non-whitespace character and the total size of the field, minus + * any trailing whitespace. + * + * @param[in,out] inp The field string to parse. Will be updated to point + * at the first non-whitespace character found. + * @param ilen The length of the parsed field, in bytes, excluding the + * field elimiter and any trailing whitespace. + * @param delim The field delimiter to search for. + * + * @return Returns the actual size of the field data. + */ +size_t +bhnd_nvram_trim_field(const char **inp, size_t ilen, char delim) +{ + const char *sp; + size_t plen; + + plen = bhnd_nvram_parse_field(inp, ilen, delim); + + /* Trim trailing whitespace */ + sp = *inp; + while (plen > 0) { + if (!bhnd_nv_isspace(*(sp + plen - 1))) + break; + + plen--; + } + + return (plen); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_value.c b/sys/dev/bhnd/nvram/bhnd_nvram_value.c new file mode 100644 index 0000000000000..8454fb6a1a212 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_value.c @@ -0,0 +1,1313 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/sbuf.h> + +#ifdef _KERNEL + +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#include <machine/_inttypes.h> + +#else /* !_KERNEL */ + +#include <inttypes.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_valuevar.h" + + +static void *bhnd_nvram_val_alloc_bytes(bhnd_nvram_val_t *value, + size_t ilen, bhnd_nvram_type itype, + uint32_t flags); +static int bhnd_nvram_val_set(bhnd_nvram_val_t *value, const void *inp, + size_t ilen, bhnd_nvram_type itype, + uint32_t flags); +static int bhnd_nvram_val_set_inline(bhnd_nvram_val_t *value, + const void *inp, size_t ilen, bhnd_nvram_type itype); + +#define BHND_NVRAM_VAL_INITIALIZER(_fmt, _storage) \ + (bhnd_nvram_val_t) { \ + .refs = 1, \ + .val_storage = _storage, \ + .fmt = _fmt, \ + .data_storage = BHND_NVRAM_VAL_DATA_NONE, \ + }; + + +/** Assert that @p value's backing representation state has initialized + * as empty. */ +#define BHND_NVRAM_VAL_ASSERT_EMPTY(_value) \ + BHND_NV_ASSERT( \ + value->data_storage == BHND_NVRAM_VAL_DATA_NONE && \ + value->data_len == 0 && \ + value->data.ptr == NULL, \ + ("previously initialized value")) + +/* Common initialization support for bhnd_nvram_val_init() and + * bhnd_nvram_val_new() */ +static int +bhnd_nvram_val_init_common(bhnd_nvram_val_t *value, bhnd_nvram_val_storage_t + val_storage, const bhnd_nvram_val_fmt_t *fmt, const void *inp, size_t ilen, + bhnd_nvram_type itype, uint32_t flags) +{ + void *outp; + bhnd_nvram_type otype; + size_t olen; + int error; + + /* Determine expected data type, and allow the format to delegate to + * a new format instance */ + if (fmt != NULL && fmt->op_filter != NULL) { + const bhnd_nvram_val_fmt_t *nfmt = fmt; + + /* Use the filter function to determine whether direct + * initialization from is itype permitted */ + error = fmt->op_filter(&nfmt, inp, ilen, itype); + if (error) + return (error); + + /* Retry initialization with new format? */ + if (nfmt != fmt) { + return (bhnd_nvram_val_init_common(value, val_storage, + nfmt, inp, ilen, itype, flags)); + } + + /* Value can be initialized with provided input type */ + otype = itype; + + } else if (fmt != NULL) { + /* Value must be initialized with the format's native + * type */ + otype = fmt->native_type; + + } else { + /* No format specified; we can initialize directly from the + * input data, and we'll handle all format operations + * internally. */ + otype = itype; + } + + /* Initialize value instance */ + *value = BHND_NVRAM_VAL_INITIALIZER(fmt, val_storage); + + /* If input data already in native format, init directly. */ + if (otype == itype) { + error = bhnd_nvram_val_set(value, inp, ilen, itype, flags); + if (error) + return (error); + + return (0); + } + + /* Determine size when encoded in native format */ + error = bhnd_nvram_value_coerce(inp, ilen, itype, NULL, &olen, otype); + if (error) + return (error); + + /* Fetch reference to (or allocate) an appropriately sized buffer */ + outp = bhnd_nvram_val_alloc_bytes(value, olen, otype, flags); + if (outp == NULL) + return (ENOMEM); + + /* Perform encode */ + error = bhnd_nvram_value_coerce(inp, ilen, itype, outp, &olen, otype); + if (error) + return (error); + + return (0); +} + +/** + * Initialize an externally allocated instance of @p value with @p fmt from the + * given @p inp buffer of @p itype and @p ilen. + * + * On success, the caller owns a reference to @p value, and is responsible for + * freeing any resources allocated for @p value via bhnd_nvram_val_release(). + * + * @param value The externally allocated value instance to be + * initialized. + * @param fmt The value's format, or NULL to use the default format + * for @p itype. + * @param inp Input buffer. + * @param ilen Input buffer length. + * @param itype Input buffer type. + * @param flags Value flags (see BHND_NVRAM_VAL_*). + * + * @retval 0 success + * @retval ENOMEM If allocation fails. + * @retval EFTYPE If @p fmt initialization from @p itype is unsupported. + * @retval EFAULT if @p ilen is not correctly aligned for elements of + * @p itype. + * @retval ERANGE If value coercion would overflow (or underflow) the + * @p fmt representation. + */ +int +bhnd_nvram_val_init(bhnd_nvram_val_t *value, const bhnd_nvram_val_fmt_t *fmt, + const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags) +{ + int error; + + error = bhnd_nvram_val_init_common(value, BHND_NVRAM_VAL_STORAGE_AUTO, + fmt, inp, ilen, itype, flags); + if (error) + bhnd_nvram_val_release(value); + + return (error); +} + +/** + * Allocate a value instance with @p fmt, and attempt to initialize its internal + * representation from the given @p inp buffer of @p itype and @p ilen. + * + * On success, the caller owns a reference to @p value, and is responsible for + * freeing any resources allocated for @p value via bhnd_nvram_val_release(). + * + * @param[out] value On success, the allocated value instance. + * @param fmt The value's format, or NULL to use the default format + * for @p itype. + * @param inp Input buffer. + * @param ilen Input buffer length. + * @param itype Input buffer type. + * @param flags Value flags (see BHND_NVRAM_VAL_*). + * + * @retval 0 success + * @retval ENOMEM If allocation fails. + * @retval EFTYPE If @p fmt initialization from @p itype is unsupported. + * @retval EFAULT if @p ilen is not correctly aligned for elements of + * @p itype. + * @retval ERANGE If value coercion would overflow (or underflow) the + * @p fmt representation. + */ +int +bhnd_nvram_val_new(bhnd_nvram_val_t **value, const bhnd_nvram_val_fmt_t *fmt, + const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags) +{ + int error; + + /* Allocate new instance */ + if ((*value = bhnd_nv_malloc(sizeof(**value))) == NULL) + return (ENOMEM); + + /* Perform common initialization. */ + error = bhnd_nvram_val_init_common(*value, + BHND_NVRAM_VAL_STORAGE_DYNAMIC, fmt, inp, ilen, itype, flags); + if (error) { + /* Will also free() the value allocation */ + bhnd_nvram_val_release(*value); + } + + return (error); +} + +/** + * Copy or retain a reference to @p value. + * + * On success, the caller is responsible for freeing the result via + * bhnd_nvram_val_release(). + * + * @param value The value to be copied (or retained). + * + * @retval bhnd_nvram_val_t if @p value was successfully copied or retained. + * @retval NULL if allocation failed. + */ +bhnd_nvram_val_t * +bhnd_nvram_val_copy(bhnd_nvram_val_t *value) +{ + bhnd_nvram_val_t *result; + const void *bytes; + bhnd_nvram_type type; + size_t len; + uint32_t flags; + int error; + + /* If dynamically allocated, simply bump the reference count */ + if (value->val_storage == BHND_NVRAM_VAL_STORAGE_DYNAMIC) { + refcount_acquire(&value->refs); + return (value); + } + + /* Otherwise, we need to perform an actual copy */ + BHND_NV_ASSERT(value->refs == 1, ("non-allocated value has " + "active refcount (%u)", value->refs)); + + /* Compute the new value's flags based on the source value */ + switch (value->data_storage) { + case BHND_NVRAM_VAL_DATA_NONE: + case BHND_NVRAM_VAL_DATA_INLINE: + case BHND_NVRAM_VAL_DATA_EXT_WEAK: + case BHND_NVRAM_VAL_DATA_EXT_ALLOC: + /* Copy the source data and permit additional allocation if the + * value cannot be represented inline */ + flags = BHND_NVRAM_VAL_COPY_DATA|BHND_NVRAM_VAL_DYNAMIC; + break; + case BHND_NVRAM_VAL_DATA_EXT_STATIC: + flags = BHND_NVRAM_VAL_STATIC_DATA; + break; + default: + BHND_NV_PANIC("invalid storage type: %d", value->data_storage); + } + + /* Allocate new value copy */ + bytes = bhnd_nvram_val_bytes(value, &len, &type); + error = bhnd_nvram_val_new(&result, value->fmt, bytes, len, type, + flags); + if (error) { + BHND_NV_LOG("copy failed: %d", error); + return (NULL); + } + + return (result); +} + +/** + * Release a reference to @p value. + * + * If this is the last reference, all associated resources will be freed. + * + * @param value The value to be released. + */ +void +bhnd_nvram_val_release(bhnd_nvram_val_t *value) +{ + BHND_NV_ASSERT(value->refs >= 1, ("value over-released")); + + /* Drop reference */ + if (!refcount_release(&value->refs)) + return; + + /* Free allocated external representation data */ + if (value->data_storage == BHND_NVRAM_VAL_DATA_EXT_ALLOC) + bhnd_nv_free(__DECONST(void *, value->data.ptr)); + + /* Free instance if dynamically allocated */ + if (value->val_storage == BHND_NVRAM_VAL_STORAGE_DYNAMIC) + bhnd_nv_free(value); +} + +/** + * Standard string/char array/char encoding implementation. + * + * Input type must be one of: + * - BHND_NVRAM_TYPE_STRING + * - BHND_NVRAM_TYPE_CHAR + * - BHND_NVRAM_TYPE_CHAR_ARRAY + */ +static int +bhnd_nvram_val_encode_string(void *outp, size_t *olen, bhnd_nvram_type otype, + const void *inp, size_t ilen, bhnd_nvram_type itype) +{ + const char *cstr; + bhnd_nvram_type otype_base; + size_t cstr_size, cstr_len; + size_t limit, nbytes; + + BHND_NV_ASSERT( + itype == BHND_NVRAM_TYPE_STRING || + itype == BHND_NVRAM_TYPE_CHAR || + itype == BHND_NVRAM_TYPE_CHAR_ARRAY, + ("unsupported type: %d", itype)); + + cstr = inp; + cstr_size = ilen; + nbytes = 0; + otype_base = bhnd_nvram_base_type(otype); + + /* Determine output byte limit */ + if (outp != NULL) + limit = *olen; + else + limit = 0; + + /* Determine string length, minus trailing NUL (if any) */ + cstr_len = strnlen(cstr, cstr_size); + + /* Parse the field data */ + switch (otype) { + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_CHAR_ARRAY: + /* String must contain exactly 1 non-terminating-NUL character + * to be represented as a single char */ + if (!bhnd_nvram_is_array_type(otype)) { + if (cstr_len != 1) + return (EFTYPE); + } + + /* Copy out the characters directly (excluding trailing NUL) */ + for (size_t i = 0; i < cstr_len; i++) { + if (limit > nbytes) + *((uint8_t *)outp + nbytes) = cstr[i]; + nbytes++; + } + + /* Provide required length */ + *olen = nbytes; + if (limit < *olen && outp != NULL) + return (ENOMEM); + + return (0); + + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_UINT8_ARRAY: + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_UINT16_ARRAY: + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_UINT32_ARRAY: + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_UINT64_ARRAY: + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_INT8_ARRAY: + case BHND_NVRAM_TYPE_INT16: + case BHND_NVRAM_TYPE_INT16_ARRAY: + case BHND_NVRAM_TYPE_INT32: + case BHND_NVRAM_TYPE_INT32_ARRAY: + case BHND_NVRAM_TYPE_INT64: + case BHND_NVRAM_TYPE_INT64_ARRAY: { + const char *p; + size_t plen, parsed_len; + int error; + + /* Trim leading/trailing whitespace */ + p = cstr; + plen = bhnd_nvram_trim_field(&p, cstr_len, '\0'); + + /* Try to parse the integer value */ + error = bhnd_nvram_parse_int(p, plen, 0, &parsed_len, outp, + olen, otype_base); + if (error) { + BHND_NV_DEBUG("error parsing '%.*s' as integer: %d\n", + BHND_NV_PRINT_WIDTH(plen), p, error); + return (error); + } + + /* Do additional bytes remain unparsed? */ + if (plen != parsed_len) { + BHND_NV_DEBUG("error parsing '%.*s' as a single " + "integer value; trailing garbage '%.*s'\n", + BHND_NV_PRINT_WIDTH(plen), p, + BHND_NV_PRINT_WIDTH(plen-parsed_len), p+parsed_len); + return (EFTYPE); + } + + return (0); + } + + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_STRING_ARRAY: + /* Copy out the string representation as-is */ + *olen = cstr_size; + + /* Need additional space for trailing NUL? */ + if (cstr_len == cstr_size) + (*olen)++; + + /* Skip output? */ + if (outp == NULL) + return (0); + + /* Verify required length */ + if (limit < *olen) + return (ENOMEM); + + /* Copy and NUL terminate */ + strncpy(outp, cstr, cstr_len); + *((char *)outp + cstr_len) = '\0'; + + return (0); + } + + BHND_NV_PANIC("unknown type %s", bhnd_nvram_type_name(otype)); +} + +/** + * Standard integer encoding implementation. + */ +static int +bhnd_nvram_val_encode_int(void *outp, size_t *olen, bhnd_nvram_type otype, + const void *inp, size_t ilen, bhnd_nvram_type itype) +{ + bhnd_nvram_type otype_base; + size_t limit, nbytes; + bool itype_signed, otype_signed, otype_int; + union { + uint64_t u64; + int64_t i64; + } intv; + + BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("non-integer type")); + + /* Determine output byte limit */ + if (outp != NULL) + limit = *olen; + else + limit = 0; + + /* Fetch output type info */ + otype_base = bhnd_nvram_base_type(otype); + otype_int = bhnd_nvram_is_int_type(otype); + otype_signed = bhnd_nvram_is_signed_type(otype_base); + + /* + * Promote integer value to a common 64-bit representation. + */ + switch (itype) { + case BHND_NVRAM_TYPE_UINT8: + if (ilen != sizeof(uint8_t)) + return (EFAULT); + + itype_signed = false; + intv.u64 = *(const uint8_t *)inp; + break; + + case BHND_NVRAM_TYPE_UINT16: + if (ilen != sizeof(uint16_t)) + return (EFAULT); + + itype_signed = false; + intv.u64 = *(const uint16_t *)inp; + break; + + case BHND_NVRAM_TYPE_UINT32: + if (ilen != sizeof(uint32_t)) + return (EFAULT); + + itype_signed = false; + intv.u64 = *(const uint32_t *)inp; + break; + + case BHND_NVRAM_TYPE_UINT64: + if (ilen != sizeof(uint64_t)) + return (EFAULT); + + itype_signed = false; + intv.u64 = *(const uint64_t *)inp; + break; + + case BHND_NVRAM_TYPE_INT8: + if (ilen != sizeof(int8_t)) + return (EFAULT); + + itype_signed = true; + intv.i64 = *(const int8_t *)inp; + break; + + case BHND_NVRAM_TYPE_INT16: + if (ilen != sizeof(int16_t)) + return (EFAULT); + + itype_signed = true; + intv.i64 = *(const int16_t *)inp; + break; + + case BHND_NVRAM_TYPE_INT32: + if (ilen != sizeof(int32_t)) + return (EFAULT); + + itype_signed = true; + intv.i64 = *(const int32_t *)inp; + break; + + case BHND_NVRAM_TYPE_INT64: + if (ilen != sizeof(int32_t)) + return (EFAULT); + + itype_signed = true; + intv.i64 = *(const int32_t *)inp; + break; + + default: + BHND_NV_PANIC("invalid type %d\n", itype); + } + + /* Perform signed/unsigned conversion */ + if (itype_signed && otype_int && !otype_signed) { + if (intv.i64 < 0) { + /* Can't represent negative value */ + BHND_NV_LOG("cannot represent %" PRId64 " as %s\n", + intv.i64, bhnd_nvram_type_name(otype)); + + return (ERANGE); + } + + /* Convert to unsigned representation */ + intv.u64 = intv.i64; + + } else if (!itype_signed && otype_int && otype_signed) { + /* Handle unsigned -> signed coercions */ + if (intv.u64 > INT64_MAX) { + /* Can't represent positive value */ + BHND_NV_LOG("cannot represent %" PRIu64 " as %s\n", + intv.u64, bhnd_nvram_type_name(otype)); + return (ERANGE); + } + + /* Convert to signed representation */ + intv.i64 = intv.u64; + } + + /* Write output */ + switch (otype) { + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_CHAR_ARRAY: + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_UINT8_ARRAY: + if (intv.u64 > UINT8_MAX) + return (ERANGE); + + nbytes = sizeof(uint8_t); + if (limit >= nbytes) + *((uint8_t *)outp) = (uint8_t)intv.u64; + break; + + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_UINT16_ARRAY: + if (intv.u64 > UINT16_MAX) + return (ERANGE); + + nbytes = sizeof(uint16_t); + if (limit >= nbytes) + *((uint16_t *)outp) = (uint16_t)intv.u64; + break; + + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_UINT32_ARRAY: + if (intv.u64 > UINT32_MAX) + return (ERANGE); + + nbytes = sizeof(uint32_t); + if (limit >= nbytes) + *((uint32_t *)outp) = (uint32_t)intv.u64; + break; + + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_UINT64_ARRAY: + nbytes = sizeof(uint64_t); + if (limit >= nbytes) + *((uint64_t *)outp) = intv.u64; + break; + + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_INT8_ARRAY: + if (intv.i64 < INT8_MIN || intv.i64 > INT8_MAX) + return (ERANGE); + + nbytes = sizeof(int8_t); + if (limit >= nbytes) + *((int8_t *)outp) = (int8_t)intv.i64; + break; + + case BHND_NVRAM_TYPE_INT16: + case BHND_NVRAM_TYPE_INT16_ARRAY: + if (intv.i64 < INT16_MIN || intv.i64 > INT16_MAX) + return (ERANGE); + + nbytes = sizeof(int16_t); + if (limit >= nbytes) + *((int16_t *)outp) = (int16_t)intv.i64; + break; + + case BHND_NVRAM_TYPE_INT32: + case BHND_NVRAM_TYPE_INT32_ARRAY: + if (intv.i64 < INT32_MIN || intv.i64 > INT32_MAX) + return (ERANGE); + + nbytes = sizeof(int32_t); + if (limit >= nbytes) + *((int32_t *)outp) = (int32_t)intv.i64; + break; + + case BHND_NVRAM_TYPE_INT64: + case BHND_NVRAM_TYPE_INT64_ARRAY: + nbytes = sizeof(int64_t); + if (limit >= nbytes) + *((int64_t *)outp) = intv.i64; + break; + + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_STRING_ARRAY: { + ssize_t len; + + /* Attempt to write the entry + NUL */ + if (otype_signed) { + len = snprintf(outp, limit, "%" PRId64, intv.i64); + } else { + len = snprintf(outp, limit, "%" PRIu64, intv.u64); + } + + if (len < 0) { + BHND_NV_LOG("snprintf() failed: %zd\n", len); + return (EFTYPE); + } + + /* Set total length to the formatted string length, plus + * trailing NUL */ + nbytes = len + 1; + break; + } + + default: + BHND_NV_LOG("unknown type %s\n", bhnd_nvram_type_name(otype)); + return (EFTYPE); + } + + /* Provide required length */ + *olen = nbytes; + if (limit < *olen) { + if (outp == NULL) + return (0); + + return (ENOMEM); + } + + return (0); +} + +/** + * Encode the given @p value as @p otype, writing the result to @p outp. + * + * @param value The value to be encoded. + * @param[out] outp On success, the value will be written to this + * buffer. This argment may be NULL if the value is + * not desired. + * @param[in,out] olen The capacity of @p outp. On success, will be set + * to the actual size of the requested value. + * @param otype The data type to be written to @p outp. + * + * @retval 0 success + * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen + * is too small to hold the encoded value. + * @retval EFTYPE If value coercion from @p value to @p otype is + * impossible. + * @retval ERANGE If value coercion would overflow (or underflow) the + * a @p otype representation. + */ +int +bhnd_nvram_val_encode(bhnd_nvram_val_t *value, void *outp, size_t *olen, + bhnd_nvram_type otype) +{ + /* Prefer format implementation */ + if (value->fmt != NULL && value->fmt->op_encode != NULL) + return (value->fmt->op_encode(value, outp, olen, otype)); + + return (bhnd_nvram_val_generic_encode(value, outp, olen, otype)); +} + +/** + * Encode the given @p value's element as @p otype, writing the result to + * @p outp. + * + * @param inp The element to be be encoded. Must be a value + * previously returned by bhnd_nvram_val_next() + * or bhnd_nvram_val_elem(). + * @param ilen The size of @p inp, as returned by + * bhnd_nvram_val_next() or bhnd_nvram_val_elem(). + * @param[out] outp On success, the value will be written to this + * buffer. This argment may be NULL if the value is + * not desired. + * @param[in,out] olen The capacity of @p outp. On success, will be set + * to the actual size of the requested value. + * @param otype The data type to be written to @p outp. + * + * @retval 0 success + * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen + * is too small to hold the encoded value. + * @retval EFTYPE If value coercion from @p value to @p otype is + * impossible. + * @retval ERANGE If value coercion would overflow (or underflow) the + * a @p otype representation. + */ +int +bhnd_nvram_val_encode_elem(bhnd_nvram_val_t *value, const void *inp, + size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) +{ + /* Prefer format implementation */ + if (value->fmt != NULL && value->fmt->op_encode_elem != NULL) { + return (value->fmt->op_encode_elem(value, inp, ilen, outp, + olen, otype)); + } + + return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen, outp, + olen, otype)); +} + +/** + * Return the type, size, and a pointer to the internal representation + * of @p value. + * + * @param value The value to be queried. + * @param[out] olen Size of the returned data, in bytes. + * @param[out] otype Data type. + */ +const void * +bhnd_nvram_val_bytes(bhnd_nvram_val_t *value, size_t *olen, + bhnd_nvram_type *otype) +{ + /* Provide type and length */ + *otype = value->data_type; + *olen = value->data_len; + + switch (value->data_storage) { + case BHND_NVRAM_VAL_DATA_EXT_ALLOC: + case BHND_NVRAM_VAL_DATA_EXT_STATIC: + case BHND_NVRAM_VAL_DATA_EXT_WEAK: + /* Return a pointer to external storage */ + return (value->data.ptr); + + case BHND_NVRAM_VAL_DATA_INLINE: + /* Return a pointer to inline storage */ + return (&value->data); + + case BHND_NVRAM_VAL_DATA_NONE: + BHND_NV_PANIC("uninitialized value"); + } + + BHND_NV_PANIC("unknown storage type: %d", value->data_storage); +} + +/** + * Iterate over all array elements in @p value. + * + * @param value The value to be iterated + * @param prev A value pointer previously returned by + * bhnd_nvram_val_next() or bhnd_nvram_val_elem(), + * or NULL to begin iteration at the first element. + * @param[in,out] len If prev is non-NULL, len must be a pointer + * to the length previously returned by + * bhnd_nvram_val_next() or bhnd_nvram_val_elem(). + * On success, will be set to the next element's + * length, in bytes. + * + * @retval non-NULL A borrowed reference to the element data. + * @retval NULL If the end of the element array is reached. + */ +const void * +bhnd_nvram_val_next(bhnd_nvram_val_t *value, const void *prev, size_t *len) +{ + /* Prefer the format implementation */ + if (value->fmt != NULL && value->fmt->op_next != NULL) + return (value->fmt->op_next(value, prev, len)); + + return (bhnd_nvram_val_generic_next(value, prev, len)); +} + +/** + * Return value's element data type. + * + * @param value The value to be queried. + */ +bhnd_nvram_type +bhnd_nvram_val_elem_type(bhnd_nvram_val_t *value) +{ + return (bhnd_nvram_base_type(value->data_type)); +} + +/** + * Return the total number of elements represented by @p value. + */ +size_t +bhnd_nvram_val_nelem(bhnd_nvram_val_t *value) +{ + const void *bytes; + bhnd_nvram_type type; + size_t nelem, len; + int error; + + /* Prefer format implementation */ + if (value->fmt != NULL && value->fmt->op_nelem != NULL) + return (value->fmt->op_nelem(value)); + + /* + * If a custom op_next() is defined, bhnd_nvram_value_nelem() almost + * certainly cannot produce a valid element count; it assumes a standard + * data format that may not apply when custom iteration is required. + * + * Instead, use bhnd_nvram_val_next() to parse the backing data and + * produce a total count. + */ + if (value->fmt != NULL && value->fmt->op_next != NULL) { + const void *next; + + next = NULL; + nelem = 0; + while ((next = bhnd_nvram_val_next(value, next, &len)) != NULL) + nelem++; + + return (nelem); + } + + /* Otherwise, compute the standard element count */ + bytes = bhnd_nvram_val_bytes(value, &len, &type); + if ((error = bhnd_nvram_value_nelem(type, bytes, len, &nelem))) { + /* Should always succeed */ + BHND_NV_PANIC("error calculating element count for type '%s' " + "with length %zu: %d\n", bhnd_nvram_type_name(type), len, + error); + } + + return (nelem); +} + +/** + * Generic implementation of bhnd_nvram_val_op_encode(), compatible with + * all supported NVRAM data types. + */ +int +bhnd_nvram_val_generic_encode(bhnd_nvram_val_t *value, void *outp, size_t *olen, + bhnd_nvram_type otype) +{ + const void *inp; + bhnd_nvram_type itype; + size_t ilen; + const void *next; + bhnd_nvram_type otype_base; + size_t limit, nelem, nbytes; + size_t next_len; + int error; + + nbytes = 0; + nelem = 0; + otype_base = bhnd_nvram_base_type(otype); + + /* + * Normally, a rank polymorphic type like a character array would not + * be representable as a rank 1 type. + * + * As a special-cased exception, we can support conversion directly + * from CHAR_ARRAY to STRING by treating the character array as a + * non-NUL-terminated string. + * + * This conversion is isomorphic; we also support conversion directly + * from a STRING to a CHAR_ARRAY by the same mechanism. + */ + inp = bhnd_nvram_val_bytes(value, &ilen, &itype); + if ((itype == BHND_NVRAM_TYPE_CHAR_ARRAY && + otype == BHND_NVRAM_TYPE_STRING) || + (itype == BHND_NVRAM_TYPE_STRING && + otype == BHND_NVRAM_TYPE_CHAR_ARRAY)) + { + return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen, + otype)); + } + + /* + * If both input and output are non-array types, try to encode them + * without performing element iteration. + */ + if (!bhnd_nvram_is_array_type(itype) && + !bhnd_nvram_is_array_type(otype)) + { + return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen, + otype)); + } + + /* Determine output byte limit */ + if (outp != NULL) + limit = *olen; + else + limit = 0; + + /* Iterate over our array elements and encode as the requested + * type */ + next = NULL; + while ((next = bhnd_nvram_val_next(value, next, &next_len))) { + void *elem_outp; + size_t elem_nbytes; + + /* If the output type is not an array type, we can only encode + * one element */ + nelem++; + if (nelem > 1 && !bhnd_nvram_is_array_type(otype)) { + return (EFTYPE); + } + + /* Determine output offset / limit */ + if (nbytes >= limit) { + elem_nbytes = 0; + elem_outp = NULL; + } else { + elem_nbytes = limit - nbytes; + elem_outp = (uint8_t *)outp + nbytes; + } + + /* Attempt encode */ + error = bhnd_nvram_val_encode_elem(value, next, next_len, + elem_outp, &elem_nbytes, otype_base); + + /* If encoding failed for any reason other than ENOMEM (which + * we'll detect and report below), return immediately */ + if (error && error != ENOMEM) + return (error); + + /* Add to total length */ + if (SIZE_MAX - nbytes < elem_nbytes) + return (EFTYPE); /* would overflow size_t */ + + nbytes += elem_nbytes; + } + + /* Provide the actual length */ + *olen = nbytes; + + /* If no output was requested, nothing left to do */ + if (outp == NULL) + return (0); + + /* Otherwise, report a memory error if the output buffer was too + * small */ + if (limit < nbytes) + return (ENOMEM); + + return (0); +} + +/** + * Generic implementation of bhnd_nvram_val_op_encode_elem(), compatible with + * all supported NVRAM data types. + */ +int +bhnd_nvram_val_generic_encode_elem(bhnd_nvram_val_t *value, const void *inp, + size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) +{ + bhnd_nvram_type itype; + + itype = bhnd_nvram_val_elem_type(value); + switch (itype) { + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_CHAR_ARRAY: + return (bhnd_nvram_val_encode_string(outp, olen, otype, inp, + ilen, itype)); + + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_INT16: + case BHND_NVRAM_TYPE_INT32: + case BHND_NVRAM_TYPE_INT64: + return (bhnd_nvram_val_encode_int(outp, olen, otype, inp, ilen, + itype)); + + default: + BHND_NV_PANIC("missing encode_elem() implementation"); + } +} + +/** + * Generic implementation of bhnd_nvram_val_op_next(), compatible with + * all supported NVRAM data types. + */ +const void * +bhnd_nvram_val_generic_next(bhnd_nvram_val_t *value, const void *prev, + size_t *len) +{ + const uint8_t *inp; + const uint8_t *next; + bhnd_nvram_type itype; + size_t ilen; + size_t offset; + + /* Otherwise, default to iterating over the backing representation + * according to its native representation */ + inp = bhnd_nvram_val_bytes(value, &ilen, &itype); + + /* First element */ + if (prev == NULL) { + /* Zero-length array? */ + if (ilen == 0) + return (NULL); + + *len = bhnd_nvram_value_size(itype, inp, ilen, 1); + return (inp); + } + + /* Advance to next element */ + BHND_NV_ASSERT(prev >= (const void *)inp, ("invalid cookiep")); + next = (const uint8_t *)prev + *len; + offset = (size_t)(next - inp); + + if (offset >= ilen) { + /* Hit end of the array */ + return (NULL); + } + + /* Determine element size */ + *len = bhnd_nvram_value_size(itype, next, ilen - offset, 1); + if (ilen - offset < *len) + BHND_NV_PANIC("short element -- misaligned representation"); + + return (next); +} + +/** + * Initialize the representation of @p value with @p ptr. + * + * If @p value is an externally allocated instance and the representation + * cannot be represented inline, the given data will not be copied, and @p ptr + * must remain valid for the lifetime of @p value. + * + * Otherwise, @p value will be initialized with a copy of the @p ptr. + * + * @param value The value to be initialized. + * @param inp The external representation. + * @param ilen The external representation length, in bytes. + * @param itype The external representation's data type. + * @param flags Value flags. + * + * @retval 0 success. + * @retval ENOMEM if allocation fails + * @retval EFTYPE if @p itype is not an array type, and @p ilen is not + * equal to the size of a single element of @p itype. + * @retval EFAULT if @p ilen is not correctly aligned for elements of + * @p itype. + */ +static int +bhnd_nvram_val_set(bhnd_nvram_val_t *value, const void *inp, size_t ilen, + bhnd_nvram_type itype, uint32_t flags) +{ + void *bytes; + + BHND_NVRAM_VAL_ASSERT_EMPTY(value); + + /* Reference the external data */ + if ((flags & BHND_NVRAM_VAL_BORROW_DATA) || + (flags & BHND_NVRAM_VAL_STATIC_DATA)) + { + if (flags & BHND_NVRAM_VAL_BORROW_DATA) + value->data_storage = BHND_NVRAM_VAL_DATA_EXT_WEAK; + else + value->data_storage = BHND_NVRAM_VAL_DATA_EXT_STATIC; + + value->data.ptr = inp; + value->data_type = itype; + value->data_len = ilen; + return (0); + } + + /* Fetch reference to (or allocate) an appropriately sized buffer */ + bytes = bhnd_nvram_val_alloc_bytes(value, ilen, itype, flags); + if (bytes == NULL) + return (ENOMEM); + + /* Copy data */ + memcpy(bytes, inp, ilen); + + return (0); +} + +/** + * Initialize the internal inline representation of @p value with a copy of + * the data referenced by @p inp of @p itype. + * + * If @p inp is NULL, @p itype and @p ilen will be validated, but no data will + * be copied. + * + * @param value The value to be initialized. + * @param inp The input data to be copied, or NULL to verify + * that data of @p ilen and @p itype can be represented + * inline. + * @param ilen The size of the external buffer to be allocated. + * @param itype The type of the external buffer to be allocated. + * + * @retval 0 success + * @retval ENOMEM if @p ilen is too large to be represented inline. + * @retval EFAULT if @p ilen is not correctly aligned for elements of + * @p itype. + */ +static int +bhnd_nvram_val_set_inline(bhnd_nvram_val_t *value, const void *inp, size_t ilen, + bhnd_nvram_type itype) +{ + BHND_NVRAM_VAL_ASSERT_EMPTY(value); + +#define NV_STORE_INIT_INLINE() do { \ + value->data_len = ilen; \ +} while(0) + +#define NV_STORE_INLINE(_type, _dest) do { \ + if (ilen != sizeof(_type)) \ + return (EFAULT); \ + \ + if (inp != NULL) { \ + value->data._dest[0] = *(const _type *)inp; \ + NV_STORE_INIT_INLINE(); \ + } \ +} while (0) + +#define NV_COPY_ARRRAY_INLINE(_type, _dest) do { \ + if (ilen % sizeof(_type) != 0) \ + return (EFAULT); \ + \ + if (ilen > nitems(value->data. _dest)) \ + return (ENOMEM); \ + \ + if (inp == NULL) \ + return (0); \ + \ + memcpy(&value->data._dest, inp, ilen); \ + if (inp != NULL) { \ + memcpy(&value->data._dest, inp, ilen); \ + NV_STORE_INIT_INLINE(); \ + } \ +} while (0) + + /* Attempt to copy to inline storage */ + switch (itype) { + case BHND_NVRAM_TYPE_CHAR: + NV_STORE_INLINE(uint8_t, ch); + return (0); + + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_INT8: + NV_STORE_INLINE(uint8_t, u8); + return (0); + + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_INT16: + NV_STORE_INLINE(uint16_t, u16); + return (0); + + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_INT32: + NV_STORE_INLINE(uint32_t, u32); + return (0); + + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_INT64: + NV_STORE_INLINE(uint32_t, u32); + return (0); + + case BHND_NVRAM_TYPE_CHAR_ARRAY: + NV_COPY_ARRRAY_INLINE(uint8_t, ch); + return (0); + + case BHND_NVRAM_TYPE_UINT8_ARRAY: + case BHND_NVRAM_TYPE_INT8_ARRAY: + NV_COPY_ARRRAY_INLINE(uint8_t, u8); + return (0); + + case BHND_NVRAM_TYPE_UINT16_ARRAY: + case BHND_NVRAM_TYPE_INT16_ARRAY: + NV_COPY_ARRRAY_INLINE(uint16_t, u16); + return (0); + + case BHND_NVRAM_TYPE_UINT32_ARRAY: + case BHND_NVRAM_TYPE_INT32_ARRAY: + NV_COPY_ARRRAY_INLINE(uint32_t, u32); + return (0); + + case BHND_NVRAM_TYPE_UINT64_ARRAY: + case BHND_NVRAM_TYPE_INT64_ARRAY: + NV_COPY_ARRRAY_INLINE(uint64_t, u64); + return (0); + + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_STRING_ARRAY: + if (ilen > sizeof(value->data.ch)) + return (ENOMEM); + + if (inp != NULL) { + memcpy(&value->data.ch, inp, ilen); + NV_STORE_INIT_INLINE(); + } + + return (0); + } + +#undef NV_STORE_INIT_INLINE +#undef NV_STORE_INLINE +#undef NV_COPY_ARRRAY_INLINE + + BHND_NV_PANIC("unknown data type %d", itype); +} + +/** + * Initialize the internal representation of @p value with a buffer allocation + * of @p len and @p itype, returning a pointer to the allocated buffer. + * + * If a buffer of @p len and @p itype can be represented inline, no + * external buffer will be allocated, and instead a pointer to the inline + * data representation will be returned. + * + * @param value The value to be initialized. + * @param ilen The size of the external buffer to be allocated. + * @param itype The type of the external buffer to be allocated. + * @param flags Value flags. + * + * @retval non-null The newly allocated buffer. + * @retval NULL If allocation failed. + * @retval NULL If @p value is an externally allocated instance. + */ +static void * +bhnd_nvram_val_alloc_bytes(bhnd_nvram_val_t *value, size_t ilen, + bhnd_nvram_type itype, uint32_t flags) +{ + void *ptr; + + BHND_NVRAM_VAL_ASSERT_EMPTY(value); + + /* Can we use inline storage? */ + if (bhnd_nvram_val_set_inline(value, NULL, ilen, itype) == 0) { + BHND_NV_ASSERT(sizeof(value->data) >= ilen, + ("ilen exceeds inline storage")); + + value->data_type = itype; + value->data_len = ilen; + value->data_storage = BHND_NVRAM_VAL_DATA_INLINE; + return (&value->data); + } + + /* Is allocation permitted? */ + if (!(flags & BHND_NVRAM_VAL_DYNAMIC)) + return (NULL); + + /* Allocate external storage */ + if ((ptr = bhnd_nv_malloc(ilen)) == NULL) + return (NULL); + + value->data.ptr = ptr; + value->data_len = ilen; + value->data_type = itype; + value->data_storage = BHND_NVRAM_VAL_DATA_EXT_ALLOC; + + return (ptr); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_value.h b/sys/dev/bhnd/nvram/bhnd_nvram_value.h new file mode 100644 index 0000000000000..65f5f2e616166 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_value.h @@ -0,0 +1,224 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/refcount.h> + +#ifdef _KERNEL +#include <machine/stdarg.h> +#else /* !_KERNEL */ +#include <stdarg.h> +#endif /* _KERNEL */ + +#include "bhnd_nvram.h" + +typedef struct bhnd_nvram_val_fmt bhnd_nvram_val_fmt_t; +typedef struct bhnd_nvram_val bhnd_nvram_val_t; + +int bhnd_nvram_val_init(bhnd_nvram_val_t *value, + const bhnd_nvram_val_fmt_t *fmt, + const void *inp, size_t ilen, + bhnd_nvram_type itype, uint32_t flags); + +int bhnd_nvram_val_new(bhnd_nvram_val_t **value, + const bhnd_nvram_val_fmt_t *fmt, + const void *inp, size_t ilen, + bhnd_nvram_type itype, uint32_t flags); + +bhnd_nvram_val_t *bhnd_nvram_val_copy(bhnd_nvram_val_t *value); + +void bhnd_nvram_val_release( + bhnd_nvram_val_t *value); + +int bhnd_nvram_val_encode(bhnd_nvram_val_t *value, + void *outp, size_t *olen, + bhnd_nvram_type otype); + +int bhnd_nvram_val_encode_elem( + bhnd_nvram_val_t *value, const void *inp, + size_t ilen, void *outp, size_t *olen, + bhnd_nvram_type otype); + +int bhnd_nvram_val_printf(bhnd_nvram_val_t *value, + const char *fmt, char *outp, size_t *olen, + ...); +int bhnd_nvram_val_vprintf(bhnd_nvram_val_t *value, + const char *fmt, char *outp, size_t *olen, + va_list ap); + + +const void *bhnd_nvram_val_bytes(bhnd_nvram_val_t *value, + size_t *len, bhnd_nvram_type *itype); + +bhnd_nvram_type bhnd_nvram_val_elem_type( + bhnd_nvram_val_t *value); + +const void *bhnd_nvram_val_next(bhnd_nvram_val_t *value, + const void *prev, size_t *len); + +size_t bhnd_nvram_val_nelem(bhnd_nvram_val_t *value); + +/** + * NVRAM value flags + */ +enum { + /** + * Do not allocate additional space for value data; all data must be + * represented inline within the value structure (default). + */ + BHND_NVRAM_VAL_FIXED = (0<<0), + + /** + * Automatically allocate additional space for value data if it cannot + * be represented within the value structure. + */ + BHND_NVRAM_VAL_DYNAMIC = (1<<0), + + /** + * Copy the value data upon initialization. (default). + */ + BHND_NVRAM_VAL_COPY_DATA = (0<<1), + + /** + * Do not perform an initial copy of the value data; the data must + * remain valid for the lifetime of the NVRAM value. + * + * Value data will still be copied if the value itself is copied to the + * heap. + */ + BHND_NVRAM_VAL_BORROW_DATA = (1<<1), + + /** + * Do not copy the value data when copying the value to the heap; the + * vlaue data is assumed to be statically allocated and must remain + * valid for the lifetime of the process. + * + * Implies BHND_NVRAM_VAL_BORROW_DATA. + */ + BHND_NVRAM_VAL_STATIC_DATA = (1<<2), +}; + +/** + * @internal + * + * NVRAM value storage types. + */ +typedef enum { + /** + * The value structure has an automatic or static storage duration + * (e.g. it is stack allocated, or is otherwise externally managed), + * and no destructors will be run prior to deallocation of the value. + * + * When performing copy/retain, the existing structure must be copied + * to a new heap allocation. + */ + BHND_NVRAM_VAL_STORAGE_AUTO = 0, + + /** + * The value structure was heap allocated and is fully managed by the + * the NVRAM value API. + * + * When performing copy/retain, the existing structure may be retained + * as-is. + */ + BHND_NVRAM_VAL_STORAGE_DYNAMIC = 2, +} bhnd_nvram_val_storage_t; + +/** + * @internal + * + * NVRAM data storage types. + */ +typedef enum { + /** Value has no active representation. This is the default for + * zero-initialized value structures. */ + BHND_NVRAM_VAL_DATA_NONE = 0, + + /** Value data is represented inline */ + BHND_NVRAM_VAL_DATA_INLINE = 1, + + /** + * Value represented by an external reference to data with a static + * storage location. The data need not be copied if copying the value. + */ + BHND_NVRAM_VAL_DATA_EXT_STATIC = 2, + + /** + * Value represented by weak external reference, which must be copied + * if copying the value */ + BHND_NVRAM_VAL_DATA_EXT_WEAK = 3, + + /** + * Value represented by an external reference that must be deallocated + * when deallocating the value + */ + BHND_NVRAM_VAL_DATA_EXT_ALLOC = 4, +} bhnd_nvram_val_data_storage_t; + +/** + * NVRAM value + */ +struct bhnd_nvram_val { + volatile u_int refs; /**< reference count */ + bhnd_nvram_val_storage_t val_storage; /**< value structure storage */ + const bhnd_nvram_val_fmt_t *fmt; /**< value format, or NULL for default behavior */ + bhnd_nvram_val_data_storage_t data_storage; /**< data storage */ + bhnd_nvram_type data_type; /**< data type */ + size_t data_len; /**< data size */ + + /** data representation */ + union { + uint8_t u8[8]; /**< 8-bit unsigned data */ + uint16_t u16[4]; /**< 16-bit unsigned data */ + uint32_t u32[2]; /**< 32-bit unsigned data */ + uint32_t u64[1]; /**< 64-bit unsigned data */ + int8_t i8[8]; /**< 8-bit signed data */ + int16_t i16[4]; /**< 16-bit signed data */ + int32_t i32[2]; /**< 32-bit signed data */ + int64_t i64[1]; /**< 64-bit signed data */ + unsigned char ch[8]; /**< 8-bit character data */ + const void *ptr; /**< external data */ + } data; +}; + +/** Declare a bhnd_nvram_val_fmt with name @p _n */ +#define BHND_NVRAM_VAL_TYPE_DECL(_n) \ + extern const bhnd_nvram_val_fmt_t bhnd_nvram_val_ ## _n ## _fmt; + +BHND_NVRAM_VAL_TYPE_DECL(bcm_decimal); +BHND_NVRAM_VAL_TYPE_DECL(bcm_hex); +BHND_NVRAM_VAL_TYPE_DECL(bcm_leddc); +BHND_NVRAM_VAL_TYPE_DECL(bcm_macaddr); +BHND_NVRAM_VAL_TYPE_DECL(bcm_string); + +#endif /* _BHND_NVRAM_BHND_NVRAM_VALUE_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c b/sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c new file mode 100644 index 0000000000000..e5c7b85953f30 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c @@ -0,0 +1,1032 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> + +#include <net/ethernet.h> + +#ifdef _KERNEL + +#include <sys/ctype.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#include <machine/_inttypes.h> + +#else /* !_KERNEL */ + +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" + +#include "bhnd_nvram_valuevar.h" + +static bool bhnd_nvram_ident_octet_string(const char *inp, + size_t ilen, char *delim, size_t *nelem); +static bool bhnd_nvram_ident_num_string(const char *inp, + size_t ilen, u_int base, u_int *obase); + +static int bhnd_nvram_val_bcm_macaddr_filter( + const bhnd_nvram_val_fmt_t **fmt, const void *inp, + size_t ilen, bhnd_nvram_type itype); +static int bhnd_nvram_val_bcm_macaddr_encode( + bhnd_nvram_val_t *value, void *outp, size_t *olen, + bhnd_nvram_type otype); + +static int bhnd_nvram_val_bcm_macaddr_string_filter( + const bhnd_nvram_val_fmt_t **fmt, const void *inp, + size_t ilen, bhnd_nvram_type itype); +static int bhnd_nvram_val_bcm_macaddr_string_encode_elem( + bhnd_nvram_val_t *value, const void *inp, + size_t ilen, void *outp, size_t *olen, + bhnd_nvram_type otype); +static const void *bhnd_nvram_val_bcm_macaddr_string_next( + bhnd_nvram_val_t *value, const void *prev, + size_t *len); + + +static int bhnd_nvram_val_bcm_int_filter( + const bhnd_nvram_val_fmt_t **fmt, const void *inp, + size_t ilen, bhnd_nvram_type itype); +static int bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val_t *value, + void *outp, size_t *olen, bhnd_nvram_type otype); + +static int bhnd_nvram_val_bcm_decimal_encode_elem( + bhnd_nvram_val_t *value, const void *inp, + size_t ilen, void *outp, size_t *olen, + bhnd_nvram_type otype); +static int bhnd_nvram_val_bcm_hex_encode_elem( + bhnd_nvram_val_t *value, const void *inp, + size_t ilen, void *outp, size_t *olen, + bhnd_nvram_type otype); + +static int bhnd_nvram_val_bcm_leddc_filter( + const bhnd_nvram_val_fmt_t **fmt, const void *inp, + size_t ilen, bhnd_nvram_type itype); +static int bhnd_nvram_val_bcm_leddc_encode_elem( + bhnd_nvram_val_t *value, const void *inp, + size_t ilen, void *outp, size_t *olen, + bhnd_nvram_type otype); + + +static int bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val_t *value, + void *outp, size_t *olen, bhnd_nvram_type otype); + +static int bhnd_nvram_val_bcmstr_csv_filter( + const bhnd_nvram_val_fmt_t **fmt, const void *inp, + size_t ilen, bhnd_nvram_type itype); +static const void *bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val_t *value, + const void *prev, size_t *len); + +/** + * Broadcom NVRAM MAC address format. + */ +const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_macaddr_fmt = { + .name = "bcm-macaddr", + .native_type = BHND_NVRAM_TYPE_UINT8_ARRAY, + .op_filter = bhnd_nvram_val_bcm_macaddr_filter, + .op_encode = bhnd_nvram_val_bcm_macaddr_encode, +}; + +/** Broadcom NVRAM MAC address string format. */ +static const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_macaddr_string_fmt = { + .name = "bcm-macaddr-string", + .native_type = BHND_NVRAM_TYPE_STRING, + .op_filter = bhnd_nvram_val_bcm_macaddr_string_filter, + .op_encode_elem = bhnd_nvram_val_bcm_macaddr_string_encode_elem, + .op_next = bhnd_nvram_val_bcm_macaddr_string_next, +}; + +/** + * Broadcom NVRAM LED duty-cycle format. + */ +const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_leddc_fmt = { + .name = "bcm-leddc", + .native_type = BHND_NVRAM_TYPE_UINT32, + .op_filter = bhnd_nvram_val_bcm_leddc_filter, + .op_encode_elem = bhnd_nvram_val_bcm_leddc_encode_elem, +}; + + +/** + * Broadcom NVRAM decimal integer format. + * + * Extends standard integer handling, encoding the string representation of + * the integer value as a decimal string: + * - Positive values will be string-encoded without a prefix. + * - Negative values will be string-encoded with a leading '-' sign. + */ +const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_decimal_fmt = { + .name = "bcm-decimal", + .native_type = BHND_NVRAM_TYPE_UINT64, + .op_filter = bhnd_nvram_val_bcm_int_filter, + .op_encode = bhnd_nvram_val_bcm_int_encode, + .op_encode_elem = bhnd_nvram_val_bcm_decimal_encode_elem, +}; + +/** + * Broadcom NVRAM decimal integer format. + * + * Extends standard integer handling, encoding the string representation of + * unsigned and positive signed integer values as an 0x-prefixed hexadecimal + * string. + * + * For compatibility with standard Broadcom NVRAM parsing, if the integer is + * both signed and negative, it will be string encoded as a negative decimal + * value, not as a twos-complement hexadecimal value. + */ +const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_hex_fmt = { + .name = "bcm-hex", + .native_type = BHND_NVRAM_TYPE_UINT64, + .op_filter = bhnd_nvram_val_bcm_int_filter, + .op_encode = bhnd_nvram_val_bcm_int_encode, + .op_encode_elem = bhnd_nvram_val_bcm_hex_encode_elem, +}; + +/** + * Broadcom NVRAM string format. + * + * Handles standard, comma-delimited, and octet-string values as used in + * Broadcom NVRAM data. + */ +const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_string_fmt = { + .name = "bcm-string", + .native_type = BHND_NVRAM_TYPE_STRING, + .op_encode = bhnd_nvram_val_bcmstr_encode, +}; + +/** Broadcom comma-delimited string. */ +static const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_string_csv_fmt = { + .name = "bcm-string[]", + .native_type = BHND_NVRAM_TYPE_STRING, + .op_filter = bhnd_nvram_val_bcmstr_csv_filter, + .op_next = bhnd_nvram_val_bcmstr_csv_next, +}; + +/** + * Common hex/decimal integer filter implementation. + */ +static int +bhnd_nvram_val_bcm_int_filter(const bhnd_nvram_val_fmt_t **fmt, const void *inp, + size_t ilen, bhnd_nvram_type itype) +{ + bhnd_nvram_type itype_base; + + itype_base = bhnd_nvram_base_type(itype); + + switch (itype_base) { + case BHND_NVRAM_TYPE_STRING: + /* + * If the input is a string, delegate to the Broadcom + * string format -- preserving the original string value + * takes priority over enforcing hexadecimal/integer string + * formatting. + */ + *fmt = &bhnd_nvram_val_bcm_string_fmt; + return (0); + + default: + if (bhnd_nvram_is_int_type(itype_base)) + return (0); + + return (EFTYPE); + } +} + +/** + * Broadcom hex/decimal integer encode implementation. + */ +static int +bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val_t *value, void *outp, size_t *olen, + bhnd_nvram_type otype) +{ + /* If encoding to a string, format multiple elements (if any) with a + * comma delimiter. */ + if (otype == BHND_NVRAM_TYPE_STRING) + return (bhnd_nvram_val_printf(value, "%[]s", outp, olen, ",")); + + return (bhnd_nvram_val_generic_encode(value, outp, olen, otype)); +} + +/** + * Broadcom hex integer encode_elem implementation. + */ +static int +bhnd_nvram_val_bcm_hex_encode_elem(bhnd_nvram_val_t *value, const void *inp, + size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) +{ + bhnd_nvram_type itype; + ssize_t width; + int error; + + itype = bhnd_nvram_val_elem_type(value); + BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type")); + + /* If not encoding as a string, perform generic value encoding */ + if (otype != BHND_NVRAM_TYPE_STRING) + return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen, + outp, olen, otype)); + + /* If the value is a signed, negative value, encode as a decimal + * string */ + if (bhnd_nvram_is_signed_type(itype)) { + int64_t sval; + size_t slen; + bhnd_nvram_type stype; + + stype = BHND_NVRAM_TYPE_INT64; + slen = sizeof(sval); + + /* Fetch 64-bit signed representation */ + error = bhnd_nvram_value_coerce(inp, ilen, itype, &sval, &slen, + stype); + if (error) + return (error); + + /* Decimal encoding required? */ + if (sval < 0) + return (bhnd_nvram_value_printf("%I64d", &sval, slen, + stype, outp, olen, otype)); + } + + /* + * Encode the value as a hex string. + * + * Most producers of Broadcom NVRAM values zero-pad hex values out to + * their native width (width * two hex characters), and we do the same + * for compatibility + */ + + width = bhnd_nvram_value_size(itype, NULL, 0, 1) * 2; + + return (bhnd_nvram_value_printf("0x%0*I64X", inp, ilen, itype, + outp, olen, width)); +} + +/** + * Broadcom decimal integer encode_elem implementation. + */ +static int +bhnd_nvram_val_bcm_decimal_encode_elem(bhnd_nvram_val_t *value, const void *inp, + size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) +{ + const char *sfmt; + bhnd_nvram_type itype; + + itype = bhnd_nvram_val_elem_type(value); + BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type")); + + /* If not encoding as a string, perform generic value encoding */ + if (otype != BHND_NVRAM_TYPE_STRING) + return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen, + outp, olen, otype)); + + sfmt = bhnd_nvram_is_signed_type(itype) ? "%I64d" : "%I64u"; + return (bhnd_nvram_value_printf(sfmt, inp, ilen, itype, outp, olen)); +} + +/** + * Broadcom LED duty-cycle filter. + */ +static int +bhnd_nvram_val_bcm_leddc_filter(const bhnd_nvram_val_fmt_t **fmt, + const void *inp, size_t ilen, bhnd_nvram_type itype) +{ + const char *p; + size_t plen; + + switch (itype) { + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_UINT32: + return (0); + + case BHND_NVRAM_TYPE_STRING: + /* Trim any whitespace */ + p = inp; + plen = bhnd_nvram_trim_field(&p, ilen, '\0'); + + /* If the value is not a valid integer string, delegate to the + * Broadcom string format */ + if (!bhnd_nvram_ident_num_string(p, plen, 0, NULL)) + *fmt = &bhnd_nvram_val_bcm_string_fmt; + + return (0); + default: + return (EFTYPE); + } +} + +/** + * Broadcom LED duty-cycle encode. + */ +static int +bhnd_nvram_val_bcm_leddc_encode_elem(bhnd_nvram_val_t *value, const void *inp, + size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) +{ + bhnd_nvram_type itype; + size_t limit, nbytes; + int error; + uint16_t led16; + uint32_t led32; + bool led16_lossy; + union { + uint16_t u16; + uint32_t u32; + } strval; + + /* + * LED duty-cycle values represent the on/off periods as a 32-bit + * integer, with the top 16 bits representing on cycles, and the + * bottom 16 representing off cycles. + * + * LED duty cycle values have three different formats: + * + * - SPROM: A 16-bit unsigned integer, with on/off cycles encoded + * as 8-bit values. + * - NVRAM: A 16-bit decimal or hexadecimal string, with on/off + * cycles encoded as 8-bit values as per the SPROM format. + * - NVRAM: A 32-bit decimal or hexadecimal string, with on/off + * cycles encoded as 16-bit values. + * + * To convert from a 16-bit representation to a 32-bit representation: + * ((value & 0xFF00) << 16) | ((value & 0x00FF) << 8) + * + * To convert from a 32-bit representation to a 16-bit representation, + * perform the same operation in reverse, discarding the lower 8-bits + * of each half of the 32-bit representation: + * ((value >> 16) & 0xFF00) | ((value >> 8) & 0x00FF) + */ + + itype = bhnd_nvram_val_elem_type(value); + nbytes = 0; + led16_lossy = false; + + /* Determine output byte limit */ + if (outp != NULL) + limit = *olen; + else + limit = 0; + + /* If the input/output types match, just delegate to standard value + * encoding support */ + if (otype == itype) { + return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen, + otype)); + } + + /* If our value is a string, it may either be a 16-bit or a 32-bit + * representation of the duty cycle */ + if (itype == BHND_NVRAM_TYPE_STRING) { + const char *p; + uint32_t ival; + size_t nlen, parsed; + + /* Parse integer value */ + p = inp; + nlen = sizeof(ival); + error = bhnd_nvram_parse_int(p, ilen, 0, &parsed, &ival, &nlen, + BHND_NVRAM_TYPE_UINT32); + if (error) + return (error); + + /* Trailing garbage? */ + if (parsed < ilen && *(p+parsed) != '\0') + return (EFTYPE); + + /* Point inp and itype to either our parsed 32-bit or 16-bit + * value */ + inp = &strval; + if (ival & 0xFFFF0000) { + strval.u32 = ival; + itype = BHND_NVRAM_TYPE_UINT32; + } else { + strval.u16 = ival; + itype = BHND_NVRAM_TYPE_UINT16; + } + } + + /* Populate both u32 and (possibly lossy) u16 LEDDC representations */ + switch (itype) { + case BHND_NVRAM_TYPE_UINT16: { + led16 = *(const uint16_t *)inp; + led32 = ((led16 & 0xFF00) << 16) | ((led16 & 0x00FF) << 8); + + /* If all bits are set in the 16-bit value (indicating that + * the value is 'unset' in SPROM), we must update the 32-bit + * representation to match. */ + if (led16 == UINT16_MAX) + led32 = UINT32_MAX; + + break; + } + + case BHND_NVRAM_TYPE_UINT32: + led32 = *(const uint32_t *)inp; + led16 = ((led32 >> 16) & 0xFF00) | ((led32 >> 8) & 0x00FF); + + /* + * Determine whether the led16 conversion is lossy: + * + * - If the lower 8 bits of each half of the 32-bit value + * aren't set, we can safely use the 16-bit representation + * without losing data. + * - If all bits in the 32-bit value are set, the variable is + * treated as unset in SPROM. We can safely use the 16-bit + * representation without losing data. + */ + if ((led32 & 0x00FF00FF) != 0 && led32 != UINT32_MAX) + led16_lossy = true; + + break; + default: + BHND_NV_PANIC("unsupported backing data type: %s", + bhnd_nvram_type_name(itype)); + } + + /* + * Encode as requested output type. + */ + switch (otype) { + case BHND_NVRAM_TYPE_STRING: + /* + * Prefer 16-bit format. + */ + if (!led16_lossy) { + return (bhnd_nvram_value_printf("0x%04hX", &led16, + sizeof(led16), BHND_NVRAM_TYPE_UINT16, outp, olen)); + } else { + return (bhnd_nvram_value_printf("0x%04X", &led32, + sizeof(led32), BHND_NVRAM_TYPE_UINT32, outp, olen)); + } + + break; + + case BHND_NVRAM_TYPE_UINT16: { + /* Can we encode as uint16 without losing data? */ + if (led16_lossy) + return (ERANGE); + + /* Write led16 format */ + nbytes += sizeof(uint16_t); + if (limit >= nbytes) + *(uint16_t *)outp = led16; + + break; + } + + case BHND_NVRAM_TYPE_UINT32: + /* Write led32 format */ + nbytes += sizeof(uint32_t); + if (limit >= nbytes) + *(uint32_t *)outp = led32; + break; + + default: + /* No other output formats are supported */ + return (EFTYPE); + } + + /* Provide the actual length */ + *olen = nbytes; + + /* Report insufficient space (if output was requested) */ + if (limit < nbytes && outp != NULL) + return (ENOMEM); + + return (0); +} + +/** + * Broadcom NVRAM string encoding. + */ +static int +bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val_t *value, void *outp, + size_t *olen, bhnd_nvram_type otype) +{ + bhnd_nvram_val_t array; + const bhnd_nvram_val_fmt_t *array_fmt; + const void *inp; + bhnd_nvram_type itype; + size_t ilen; + int error; + + inp = bhnd_nvram_val_bytes(value, &ilen, &itype); + + /* If the output is not an array type (or if it's a character array), + * we can fall back on standard string encoding */ + if (!bhnd_nvram_is_array_type(otype) || + otype == BHND_NVRAM_TYPE_CHAR_ARRAY) + { + return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen, + otype)); + } + + /* Otherwise, we need to interpret our value as either a macaddr + * string, or a comma-delimited string. */ + inp = bhnd_nvram_val_bytes(value, &ilen, &itype); + if (bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL)) + array_fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt; + else + array_fmt = &bhnd_nvram_val_bcm_string_csv_fmt; + + /* Wrap in array-typed representation */ + error = bhnd_nvram_val_init(&array, array_fmt, inp, ilen, itype, + BHND_NVRAM_VAL_BORROW_DATA); + if (error) { + BHND_NV_LOG("error initializing array representation: %d\n", + error); + return (error); + } + + /* Ask the array-typed value to perform the encode */ + error = bhnd_nvram_val_encode(&array, outp, olen, otype); + if (error) + BHND_NV_LOG("error encoding array representation: %d\n", error); + + bhnd_nvram_val_release(&array); + + return (error); +} + +/** + * Broadcom NVRAM comma-delimited string filter. + */ +static int +bhnd_nvram_val_bcmstr_csv_filter(const bhnd_nvram_val_fmt_t **fmt, + const void *inp, size_t ilen, bhnd_nvram_type itype) +{ + switch (itype) { + case BHND_NVRAM_TYPE_STRING: + case BHND_NVRAM_TYPE_STRING_ARRAY: + return (0); + default: + return (EFTYPE); + } +} + +/** + * Broadcom NVRAM comma-delimited string iteration. + */ +static const void * +bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val_t *value, const void *prev, + size_t *len) +{ + const char *next; + const char *inp; + bhnd_nvram_type itype; + size_t ilen, remain; + char delim; + + /* Fetch backing representation */ + inp = bhnd_nvram_val_bytes(value, &ilen, &itype); + + /* Fetch next value */ + switch (itype) { + case BHND_NVRAM_TYPE_STRING: + /* Zero-length array? */ + if (ilen == 0) + return (NULL); + + if (prev == NULL) { + /* First element */ + next = inp; + remain = ilen; + delim = ','; + } else { + /* Advance to the previous element's delimiter */ + next = (const char *)prev + *len; + + /* Did we hit the end of the string? */ + if ((size_t)(next - inp) >= ilen) + return (NULL); + + /* Fetch (and skip past) the delimiter */ + delim = *next; + next++; + remain = ilen - (size_t)(next - inp); + + /* Was the delimiter the final character? */ + if (remain == 0) + return (NULL); + } + + /* Parse the field value, up to the next delimiter */ + *len = bhnd_nvram_parse_field(&next, remain, delim); + + return (next); + + case BHND_NVRAM_TYPE_STRING_ARRAY: + next = bhnd_nvram_string_array_next(inp, ilen, prev); + if (next != NULL) { + *len = strlen(next); + + /* Account for trailing NUL */ + if (*len + (size_t)(next - inp) < ilen) + (*len)++; + } + + return (next); + default: + BHND_NV_PANIC("unsupported type: %d", itype); + } +} + +/** + * MAC address filter. + */ +static int +bhnd_nvram_val_bcm_macaddr_filter(const bhnd_nvram_val_fmt_t **fmt, + const void *inp, size_t ilen, bhnd_nvram_type itype) +{ + switch (itype) { + case BHND_NVRAM_TYPE_UINT8_ARRAY: + return (0); + case BHND_NVRAM_TYPE_STRING: + /* Let bcm_macaddr_string format handle it */ + *fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt; + return (0); + default: + return (EFTYPE); + } +} + +/** + * MAC address encoding. + */ +static int +bhnd_nvram_val_bcm_macaddr_encode(bhnd_nvram_val_t *value, void *outp, + size_t *olen, bhnd_nvram_type otype) +{ + const void *inp; + bhnd_nvram_type itype; + size_t ilen; + + /* + * If converting to a string (or a single-element string array), + * produce an octet string (00:00:...). + */ + if (bhnd_nvram_base_type(otype) == BHND_NVRAM_TYPE_STRING) { + return (bhnd_nvram_val_printf(value, "%[]02hhX", outp, olen, + ":")); + } + + /* Otherwise, use standard encoding support */ + inp = bhnd_nvram_val_bytes(value, &ilen, &itype); + return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen, otype));} + +/** + * MAC address string filter. + */ +static int +bhnd_nvram_val_bcm_macaddr_string_filter(const bhnd_nvram_val_fmt_t **fmt, + const void *inp, size_t ilen, bhnd_nvram_type itype) +{ + switch (itype) { + case BHND_NVRAM_TYPE_STRING: + /* Use the standard Broadcom string format implementation if + * the input is not an octet string. */ + if (!bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL)) + *fmt = &bhnd_nvram_val_bcm_string_fmt; + + return (0); + default: + return (EFTYPE); + } +} + + +/** + * MAC address string octet encoding. + */ +static int +bhnd_nvram_val_bcm_macaddr_string_encode_elem(bhnd_nvram_val_t *value, + const void *inp, size_t ilen, void *outp, size_t *olen, + bhnd_nvram_type otype) +{ + size_t nparsed; + int error; + + /* If integer encoding is requested, explicitly parse our + * non-0x-prefixed as a base 16 integer value */ + if (bhnd_nvram_is_int_type(otype)) { + error = bhnd_nvram_parse_int(inp, ilen, 16, &nparsed, outp, + olen, otype); + if (error) + return (error); + + if (nparsed != ilen) + return (EFTYPE); + + return (0); + } + + /* Otherwise, use standard encoding support */ + return (bhnd_nvram_value_coerce(inp, ilen, + bhnd_nvram_val_elem_type(value), outp, olen, otype)); +} + +/** + * MAC address string octet iteration. + */ +static const void * +bhnd_nvram_val_bcm_macaddr_string_next(bhnd_nvram_val_t *value, const void *prev, + size_t *len) +{ + const char *next; + const char *str; + bhnd_nvram_type stype; + size_t slen, remain; + char delim; + + /* Fetch backing string */ + str = bhnd_nvram_val_bytes(value, &slen, &stype); + BHND_NV_ASSERT(stype == BHND_NVRAM_TYPE_STRING, + ("unsupported type: %d", stype)); + + /* Zero-length array? */ + if (slen == 0) + return (NULL); + + if (prev == NULL) { + /* First element */ + + /* Determine delimiter */ + if (!bhnd_nvram_ident_octet_string(str, slen, &delim, NULL)) { + /* Default to comma-delimited parsing */ + delim = ','; + } + + /* Parsing will start at the base string pointer */ + next = str; + remain = slen; + } else { + /* Advance to the previous element's delimiter */ + next = (const char *)prev + *len; + + /* Did we hit the end of the string? */ + if ((size_t)(next - str) >= slen) + return (NULL); + + /* Fetch (and skip past) the delimiter */ + delim = *next; + next++; + remain = slen - (size_t)(next - str); + + /* Was the delimiter the final character? */ + if (remain == 0) + return (NULL); + } + + /* Parse the field value, up to the next delimiter */ + *len = bhnd_nvram_parse_field(&next, remain, delim); + + return (next); +} + + +/** + * Determine whether @p inp is in octet string format, consisting of a + * fields of two hex characters, separated with ':' or '-' delimiters. + * + * This may be used to identify MAC address octet strings + * (BHND_NVRAM_SFMT_MACADDR). + * + * @param inp The string to be parsed. + * @param ilen The length of @p inp, in bytes. + * @param[out] delim On success, the delimiter used by this octet + * string. May be set to NULL if the field + * delimiter is not desired. + * @param[out] nelem On success, the number of fields in this + * octet string. May be set to NULL if the field + * count is not desired. + * + * + * @retval true if @p inp is a valid octet string + * @retval false if @p inp is not a valid octet string. + */ +static bool +bhnd_nvram_ident_octet_string(const char *inp, size_t ilen, char *delim, + size_t *nelem) +{ + size_t elem_count; + size_t max_elem_count, min_elem_count; + size_t field_count; + char idelim; + + field_count = 0; + + /* Require exactly two digits. If we relax this, there is room + * for ambiguity with signed integers and the '-' delimiter */ + min_elem_count = 2; + max_elem_count = 2; + + /* Identify the delimiter used. The standard delimiter for MAC + * addresses is ':', but some earlier NVRAM formats may use '-' */ + for (const char *d = ":-";; d++) { + const char *loc; + + /* No delimiter found, not an octet string */ + if (*d == '\0') + return (false); + + /* Look for the delimiter */ + if ((loc = memchr(inp, *d, ilen)) == NULL) + continue; + + /* Delimiter found */ + idelim = *loc; + break; + } + + /* To disambiguate from signed integers, if the delimiter is "-", + * the octets must be exactly 2 chars each */ + if (idelim == '-') + min_elem_count = 2; + + /* String must be composed of individual octets (zero or more hex + * digits) separated by our delimiter. */ + elem_count = 0; + for (const char *p = inp; (size_t)(p - inp) < ilen; p++) { + switch (*p) { + case ':': + case '-': + case '\0': + /* Hit a delim character; all delims must match + * the first delimiter used */ + if (*p != '\0' && *p != idelim) + return (false); + + /* Must have parsed at least min_elem_count digits */ + if (elem_count < min_elem_count) + return (false); + + /* Reset element count */ + elem_count = 0; + + /* Bump field count */ + field_count++; + break; + default: + /* More than maximum number of hex digits? */ + if (elem_count >= max_elem_count) + return (false); + + /* Octet values must be hex digits */ + if (!bhnd_nv_isxdigit(*p)) + return (false); + + elem_count++; + break; + } + } + + if (delim != NULL) + *delim = idelim; + + if (nelem != NULL) + *nelem = field_count; + + return (true); +} + + +/** + * Determine whether @p inp is in hexadecimal, octal, or decimal string + * format. + * + * - A @p str may be prefixed with a single optional '+' or '-' sign denoting + * signedness. + * - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a + * base 16 integer follows. + * - An octal @p str may include a '0' prefix, denoting that an octal integer + * follows. + * + * @param inp The string to be parsed. + * @param ilen The length of @p inp, in bytes. + * @param base The input string's base (2-36), or 0. + * @param[out] obase On success, will be set to the base of the parsed + * integer. May be set to NULL if the base is not + * desired. + * + * @retval true if @p inp is a valid number string + * @retval false if @p inp is not a valid number string. + * @retval false if @p base is invalid. + */ +static bool +bhnd_nvram_ident_num_string(const char *inp, size_t ilen, u_int base, + u_int *obase) +{ + size_t nbytes, ndigits; + + nbytes = 0; + ndigits = 0; + + /* Parse and skip sign */ + if (nbytes >= ilen) + return (false); + + if (inp[nbytes] == '-' || inp[nbytes] == '+') + nbytes++; + + /* Truncated after sign character? */ + if (nbytes == ilen) + return (false); + + /* Identify (or validate) hex base, skipping 0x/0X prefix */ + if (base == 16 || base == 0) { + /* Check for (and skip) 0x/0X prefix */ + if (ilen - nbytes >= 2 && inp[nbytes] == '0' && + (inp[nbytes+1] == 'x' || inp[nbytes+1] == 'X')) + { + base = 16; + nbytes += 2; + } + } + + /* Truncated after hex prefix? */ + if (nbytes == ilen) + return (false); + + /* Differentiate decimal/octal by looking for a leading 0 */ + if (base == 0) { + if (inp[nbytes] == '0') { + base = 8; + } else { + base = 10; + } + } + + /* Consume and validate all remaining digit characters */ + for (; nbytes < ilen; nbytes++) { + u_int carry; + char c; + + /* Parse carry value */ + c = inp[nbytes]; + if (bhnd_nv_isdigit(c)) { + carry = c - '0'; + } else if (bhnd_nv_isxdigit(c)) { + if (bhnd_nv_isupper(c)) + carry = (c - 'A') + 10; + else + carry = (c - 'a') + 10; + } else { + /* Hit a non-digit character */ + return (false); + } + + /* If carry is outside the base, it's not a valid digit + * in the current parse context; consider it a non-digit + * character */ + if (carry >= base) + return (false); + + /* Increment parsed digit count */ + ndigits++; + } + + /* Empty integer string? */ + if (ndigits == 0) + return (false); + + /* Valid integer -- provide the base and return */ + if (obase != NULL) + *obase = base; + return (true); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c b/sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c new file mode 100644 index 0000000000000..ad57fd609050b --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c @@ -0,0 +1,883 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/sbuf.h> + +#ifdef _KERNEL + +#include <sys/ctype.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#include <machine/_inttypes.h> + +#else /* !_KERNEL */ + +#include <ctype.h> +#include <inttypes.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" +#include "bhnd_nvram_valuevar.h" + +#ifdef _KERNEL +#define bhnd_nv_hex2ascii(hex) hex2ascii(hex) +#else /* !_KERNEL */ +static char const bhnd_nv_hex2ascii[] = "0123456789abcdefghijklmnopqrstuvwxyz"; +#define bhnd_nv_hex2ascii(hex) (bhnd_nv_hex2ascii[hex]) +#endif /* _KERNEL */ + +/** + * Maximum size, in bytes, of a string-encoded NVRAM integer value, not + * including any prefix (0x, 0, etc). + * + * We assume the largest possible encoding is the base-2 representation + * of a 64-bit integer. + */ +#define NV_NUMSTR_MAX ((sizeof(uint64_t) * CHAR_BIT) + 1) + +/** + * Format a string representation of @p value using @p fmt, with, writing the + * result to @p outp. + * + * @param value The value to be formatted. + * @param fmt The format string. + * @param[out] outp On success, the string will be written to this + * buffer. This argment may be NULL if the value is + * not desired. + * @param[in,out] olen The capacity of @p outp. On success, will be set + * to the actual number of bytes required for the + * requested string encoding (including a trailing + * NUL). + * + * Refer to bhnd_nvram_val_vprintf() for full format string documentation. + * + * @retval 0 success + * @retval EINVAL If @p fmt contains unrecognized format string + * specifiers. + * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen + * is too small to hold the encoded value. + * @retval EFTYPE If value coercion from @p value to a single string + * value via @p fmt is unsupported. + * @retval ERANGE If value coercion of @p value would overflow (or + * underflow) the representation defined by @p fmt. + */ +int +bhnd_nvram_val_printf(bhnd_nvram_val_t *value, const char *fmt, char *outp, + size_t *olen, ...) +{ + va_list ap; + int error; + + va_start(ap, olen); + error = bhnd_nvram_val_vprintf(value, fmt, outp, olen, ap); + va_end(ap); + + return (error); +} + + +/** + * Format a string representation of the elements of @p value using @p fmt, + * writing the result to @p outp. + * + * @param value The value to be formatted. + * @param fmt The format string. + * @param[out] outp On success, the string will be written to this + * buffer. This argment may be NULL if the value is + * not desired. + * @param[in,out] olen The capacity of @p outp. On success, will be set + * to the actual number of bytes required for the + * requested string encoding (including a trailing + * NUL). + * @param ap Argument list. + * + * @par Format Strings + * + * Value format strings are similar, but not identical to, those used + * by printf(3). + * + * Format specifier format: + * %[repeat][flags][width][.precision][length modifier][specifier] + * + * The format specifier is interpreted as an encoding directive for an + * individual value element; each format specifier will fetch the next element + * from the value, encode the element as the appropriate type based on the + * length modifiers and specifier, and then format the result as a string. + * + * For example, given a string value of '0x000F', and a format specifier of + * '%#hhx', the value will be asked to encode its first element as + * BHND_NVRAM_TYPE_UINT8. String formatting will then be applied to the 8-bit + * unsigned integer representation, producing a string value of "0xF". + * + * Repeat: + * - [digits] Repeatedly apply the format specifier to the input + * value's elements up to `digits` times. The delimiter + * must be passed as a string in the next variadic + * argument. + * - [] Repeatedly apply the format specifier to the input + * value's elements until all elements have been. The + * processed. The delimiter must be passed as a string in + * the next variadic argument. + * - [*] Repeatedly apply the format specifier to the input + * value's elements. The repeat count is read from the + * next variadic argument as a size_t value + * + * Flags: + * - '#' use alternative form (e.g. 0x/0X prefixing of hex + * strings). + * - '0' zero padding + * - '-' left adjust padding + * - '+' include a sign character + * - ' ' include a space in place of a sign character for + * positive numbers. + * + * Width/Precision: + * - digits minimum field width. + * - * read the minimum field width from the next variadic + * argument as a ssize_t value. A negative value enables + * left adjustment. + * - .digits field precision. + * - .* read the field precision from the next variadic argument + * as a ssize_t value. A negative value enables left + * adjustment. + * + * Length Modifiers: + * - 'hh', 'I8' Convert the value to an 8-bit signed or unsigned + * integer. + * - 'h', 'I16' Convert the value to an 16-bit signed or unsigned + * integer. + * - 'l', 'I32' Convert the value to an 32-bit signed or unsigned + * integer. + * - 'll', 'j', 'I64' Convert the value to an 64-bit signed or unsigned + * integer. + * + * Data Specifiers: + * - 'd', 'i' Convert and format as a signed decimal integer. + * - 'u' Convert and format as an unsigned decimal integer. + * - 'o' Convert and format as an unsigned octal integer. + * - 'x' Convert and format as an unsigned hexadecimal integer, + * using lowercase hex digits. + * - 'X' Convert and format as an unsigned hexadecimal integer, + * using uppercase hex digits. + * - 's' Convert and format as a string. + * - '%' Print a literal '%' character. + * + * @retval 0 success + * @retval EINVAL If @p fmt contains unrecognized format string + * specifiers. + * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen + * is too small to hold the encoded value. + * @retval EFTYPE If value coercion from @p value to a single string + * value via @p fmt is unsupported. + * @retval ERANGE If value coercion of @p value would overflow (or + * underflow) the representation defined by @p fmt. + */ +int +bhnd_nvram_val_vprintf(bhnd_nvram_val_t *value, const char *fmt, char *outp, + size_t *olen, va_list ap) +{ + const void *elem; + size_t elen; + size_t limit, nbytes; + int error; + + elem = NULL; + + /* Determine output byte limit */ + nbytes = 0; + if (outp != NULL) + limit = *olen; + else + limit = 0; + +#define WRITE_CHAR(_c) do { \ + if (limit > nbytes) \ + *(outp + nbytes) = _c; \ + \ + if (nbytes == SIZE_MAX) \ + return (EFTYPE); \ + nbytes++; \ +} while (0) + + /* Encode string value as per the format string */ + for (const char *p = fmt; *p != '\0'; p++) { + const char *delim; + size_t precision, width, delim_len; + u_long repeat, bits; + bool alt_form, ladjust, have_precision; + char padc, signc, lenc; + + padc = ' '; + signc = '\0'; + lenc = '\0'; + delim = ""; + delim_len = 0; + + ladjust = false; + alt_form = false; + + have_precision = false; + precision = 1; + bits = 32; + width = 0; + repeat = 1; + + /* Copy all input to output until we hit a format specifier */ + if (*p != '%') { + WRITE_CHAR(*p); + continue; + } + + /* Hit '%' -- is this followed by an escaped '%' literal? */ + p++; + if (*p == '%') { + WRITE_CHAR('%'); + p++; + continue; + } + + /* Parse repeat specifier */ + if (*p == '[') { + p++; + + /* Determine repeat count */ + if (*p == ']') { + /* Repeat consumes all input */ + repeat = bhnd_nvram_val_nelem(value); + } else if (*p == '*') { + /* Repeat is supplied as an argument */ + repeat = va_arg(ap, size_t); + p++; + } else { + char *endp; + + /* Repeat specified as argument */ + repeat = strtoul(p, &endp, 10); + if (p == endp) { + BHND_NV_LOG("error parsing repeat " + "count at '%s'", p); + return (EINVAL); + } + + /* Advance past repeat count */ + p = endp; + } + + /* Advance past terminating ']' */ + if (*p != ']') { + BHND_NV_LOG("error parsing repeat count at " + "'%s'", p); + return (EINVAL); + } + p++; + + delim = va_arg(ap, const char *); + delim_len = strlen(delim); + } + + /* Parse flags */ + while (*p != '\0') { + const char *np; + bool stop; + + stop = false; + np = p+1; + + switch (*p) { + case '#': + alt_form = true; + break; + case '0': + padc = '0'; + break; + case '-': + ladjust = true; + break; + case ' ': + /* Must not override '+' */ + if (signc != '+') + signc = ' '; + break; + case '+': + signc = '+'; + break; + default: + /* Non-flag character */ + stop = true; + break; + } + + if (stop) + break; + else + p = np; + } + + /* Parse minimum width */ + if (*p == '*') { + ssize_t arg; + + /* Width is supplied as an argument */ + arg = va_arg(ap, int); + + /* Negative width argument is interpreted as + * '-' flag followed by positive width */ + if (arg < 0) { + ladjust = true; + arg = -arg; + } + + width = arg; + p++; + } else if (bhnd_nv_isdigit(*p)) { + uint32_t v; + size_t len, parsed; + + /* Parse width value */ + len = sizeof(v); + error = bhnd_nvram_parse_int(p, strlen(p), 10, &parsed, + &v, &len, BHND_NVRAM_TYPE_UINT32); + if (error) { + BHND_NV_LOG("error parsing width %s: %d\n", p, + error); + return (EINVAL); + } + + /* Save width and advance input */ + width = v; + p += parsed; + } + + /* Parse precision */ + if (*p == '.') { + uint32_t v; + size_t len, parsed; + + p++; + have_precision = true; + + if (*p == '*') { + ssize_t arg; + + /* Precision is specified as an argument */ + arg = va_arg(ap, int); + + /* Negative precision argument is interpreted + * as '-' flag followed by positive + * precision */ + if (arg < 0) { + ladjust = true; + arg = -arg; + } + + precision = arg; + } else if (!bhnd_nv_isdigit(*p)) { + /* Implicit precision of 0 */ + precision = 0; + } else { + /* Parse precision value */ + len = sizeof(v); + error = bhnd_nvram_parse_int(p, strlen(p), 10, + &parsed, &v, &len, + BHND_NVRAM_TYPE_UINT32); + if (error) { + BHND_NV_LOG("error parsing width %s: " + "%d\n", p, error); + return (EINVAL); + } + + /* Save precision and advance input */ + precision = v; + p += parsed; + } + } + + /* Parse length modifiers */ + while (*p != '\0') { + const char *np; + bool stop; + + stop = false; + np = p+1; + + switch (*p) { + case 'h': + if (lenc == '\0') { + /* Set initial length value */ + lenc = *p; + bits = 16; + } else if (lenc == *p && bits == 16) { + /* Modify previous length value */ + bits = 8; + } else { + BHND_NV_LOG("invalid length modifier " + "%c\n", *p); + return (EINVAL); + } + break; + + case 'l': + if (lenc == '\0') { + /* Set initial length value */ + lenc = *p; + bits = 32; + } else if (lenc == *p && bits == 32) { + /* Modify previous length value */ + bits = 64; + } else { + BHND_NV_LOG("invalid length modifier " + "%c\n", *p); + return (EINVAL); + } + break; + + case 'j': + /* Conflicts with all other length + * specifications, and may only occur once */ + if (lenc != '\0') { + BHND_NV_LOG("invalid length modifier " + "%c\n", *p); + return (EINVAL); + } + + lenc = *p; + bits = 64; + break; + + case 'I': { + char *endp; + + /* Conflicts with all other length + * specifications, and may only occur once */ + if (lenc != '\0') { + BHND_NV_LOG("invalid length modifier " + "%c\n", *p); + return (EINVAL); + } + + lenc = *p; + + /* Parse the length specifier value */ + p++; + bits = strtoul(p, &endp, 10); + if (p == endp) { + BHND_NV_LOG("invalid size specifier: " + "%s\n", p); + return (EINVAL); + } + + /* Advance input past the parsed integer */ + np = endp; + break; + } + default: + /* Non-length modifier character */ + stop = true; + break; + } + + if (stop) + break; + else + p = np; + } + + /* Parse conversion specifier and format the value(s) */ + for (u_long n = 0; n < repeat; n++) { + bhnd_nvram_type arg_type; + size_t arg_size; + size_t i; + u_long base; + bool is_signed, is_upper; + + is_signed = false; + is_upper = false; + base = 0; + + /* Fetch next element */ + elem = bhnd_nvram_val_next(value, elem, &elen); + if (elem == NULL) { + BHND_NV_LOG("format string references more " + "than %zu available value elements\n", + bhnd_nvram_val_nelem(value)); + return (EINVAL); + } + + /* + * If this is not the first value, append the delimiter. + */ + if (n > 0) { + size_t nremain = 0; + if (limit > nbytes) + nremain = limit - nbytes; + + if (nremain >= delim_len) + memcpy(outp + nbytes, delim, delim_len); + + /* Add delimiter length to the total byte count */ + if (SIZE_MAX - nbytes < delim_len) + return (EFTYPE); /* overflows size_t */ + + nbytes += delim_len; + } + + /* Parse integer conversion specifiers */ + switch (*p) { + case 'd': + case 'i': + base = 10; + is_signed = true; + break; + + case 'u': + base = 10; + break; + + case 'o': + base = 8; + break; + + case 'x': + base = 16; + break; + + case 'X': + base = 16; + is_upper = true; + break; + } + + /* Format argument */ + switch (*p) { +#define NV_ENCODE_INT(_width) do { \ + arg_type = (is_signed) ? BHND_NVRAM_TYPE_INT ## _width : \ + BHND_NVRAM_TYPE_UINT ## _width; \ + arg_size = sizeof(v.u ## _width); \ + error = bhnd_nvram_val_encode_elem(value, elem, elen, \ + &v.u ## _width, &arg_size, arg_type); \ + if (error) { \ + BHND_NV_LOG("error encoding argument as %s: %d\n", \ + bhnd_nvram_type_name(arg_type), error); \ + return (error); \ + } \ + \ + if (is_signed) { \ + if (v.i ## _width < 0) { \ + add_neg = true; \ + numval = (int64_t)-(v.i ## _width); \ + } else { \ + numval = (int64_t) (v.i ## _width); \ + } \ + } else { \ + numval = v.u ## _width; \ + } \ +} while(0) + case 'd': + case 'i': + case 'u': + case 'o': + case 'x': + case 'X': { + char numbuf[NV_NUMSTR_MAX]; + char *sptr; + uint64_t numval; + size_t slen; + bool add_neg; + union { + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + } v; + + add_neg = false; + + /* If precision is specified, it overrides + * (and behaves identically) to a zero-prefixed + * minimum width */ + if (have_precision) { + padc = '0'; + width = precision; + ladjust = false; + } + + /* If zero-padding is used, value must be right + * adjusted */ + if (padc == '0') + ladjust = false; + + /* Request encode to the appropriate integer + * type, and then promote to common 64-bit + * representation */ + switch (bits) { + case 8: + NV_ENCODE_INT(8); + break; + case 16: + NV_ENCODE_INT(16); + break; + case 32: + NV_ENCODE_INT(32); + break; + case 64: + NV_ENCODE_INT(64); + break; + default: + BHND_NV_LOG("invalid length specifier: " + "%lu\n", bits); + return (EINVAL); + } +#undef NV_ENCODE_INT + + /* If a precision of 0 is specified and the + * value is also zero, no characters should + * be produced */ + if (have_precision && precision == 0 && + numval == 0) + { + break; + } + + /* Emit string representation to local buffer */ + BHND_NV_ASSERT(base <= 16, ("invalid base")); + sptr = numbuf + nitems(numbuf) - 1; + for (slen = 0; slen < sizeof(numbuf); slen++) { + char c; + uint64_t n; + + n = numval % base; + c = bhnd_nv_hex2ascii(n); + if (is_upper) + c = bhnd_nv_toupper(c); + + sptr--; + *sptr = c; + + numval /= (uint64_t)base; + if (numval == 0) { + slen++; + break; + } + } + + arg_size = slen; + + /* Reserve space for 0/0x prefix? */ + if (alt_form) { + if (numval == 0) { + /* If 0, no prefix */ + alt_form = false; + } else if (base == 8) { + arg_size += 1; /* 0 */ + } else if (base == 16) { + arg_size += 2; /* 0x/0X */ + } + } + + /* Reserve space for ' ', '+', or '-' prefix? */ + if (add_neg || signc != '\0') { + if (add_neg) + signc = '-'; + + arg_size++; + } + + /* Right adjust (if using spaces) */ + if (!ladjust && padc != '0') { + for (i = arg_size; i < width; i++) + WRITE_CHAR(padc); + } + + if (signc != '\0') + WRITE_CHAR(signc); + + if (alt_form) { + if (base == 8) { + WRITE_CHAR('0'); + } else if (base == 16) { + WRITE_CHAR('0'); + if (is_upper) + WRITE_CHAR('X'); + else + WRITE_CHAR('x'); + } + } + + /* Right adjust (if using zeros) */ + if (!ladjust && padc == '0') { + for (i = slen; i < width; i++) + WRITE_CHAR(padc); + } + + /* Write the string to our output buffer */ + if (limit > nbytes && limit - nbytes >= slen) + memcpy(outp + nbytes, sptr, slen); + + /* Update the total byte count */ + if (SIZE_MAX - nbytes < arg_size) + return (EFTYPE); /* overflows size_t */ + + nbytes += arg_size; + + /* Left adjust */ + for (i = arg_size; ladjust && i < width; i++) + WRITE_CHAR(padc); + + break; + } + + case 's': { + char *s; + size_t slen; + + /* Query the total length of the element when + * converted to a string */ + arg_type = BHND_NVRAM_TYPE_STRING; + error = bhnd_nvram_val_encode_elem(value, elem, + elen, NULL, &arg_size, arg_type); + if (error) { + BHND_NV_LOG("error encoding argument " + "as %s: %d\n", + bhnd_nvram_type_name(arg_type), + error); + return (error); + } + + /* Do not include trailing NUL in the string + * length */ + if (arg_size > 0) + arg_size--; + + /* Right adjust */ + for (i = arg_size; !ladjust && i < width; i++) + WRITE_CHAR(padc); + + /* Determine output positition and remaining + * buffer space */ + if (limit > nbytes) { + s = outp + nbytes; + slen = limit - nbytes; + } else { + s = NULL; + slen = 0; + } + + /* Encode the string to our output buffer */ + error = bhnd_nvram_val_encode_elem(value, elem, + elen, s, &slen, arg_type); + if (error && error != ENOMEM) { + BHND_NV_LOG("error encoding argument " + "as %s: %d\n", + bhnd_nvram_type_name(arg_type), + error); + return (error); + } + + /* Update the total byte count */ + if (SIZE_MAX - nbytes < arg_size) + return (EFTYPE); /* overflows size_t */ + + nbytes += arg_size; + + /* Left adjust */ + for (i = arg_size; ladjust && i < width; i++) + WRITE_CHAR(padc); + + break; + } + + case 'c': { + char c; + + arg_type = BHND_NVRAM_TYPE_CHAR; + arg_size = bhnd_nvram_value_size(arg_type, NULL, + 0, 1); + + /* Encode as single character */ + error = bhnd_nvram_val_encode_elem(value, elem, + elen, &c, &arg_size, arg_type); + if (error) { + BHND_NV_LOG("error encoding argument " + "as %s: %d\n", + bhnd_nvram_type_name(arg_type), + error); + return (error); + } + + BHND_NV_ASSERT(arg_size == sizeof(c), + ("invalid encoded size")); + + /* Right adjust */ + for (i = arg_size; !ladjust && i < width; i++) + WRITE_CHAR(padc); + + WRITE_CHAR(padc); + + /* Left adjust */ + for (i = arg_size; ladjust && i < width; i++) + WRITE_CHAR(padc); + + break; + } + } + } + } + + /* Append terminating NUL */ + if (limit > nbytes) + *(outp + nbytes) = '\0'; + + if (nbytes < SIZE_MAX) + nbytes++; + else + return (EFTYPE); + + /* Report required space */ + *olen = nbytes; + if (limit < nbytes) { + if (outp != NULL) + return (ENOMEM); + } + + return (0); +} diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_valuevar.h b/sys/dev/bhnd/nvram/bhnd_nvram_valuevar.h new file mode 100644 index 0000000000000..b0e098bc714b4 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_valuevar.h @@ -0,0 +1,100 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $FreeBSD$ + */ + +#ifndef _BHND_NVRAM_BHND_NVRAM_VALUEVAR_H_ +#define _BHND_NVRAM_BHND_NVRAM_VALUEVAR_H_ + +#include "bhnd_nvram_value.h" + +int bhnd_nvram_val_generic_encode(bhnd_nvram_val_t *value, + void *outp, size_t *olen, bhnd_nvram_type otype); +int bhnd_nvram_val_generic_encode_elem(bhnd_nvram_val_t *value, + const void *inp, size_t ilen, void *outp, size_t *olen, + bhnd_nvram_type otype); +const void *bhnd_nvram_val_generic_next(bhnd_nvram_val_t *value, + const void *prev, size_t *len); +/** + * Filter input data prior to initialization. + * + * This may be used to permit direct initialization from data types other than + * the default native_type defined by @p fmt. + * + * @param[in,out] fmt Indirect pointer to the NVRAM value format. If + * modified by the caller, initialization will be + * restarted and performed using the provided + * format instance. + * @param inp Input data. + * @param ilen Input data length. + * @param itype Input data type. + * + * @retval 0 If initialization from @p inp is supported. + * @retval EFTYPE If initialization from @p inp is unsupported. + * @retval EFAULT if @p ilen is not correctly aligned for elements of + * @p itype. + */ +typedef int (bhnd_nvram_val_op_filter)(const bhnd_nvram_val_fmt_t **fmt, + const void *inp, size_t ilen, bhnd_nvram_type itype); + +/** @see bhnd_nvram_val_encode() */ +typedef int (bhnd_nvram_val_op_encode)(bhnd_nvram_val_t *value, void *outp, + size_t *olen, bhnd_nvram_type otype); + +/** @see bhnd_nvram_val_encode_elem() */ +typedef int (bhnd_nvram_val_op_encode_elem)(bhnd_nvram_val_t *value, + const void *inp, size_t ilen, void *outp, size_t *olen, + bhnd_nvram_type otype); + +/** @see bhnd_nvram_val_next() */ +typedef const void *(bhnd_nvram_val_op_next)(bhnd_nvram_val_t *value, + const void *prev, size_t *len); + +/** @see bhnd_nvram_val_nelem() */ +typedef size_t (bhnd_nvram_val_op_nelem)(bhnd_nvram_val_t *value); + +/** + * NVRAM value format. + * + * Provides a set of callbacks to support defining custom parsing + * and encoding/conversion behavior when representing values as + * instances of bhnd_nvram_val. + */ +struct bhnd_nvram_val_fmt { + const char *name; /**< type name */ + bhnd_nvram_type native_type; /**< native value representation */ + + bhnd_nvram_val_op_filter *op_filter; + bhnd_nvram_val_op_encode *op_encode; + bhnd_nvram_val_op_encode_elem *op_encode_elem; + bhnd_nvram_val_op_nelem *op_nelem; + bhnd_nvram_val_op_next *op_next; +}; + +#endif /* _BHND_NVRAM_BHND_NVRAM_VALUEVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_sprom.c b/sys/dev/bhnd/nvram/bhnd_sprom.c index f51e21f590adc..d830af9785d1b 100644 --- a/sys/dev/bhnd/nvram/bhnd_sprom.c +++ b/sys/dev/bhnd/nvram/bhnd_sprom.c @@ -52,15 +52,9 @@ __FBSDID("$FreeBSD$"); #include "bhnd_nvram_if.h" -#include "bhnd_spromvar.h" +#include "bhnd_nvram_io.h" -#define SPROM_LOCK_INIT(sc) \ - mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ - "BHND SPROM lock", MTX_DEF) -#define SPROM_LOCK(sc) mtx_lock(&(sc)->mtx) -#define SPROM_UNLOCK(sc) mtx_unlock(&(sc)->mtx) -#define SPROM_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what) -#define SPROM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx) +#include "bhnd_spromvar.h" /** * Default bhnd sprom driver implementation of DEVICE_PROBE(). @@ -68,9 +62,6 @@ __FBSDID("$FreeBSD$"); int bhnd_sprom_probe(device_t dev) { - /* Quiet by default */ - if (!bootverbose) - device_quiet(dev); device_set_desc(dev, "SPROM/OTP"); /* Refuse wildcard attachments */ @@ -100,32 +91,62 @@ int bhnd_sprom_attach(device_t dev, bus_size_t offset) { struct bhnd_sprom_softc *sc; - int error; - + struct bhnd_nvram_io *io; + struct bhnd_resource *r; + bus_size_t r_size, sprom_size; + int rid; + int error; + sc = device_get_softc(dev); sc->dev = dev; + io = NULL; + /* Allocate SPROM resource */ - sc->sprom_rid = 0; - sc->sprom_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, - &sc->sprom_rid, RF_ACTIVE); - if (sc->sprom_res == NULL) { + rid = 0; + r = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (r == NULL) { device_printf(dev, "failed to allocate resources\n"); return (ENXIO); } - /* Initialize SPROM shadow */ - if ((error = bhnd_sprom_init(&sc->shadow, sc->sprom_res, offset))) + /* Determine SPROM size */ + r_size = rman_get_size(r->res); + if (r_size <= offset || (r_size - offset) > BUS_SPACE_MAXSIZE) { + device_printf(dev, "invalid sprom offset\n"); + error = ENXIO; goto failed; + } - /* 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 @@ bhnd_sprom_detach(device_t dev) sc = device_get_softc(dev); - bhnd_release_resource(dev, SYS_RES_MEMORY, sc->sprom_rid, - sc->sprom_res); - bhnd_sprom_fini(&sc->shadow); - SPROM_LOCK_DESTROY(sc); + bhnd_nvram_store_free(sc->store); return (0); } @@ -172,16 +190,9 @@ static int bhnd_sprom_getvar_method(device_t dev, const char *name, void *buf, size_t *len, bhnd_nvram_type type) { - struct bhnd_sprom_softc *sc; - int error; - - 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 @@ static int bhnd_sprom_setvar_method(device_t dev, const char *name, const void *buf, size_t len, bhnd_nvram_type type) { - struct bhnd_sprom_softc *sc; - int error; - - 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 @@ static device_method_t bhnd_sprom_methods[] = { DEVMETHOD_END }; -DEFINE_CLASS_0(bhnd_nvram, bhnd_sprom_driver, bhnd_sprom_methods, sizeof(struct bhnd_sprom_softc)); +DEFINE_CLASS_0(bhnd_nvram_store, bhnd_sprom_driver, bhnd_sprom_methods, sizeof(struct bhnd_sprom_softc)); MODULE_VERSION(bhnd_sprom, 1); diff --git a/sys/dev/bhnd/nvram/bhnd_sprom_parser.c b/sys/dev/bhnd/nvram/bhnd_sprom_parser.c deleted file mode 100644 index bf6089a89cd43..0000000000000 --- a/sys/dev/bhnd/nvram/bhnd_sprom_parser.c +++ /dev/null @@ -1,756 +0,0 @@ -/*- - * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> - * 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 <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#include <sys/bus.h> -#include <sys/endian.h> -#include <sys/limits.h> -#include <sys/rman.h> -#include <sys/systm.h> - -#include <machine/bus.h> -#include <machine/resource.h> - -#include "bhnd_nvram_common.h" - -#include "bhnd_sprom_parservar.h" - -/* - * BHND SPROM Parser - * - * Provides identification, decoding, and encoding of BHND SPROM data. - */ - -static int sprom_direct_read(struct bhnd_sprom *sc, size_t offset, - void *buf, size_t nbytes, uint8_t *crc); -static int sprom_extend_shadow(struct bhnd_sprom *sc, - size_t image_size, uint8_t *crc); -static int sprom_populate_shadow(struct bhnd_sprom *sc); - -static int sprom_get_var_defn(struct bhnd_sprom *sc, - const char *name, - const struct bhnd_nvram_vardefn **var, - const struct bhnd_sprom_vardefn **sprom, - size_t *size, size_t *nelem, - bhnd_nvram_type req_type); - -static char sprom_get_delim_char(struct bhnd_sprom *sc, - bhnd_nvram_sfmt sfmt); - -/* SPROM revision is always located at the second-to-last byte */ -#define SPROM_REV(_sc) SPROM_READ_1((_sc), (_sc)->sp_size - 2) - -/* SPROM CRC is always located at the last byte */ -#define SPROM_CRC_OFF(_sc) SPROM_CRC_LEN(_sc) - -/* SPROM CRC covers all but the final CRC byte */ -#define SPROM_CRC_LEN(_sc) ((_sc)->sp_size - 1) - -/* SPROM shadow I/O (with byte-order translation) */ -#define SPROM_READ_1(_sc, _off) SPROM_READ_ENC_1(_sc, _off) -#define SPROM_READ_2(_sc, _off) le16toh(SPROM_READ_ENC_2(_sc, _off)) -#define SPROM_READ_4(_sc, _off) le32toh(SPROM_READ_ENC_4(_sc, _off)) - -#define SPROM_WRITE_1(_sc, _off, _v) SPROM_WRITE_ENC_1(_sc, _off, (_v)) -#define SPROM_WRITE_2(_sc, _off, _v) SPROM_WRITE_ENC_2(_sc, _off, \ - htole16(_v)) -#define SPROM_WRITE_4(_sc, _off, _v) SPROM_WRITE_ENC_4(_sc, _off, \ - htole32(_v)) - -/* SPROM shadow I/O (without byte-order translation) */ -#define SPROM_READ_ENC_1(_sc, _off) (*(uint8_t *)((_sc)->sp_shadow + _off)) -#define SPROM_READ_ENC_2(_sc, _off) (*(uint16_t *)((_sc)->sp_shadow + _off)) -#define SPROM_READ_ENC_4(_sc, _off) (*(uint32_t *)((_sc)->sp_shadow + _off)) - -#define SPROM_WRITE_ENC_1(_sc, _off, _v) \ - *((uint8_t *)((_sc)->sp_shadow + _off)) = (_v) -#define SPROM_WRITE_ENC_2(_sc, _off, _v) \ - *((uint16_t *)((_sc)->sp_shadow + _off)) = (_v) -#define SPROM_WRITE_ENC_4(_sc, _off, _v) \ - *((uint32_t *)((_sc)->sp_shadow + _off)) = (_v) - -/* Call @p _next macro with the C type, widened (signed or unsigned) 32-bit C - * type, width, and min/max values associated with @p _dtype */ -#define SPROM_SWITCH_TYPE(_dtype, _next, ...) \ -do { \ - switch (_dtype) { \ - case BHND_NVRAM_TYPE_UINT8: \ - _next (uint8_t, uint32_t, 1, 0, \ - UINT8_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_UINT16: \ - _next (uint16_t, uint32_t, 2, 0, \ - UINT16_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_UINT32: \ - _next (uint32_t, uint32_t, 4, 0, \ - UINT32_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_INT8: \ - _next (int8_t, int32_t, 1, \ - INT8_MIN, INT8_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_INT16: \ - _next (int16_t, int32_t, 2, \ - INT16_MIN, INT16_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_INT32: \ - _next (int32_t, int32_t, 4, \ - INT32_MIN, INT32_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_CHAR: \ - _next (char, int32_t, 1, \ - CHAR_MIN, CHAR_MAX, ## __VA_ARGS__); \ - break; \ - case BHND_NVRAM_TYPE_CSTR: \ - panic("%s: BHND_NVRAM_TYPE_CSTR unhandled", \ - __FUNCTION__); \ - break; \ - } \ -} while (0) - - -/* Verify the range of _val of (_stype) within _type */ -#define SPROM_VERIFY_RANGE(_type, _widen, _width, _min, _max, _val, \ - _stype) \ -do { \ - if (BHND_NVRAM_SIGNED_TYPE(_stype)) { \ - int32_t sval = (int32_t) (_val); \ - if (sval > (_max) || sval < (_min)) \ - return (ERANGE); \ - } else { \ - if ((_val) > (_max)) \ - return (ERANGE); \ - } \ -} while(0) - -/* - * Table of supported SPROM image formats, sorted by image size, ascending. - */ -#define SPROM_FMT(_sz, _revmin, _revmax, _sig) \ - { SPROM_SZ_ ## _sz, _revmin, _revmax, \ - SPROM_SIG_ ## _sig ## _OFF, \ - SPROM_SIG_ ## _sig } - -static const struct sprom_fmt { - size_t size; - uint8_t rev_min; - uint8_t rev_max; - size_t sig_offset; - uint16_t sig_req; -} sprom_fmts[] = { - SPROM_FMT(R1_3, 1, 3, NONE), - SPROM_FMT(R4_8_9, 4, 4, R4), - SPROM_FMT(R4_8_9, 8, 9, R8_9), - SPROM_FMT(R10, 10, 10, R10), - SPROM_FMT(R11, 11, 11, R11) -}; - -/** - * Identify the SPROM format at @p offset within @p r, verify the CRC, - * and allocate a local shadow copy of the SPROM data. - * - * After successful initialization, @p r will not be accessed; any pin - * configuration required for SPROM access may be reset. - * - * @param[out] sprom On success, will be initialized with shadow of the SPROM - * data. - * @param r An active resource mapping the SPROM data. - * @param offset Offset of the SPROM data within @p resource. - */ -int -bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r, - bus_size_t offset) -{ - bus_size_t res_size; - int error; - - sprom->dev = rman_get_device(r->res); - sprom->sp_res = r; - sprom->sp_res_off = offset; - - /* Determine maximum possible SPROM image size */ - res_size = rman_get_size(r->res); - if (offset >= res_size) - return (EINVAL); - - sprom->sp_size_max = MIN(res_size - offset, SPROM_SZ_MAX); - - /* Allocate and populate SPROM shadow */ - sprom->sp_size = 0; - sprom->sp_capacity = sprom->sp_size_max; - sprom->sp_shadow = malloc(sprom->sp_capacity, M_BHND_NVRAM, M_NOWAIT); - if (sprom->sp_shadow == NULL) - return (ENOMEM); - - /* Read and identify SPROM image */ - if ((error = sprom_populate_shadow(sprom))) - return (error); - - return (0); -} - -/** - * Release all resources held by @p sprom. - * - * @param sprom A SPROM instance previously initialized via bhnd_sprom_init(). - */ -void -bhnd_sprom_fini(struct bhnd_sprom *sprom) -{ - free(sprom->sp_shadow, M_BHND_NVRAM); -} - -/* Perform a read using a SPROM offset descriptor, safely widening the result - * to its 32-bit representation before assigning it to @p _dest. */ -#define SPROM_GETVAR_READ(_type, _widen, _width, _min, _max, _sc, _off, \ - _dest) \ -do { \ - _type _v = (_type)SPROM_READ_ ## _width(_sc, _off->offset); \ - if (_off->shift > 0) { \ - _v >>= _off->shift; \ - } else if (off->shift < 0) { \ - _v <<= -_off->shift; \ - } \ - \ - if (_off->cont) \ - _dest |= ((uint32_t) (_widen) _v) & _off->mask; \ - else \ - _dest = ((uint32_t) (_widen) _v) & _off->mask; \ -} while(0) - -/* Emit a value read using a SPROM offset descriptor, narrowing the - * result output representation. */ -#define SPROM_GETVAR_WRITE(_type, _widen, _width, _min, _max, _off, \ - _src, _buf) \ -do { \ - _type _v = (_type) (_widen) _src; \ - *((_type *)_buf) = _v; \ -} while(0) - -/* String format a value read using a SPROM offset descriptor */ -#define SPROM_GETVAR_SNPRINTF(_type, _widen, _width, _min, _max, _src, \ - _buf, _remain, _fmt, _nwrite) \ -do { \ - _nwrite = snprintf(_buf, _remain, _fmt, (_type) (_widen) _src); \ -} while(0) - -/** - * Read a SPROM variable, performing conversion to host byte order. - * - * @param sc The SPROM parser state. - * @param name The SPROM variable name. - * @param[out] buf On success, the requested value will be written - * to this buffer. This argment may be NULL if - * the value is not desired. - * @param[in,out] len The capacity of @p buf. On success, will be set - * to the actual size of the requested value. - * @param type The requested data type to be written to @p buf. - * - * @retval 0 success - * @retval ENOENT The requested variable was not found. - * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too - * small to hold the requested value. - * @retval non-zero If reading @p name otherwise fails, a regular unix - * error code will be returned. - */ -int -bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf, - size_t *len, bhnd_nvram_type type) -{ - const struct bhnd_nvram_vardefn *nv; - const struct bhnd_sprom_vardefn *sv; - void *outp; - size_t all1_offs; - size_t req_size, nelem; - size_t str_remain; - char str_delim; - uint32_t val; - int error; - - error = sprom_get_var_defn(sc, name, &nv, &sv, &req_size, &nelem, type); - if (error) - return (error); - - outp = buf; - str_remain = 0; - str_delim = '\0'; - - - if (type != BHND_NVRAM_TYPE_CSTR) { - /* Provide required size */ - if (outp == NULL) { - *len = req_size; - return (0); - } - - /* Check (and update) target buffer len */ - if (*len < req_size) - return (ENOMEM); - else - *len = req_size; - } else { - /* String length calculation requires performing - * the actual string formatting */ - KASSERT(req_size == 0, - ("req_size set for variable-length type")); - - /* If caller is querying length, the len argument - * may be uninitialized */ - if (outp != NULL) - str_remain = *len; - - /* Fetch delimiter for the variable's string format */ - str_delim = sprom_get_delim_char(sc, nv->sfmt); - } - - /* Read data */ - all1_offs = 0; - val = 0; - for (size_t i = 0; i < sv->num_offsets; i++) { - const struct bhnd_sprom_offset *off; - - off = &sv->offsets[i]; - KASSERT(!off->cont || i > 0, ("cont marked on first offset")); - - /* If not a continuation, advance the output buffer; if - * a C string, this requires appending a delimiter character */ - if (i > 0 && !off->cont) { - size_t width = bhnd_nvram_type_width(type); - - /* Non-fixed width types (such as CSTR) will have a 0 - * width value */ - if (width != 0) { - KASSERT(outp != NULL, ("NULL output buffer")); - outp = ((uint8_t *)outp) + width; - } - - /* Append CSTR delim, if necessary */ - if (type == BHND_NVRAM_TYPE_CSTR && - str_delim != '\0' && - i != 0) - { - if (outp != NULL && str_remain >= 1) { - *((char *)outp) = str_delim; - outp = ((char *)outp + 1); - - /* Drop outp reference if we hit 0 */ - if (str_remain-- == 0) - outp = NULL; - } - - if (SIZE_MAX - 1 < req_size) - return (EFTYPE); /* too long */ - req_size++; - } - } - - /* Read the value, widening to a common uint32 - * representation */ - SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_READ, sc, off, val); - - /* If IGNALL1, record whether value has all bits set. */ - if (nv->flags & BHND_NVRAM_VF_IGNALL1) { - uint32_t all1; - - all1 = off->mask; - if (off->shift > 0) - all1 >>= off->shift; - else if (off->shift < 0) - all1 <<= -off->shift; - - if ((val & all1) == all1) - all1_offs++; - } - - /* Skip writing if additional continuations remain */ - if (i+1 < sv->num_offsets && sv->offsets[i].cont) - continue; - - /* Perform write */ - if (type == BHND_NVRAM_TYPE_CSTR) { - const char *fmtstr; - int written; - - fmtstr = bhnd_nvram_type_fmt(off->type, nv->sfmt, i); - if (fmtstr == NULL) { - device_printf(sc->dev, "no NVRAM format string " - "for '%s' (type=%d)\n", name, off->type); - return (EOPNOTSUPP); - } - - SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_SNPRINTF, val, - outp, str_remain, fmtstr, written); - - if (written <= 0) - return (EFTYPE); - - /* Calculate remaining capacity, drop outp reference - * if we hit 0 -- otherwise, advance the buffer - * position */ - if (written >= str_remain) { - str_remain = 0; - outp = NULL; - } else { - str_remain -= written; - if (outp != NULL) - outp = (char *)outp + written; - } - - /* Add additional bytes to total length */ - if (SIZE_MAX - written < req_size) - return (EFTYPE); /* string too long */ - req_size += written; - } else { - /* Verify range */ - SPROM_SWITCH_TYPE(type, SPROM_VERIFY_RANGE, val, - off->type); - - /* Write the value, narrowing to the appropriate output - * width. */ - SPROM_SWITCH_TYPE(type, SPROM_GETVAR_WRITE, off, val, - outp); - } - } - - /* Should value should be treated as uninitialized? */ - if (nv->flags & BHND_NVRAM_VF_IGNALL1 && all1_offs == sv->num_offsets) - return (ENOENT); - - /* If this is a C string request, we need to provide the computed - * length. */ - if (type == BHND_NVRAM_TYPE_CSTR) { - /* Account for final trailing NUL */ - if (SIZE_MAX - 1 < req_size) - return (EFTYPE); /* string too long */ - req_size++; - - /* Return an error if a too-small output buffer was provided */ - if (buf != NULL && *len < req_size) { - *len = req_size; - return (ENOMEM); - } - - *len = req_size; - } - - return (0); -} - -/* Perform a read of a variable offset from _src, safely widening the result - * to its 32-bit representation before assigning it to @p _dest. */ -#define SPROM_SETVAR_READ(_type, _widen, _width, _min, _max, _off, \ - _src, _dest) \ -do { \ - _type _v = *(const _type *)_src; \ - if (_off->shift > 0) { \ - _v <<= _off->shift; \ - } else if (off->shift < 0) { \ - _v >>= -_off->shift; \ - } \ - _dest = ((uint32_t) (_widen) _v) & _off->mask; \ -} while(0) - - -/* Emit a value read using a SPROM offset descriptor, narrowing the - * result output representation and, if necessary, OR'ing it with the - * previously read value from @p _buf. */ -#define SPROM_SETVAR_WRITE(_type, _widen, _width, _min, _max, _sc, \ - _off, _src) \ -do { \ - _type _v = (_type) (_widen) _src; \ - if (_off->cont) \ - _v |= SPROM_READ_ ## _width(_sc, _off->offset); \ - SPROM_WRITE_ ## _width(_sc, _off->offset, _v); \ -} while(0) - -/** - * Set a local value for a SPROM variable, performing conversion to SPROM byte - * order. - * - * The new value will be written to the backing SPROM shadow. - * - * @param sc The SPROM parser state. - * @param name The SPROM variable name. - * @param[out] buf The new value. - * @param[in,out] len The size of @p buf. - * @param type The data type of @p buf. - * - * @retval 0 success - * @retval ENOENT The requested variable was not found. - * @retval EINVAL If @p len does not match the expected variable size. - */ -int -bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name, const void *buf, - size_t len, bhnd_nvram_type type) -{ - const struct bhnd_nvram_vardefn *nv; - const struct bhnd_sprom_vardefn *sv; - size_t req_size, nelem; - int error; - uint8_t crc; - - error = sprom_get_var_defn(sc, name, &nv, &sv, &req_size, &nelem, type); - if (error) - return (error); - - /* String parsing is currently unsupported */ - if (type == BHND_NVRAM_TYPE_CSTR) - return (EOPNOTSUPP); - - /* Provide required size */ - if (len != req_size) - return (EINVAL); - - /* Write data */ - for (size_t i = 0; i < sv->num_offsets; i++) { - const struct bhnd_sprom_offset *off; - uint32_t val; - - off = &sv->offsets[i]; - KASSERT(!off->cont || i > 0, ("cont marked on first offset")); - - /* If not a continuation, advance the input pointer */ - if (i > 0 && !off->cont) { - buf = ((const uint8_t *)buf) + - bhnd_nvram_type_width(sv->offsets[i-1].type); - } - - /* Read the value, widening to a common uint32 - * representation */ - SPROM_SWITCH_TYPE(nv->type, SPROM_SETVAR_READ, off, buf, val); - - /* Verify range */ - SPROM_SWITCH_TYPE(nv->type, SPROM_VERIFY_RANGE, val, type); - - /* Write the value, narrowing to the appropriate output - * width. */ - SPROM_SWITCH_TYPE(off->type, SPROM_SETVAR_WRITE, sc, off, val); - } - - /* Update CRC */ - crc = ~bhnd_nvram_crc8(sc->sp_shadow, SPROM_CRC_LEN(sc), - BHND_NVRAM_CRC8_INITIAL); - SPROM_WRITE_1(sc, SPROM_CRC_OFF(sc), crc); - - return (0); -} - -/* Read and identify the SPROM image by incrementally performing - * read + CRC of all supported image formats */ -static int -sprom_populate_shadow(struct bhnd_sprom *sc) -{ - const struct sprom_fmt *fmt; - int error; - uint16_t sig; - uint8_t srom_rev; - uint8_t crc; - - crc = BHND_NVRAM_CRC8_INITIAL; - - /* Identify the SPROM revision (and populate the SPROM shadow) */ - for (size_t i = 0; i < nitems(sprom_fmts); i++) { - fmt = &sprom_fmts[i]; - - /* Read image data and check CRC */ - if ((error = sprom_extend_shadow(sc, fmt->size, &crc))) - return (error); - - /* Skip on invalid CRC */ - if (crc != BHND_NVRAM_CRC8_VALID) - continue; - - /* Fetch SROM revision */ - srom_rev = SPROM_REV(sc); - - /* Early sromrev 1 devices (specifically some BCM440x enet - * cards) are reported to have been incorrectly programmed - * with a revision of 0x10. */ - if (fmt->size == SPROM_SZ_R1_3 && srom_rev == 0x10) - srom_rev = 0x1; - - /* Verify revision range */ - if (srom_rev < fmt->rev_min || srom_rev > fmt->rev_max) - continue; - - /* Verify signature (if any) */ - sig = SPROM_SIG_NONE; - if (fmt->sig_offset != SPROM_SIG_NONE_OFF) - sig = SPROM_READ_2(sc, fmt->sig_offset); - - if (sig != fmt->sig_req) { - device_printf(sc->dev, - "invalid sprom %hhu signature: 0x%hx " - "(expected 0x%hx)\n", - srom_rev, sig, fmt->sig_req); - return (EINVAL); - } - - /* Identified */ - sc->sp_rev = srom_rev; - return (0); - } - - /* identification failed */ - device_printf(sc->dev, "unrecognized SPROM format\n"); - return (EINVAL); -} - -/* - * Extend the shadowed SPROM buffer to image_size, reading any required - * data from the backing SPROM resource and updating the CRC. - */ -static int -sprom_extend_shadow(struct bhnd_sprom *sc, size_t image_size, - uint8_t *crc) -{ - int error; - - KASSERT(image_size >= sc->sp_size, (("shadow truncation unsupported"))); - - /* Verify the request fits within our shadow buffer */ - if (image_size > sc->sp_capacity) - return (ENOSPC); - - /* Skip no-op requests */ - if (sc->sp_size == image_size) - return (0); - - /* Populate the extended range */ - error = sprom_direct_read(sc, sc->sp_size, sc->sp_shadow + sc->sp_size, - image_size - sc->sp_size, crc); - if (error) - return (error); - - sc->sp_size = image_size; - return (0); -} - -/** - * Read nbytes at the given offset from the backing SPROM resource, and - * update the CRC. - */ -static int -sprom_direct_read(struct bhnd_sprom *sc, size_t offset, void *buf, - size_t nbytes, uint8_t *crc) -{ - bus_size_t res_offset; - uint16_t *p; - - KASSERT(nbytes % sizeof(uint16_t) == 0, ("unaligned sprom size")); - KASSERT(offset % sizeof(uint16_t) == 0, ("unaligned sprom offset")); - - /* Check for read overrun */ - if (offset >= sc->sp_size_max || sc->sp_size_max - offset < nbytes) { - device_printf(sc->dev, "requested SPROM read would overrun\n"); - return (EINVAL); - } - - /* Perform read and update CRC */ - p = (uint16_t *)buf; - res_offset = sc->sp_res_off + offset; - - bhnd_bus_read_region_stream_2(sc->sp_res, res_offset, p, - (nbytes / sizeof(uint16_t))); - *crc = bhnd_nvram_crc8(p, nbytes, *crc); - - return (0); -} - - -/** - * Locate the variable and SPROM revision-specific definitions - * for variable with @p name. - */ -static int -sprom_get_var_defn(struct bhnd_sprom *sc, const char *name, - const struct bhnd_nvram_vardefn **var, - const struct bhnd_sprom_vardefn **sprom, - size_t *size, size_t *nelem, bhnd_nvram_type req_type) -{ - /* Find variable definition */ - *var = bhnd_nvram_find_vardefn(name); - if (*var == NULL) - return (ENOENT); - - /* Find revision-specific SPROM definition */ - for (size_t i = 0; i < (*var)->num_sp_defs; i++) { - const struct bhnd_sprom_vardefn *sp = &(*var)->sp_defs[i]; - - if (sc->sp_rev < sp->compat.first) - continue; - - if (sc->sp_rev > sp->compat.last) - continue; - - /* Found */ - *sprom = sp; - - /* Calculate element count and total size, in bytes */ - *nelem = 0; - for (size_t j = 0; j < sp->num_offsets; j++) - if (!sp->offsets[j].cont) - *nelem += 1; - - *size = bhnd_nvram_type_width(req_type) * (*nelem); - return (0); - } - - /* Not supported by this SPROM revision */ - return (ENOENT); -} - -/** - * Return the array element delimiter for @p sfmt, or '\0' if none. - */ -static char -sprom_get_delim_char(struct bhnd_sprom *sc, bhnd_nvram_sfmt sfmt) -{ - switch (sfmt) { - case BHND_NVRAM_SFMT_HEX: - case BHND_NVRAM_SFMT_DEC: - return (','); - - case BHND_NVRAM_SFMT_CCODE: - case BHND_NVRAM_SFMT_LEDDC: - return ('\0'); - - case BHND_NVRAM_SFMT_MACADDR: - return (':'); - - default: - device_printf(sc->dev, "unknown NVRAM string format: %d\n", - sfmt); - return (','); - } -} diff --git a/sys/dev/bhnd/nvram/bhnd_sprom_parservar.h b/sys/dev/bhnd/nvram/bhnd_sprom_parservar.h deleted file mode 100644 index 7e519f829dc5c..0000000000000 --- a/sys/dev/bhnd/nvram/bhnd_sprom_parservar.h +++ /dev/null @@ -1,64 +0,0 @@ -/*- - * Copyright (c) 2016 Landon Fuller <landon@landonf.org> - * 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_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 - -#define SPROM_SIG_NONE 0x0 -#define SPROM_SIG_NONE_OFF 0x0 - -/** SPROM signature (rev 4) */ -#define SPROM_SIG_R4 0x5372 -#define SPROM_SIG_R4_OFF 64 /**< SPROM signature offset (rev 4) */ - -/** SPROM signature (rev 8, 9) */ -#define SPROM_SIG_R8_9 SPROM_SIG_R4 -#define SPROM_SIG_R8_9_OFF 128 /**< SPROM signature offset (rev 8-9) */ - -/** SPROM signature (rev 10) */ -#define SPROM_SIG_R10 SPROM_SIG_R4 -#define SPROM_SIG_R10_OFF 438 /**< SPROM signature offset (rev 10) */ - -/** SPROM signature (rev 11) */ -#define SPROM_SIG_R11 0x0634 -#define SPROM_SIG_R11_OFF 128 /**< SPROM signature offset (rev 11) */ - -#endif /* _BHND_NVRAM_SPROM_PARSERVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_spromvar.h b/sys/dev/bhnd/nvram/bhnd_spromvar.h index df43c4c6106a5..f3a9bc3c4dc22 100644 --- a/sys/dev/bhnd/nvram/bhnd_spromvar.h +++ b/sys/dev/bhnd/nvram/bhnd_spromvar.h @@ -34,7 +34,7 @@ #include <dev/bhnd/bhnd.h> -#include "bhnd_sprom_parser.h" +#include "bhnd_nvram_store.h" DECLARE_CLASS(bhnd_sprom_driver); @@ -49,11 +49,8 @@ int bhnd_sprom_detach(device_t dev); * softc structures. */ struct bhnd_sprom_softc { - device_t dev; - struct bhnd_resource *sprom_res; /**< SPROM resource */ - int sprom_rid; /**< SPROM RID */ - struct bhnd_sprom shadow; /**< SPROM shadow */ - struct mtx mtx; /**< SPROM shadow mutex */ + device_t dev; + struct bhnd_nvram_store *store; /**< nvram store */ }; #endif /* _BHND_NVRAM_BHND_SPROMVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/nvram_map b/sys/dev/bhnd/nvram/nvram_map index f38573500ab74..33bbbb94e8abe 100644 --- a/sys/dev/bhnd/nvram/nvram_map +++ b/sys/dev/bhnd/nvram/nvram_map @@ -21,1445 +21,3054 @@ # $FreeBSD$ # -# NVRAM variable definitions and revision-specific SPROM offsets. +# NVRAM variable and SPROM layout descriptions. # -# Processed by nvram_map_gen.awk to produce bhnd_nvram_map.h +# Process with nvram_map_gen.awk to produce bhnd_nvram_map.h and +# bhnd_nvram_map_data.h # # NOTE: file was originally generated automatically by using libclang # to analyze and extract format information and descriptions from Broadcom's # available ISC-licensed CIS and SROM code and associated headers. # -# Board Info -# +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." + } + + 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" + } -u16 boardvendor {} # PCI vendor ID (SoC NVRAM-only) -u16 subvid { srom >= 2 0x6 } # PCI subvendor ID -u16 devid { srom >= 8 0x60 } # PCI device ID + u8 txchain { + desc "Available TX Chains" + help "TX chains 0-3 are marked as available if the + corresponding bit is set." + + all1 ignore + } -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 + u8 rxchain { + desc "Available RX Chains" + help "RX chains 0-3 are marked as available if the + corresponding bit is set." + + all1 ignore + } + + u16 antswitch { + desc "Antenna Diversity Switch Type" + help "The antenna diversity switch configuration used by + this device. The value is hardware-specific." + all1 ignore + } } -u32 boardflags2 { - srom 4 0x48 - srom 5-7 0x4E - srom >= 8 0x88 + +u8 aga0 { + #desc <variable description> + #help <variable help> } -u32 boardflags3 { - srom >= 11 0x8C +u8 aga1 { + #desc <variable description> + #help <variable help> } - -# 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 +u8 aga2 { + #desc <variable description> + #help <variable help> } - -# Board revision -u16 boardrev { - srom 1-3 u8 0x5D - srom 4-7 0x42 - srom >= 8 0x82 +u8 agbg0 { + #desc <variable description> + #help <variable help> } - -# Board type -u16 boardtype { - srom >= 2 0x4 +u8 agbg1 { + #desc <variable description> + #help <variable help> } - -# SROM revision -u8 sromrev { - srom 1-3 0x74 - srom 4-9 0x1B6 - srom 10 0x1CA - srom 11 0x1D2 +u8 agbg2 { + #desc <variable description> + #help <variable help> } - - -# PMU Info -# - -# PMU min resource mask (embedded-only). -u32 rmin { - sfmt decimal +u8 antswctl2g { + #desc <variable description> + #help <variable help> } - -# PMU min resource max (embedded-only). -u32 rmax { - sfmt decimal +u8 antswctl5g { + #desc <variable description> + #help <variable help> } - - -# Antennas available -u8 aa2g { - srom 1-3 0x5C (&0x30, >>4) - srom 4-7 0x5D - srom 8-10 0x9D - srom >= 11 0xA1 +u32 boardflags { + #desc <variable description> + #help <variable help> } -u8 aa5g { - srom 1-3 0x5C (&0xC0, >>6) - srom 4-7 0x5C - srom 8-10 0x9C - srom >= 11 0xA0 +u32 boardflags2 { + #desc <variable description> + #help <variable help> } - -# ACPHY PA trimming parameters: 40 -u16[12] pa5gbw40a0 { - srom >= 11 0x110 +u32 boardflags3 { + #desc <variable description> + #help <variable help> } - -# ACPHY PA trimming parameters: 80 -u16[12] pa5gbw80a0 { - srom >= 11 0x138 +u16 boardnum { + fmt decimal + #desc <variable description> + #help <variable help> } - -# ACPHY PA trimming parameters: 40/80 -u16[12] pa5gbw4080a0 { - srom >= 11 0x138 +u16 boardrev { + #desc <variable description> + #help <variable help> } -u16[12] pa5gbw4080a1 { - srom >= 11 u16 0xB6, u16 0xBC, u16 0xCE, u16 0xD4, u16[8] 0x128 +u16 boardtype { + #desc <variable description> + #help <variable help> } - -# ACPHY PA trimming parameters: CCK -u16[3] pa2gccka0 { - srom >= 11 0x102 +u16 boardvendor { + #desc <variable description> + #help <variable help> } - -# ACPHY Power-per-rate 2gpo -u16 dot11agofdmhrbw202gpo { - srom >= 11 0x15C +u16 bw40po { + #desc <variable description> + #help <variable help> } -u16 ofdmlrbw202gpo { - srom >= 11 0x15E +u16 bwduppo { + #desc <variable description> + #help <variable help> } - -# ACPHY Power-per-rate 5gpo -u32 mcsbw805glpo { - srom >= 11 0x168 +u8 bxa2g { + #desc <variable description> + #help <variable help> } -u32 mcsbw805gmpo { - srom >= 11 0x178 +u8 bxa5g { + #desc <variable description> + #help <variable help> } -u32 mcsbw805ghpo { - srom >= 11 0x188 +u8 cc { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcslr5glpo { - srom >= 11 0x190 (&0xFFF) +u16 cck2gpo { + #desc <variable description> + #help <variable help> } -u16 mcslr5gmpo { - srom >= 11 0x192 +u16 cckPwrOffset { + #desc <variable description> + #help <variable help> } -u16 mcslr5ghpo { - srom >= 11 0x194 +u16 cckbw202gpo { + #desc <variable description> + #help <variable help> } - -# ACPHY Power-per-rate sbpo -u16 sb20in40hrpo { - srom >= 11 0x196 +u16 cckbw20ul2gpo { + #desc <variable description> + #help <variable help> } -u16 sb20in80and160hr5glpo { - srom >= 11 0x198 +char ccode[2] { + #desc <variable description> + #help <variable help> } -u16 sb40and80hr5glpo { - srom >= 11 0x19A +u16 cddpo { + #desc <variable description> + #help <variable help> } -u16 sb20in80and160hr5gmpo { - srom >= 11 0x19C +u16 devid { + #desc <variable description> + #help <variable help> } -u16 sb40and80hr5gmpo { - srom >= 11 0x19E +u16 dot11agduphrpo { + #desc <variable description> + #help <variable help> } -u16 sb20in80and160hr5ghpo { - srom >= 11 0x1A0 +u16 dot11agduplrpo { + #desc <variable description> + #help <variable help> } -u16 sb40and80hr5ghpo { - srom >= 11 0x1A2 +u16 dot11agofdmhrbw202gpo { + #desc <variable description> + #help <variable help> } -u16 sb20in40lrpo { - srom >= 11 0x1A4 +u8 elna2g { + #desc <variable description> + #help <variable help> } -u16 sb20in80and160lr5glpo { - srom >= 11 0x1A6 +u8 elna5g { + #desc <variable description> + #help <variable help> } -u16 sb40and80lr5glpo { - srom >= 11 0x1A8 +u8 epagain2g { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 sb20in80and160lr5gmpo { - srom >= 11 0x1AA +u8 epagain5g { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 sb40and80lr5gmpo { - srom >= 11 0x1AC +u8 et1macaddr[6] { + fmt macaddr + #desc <variable description> + #help <variable help> } -u16 sb20in80and160lr5ghpo { - srom >= 11 0x1AE +u8 eu_edthresh2g { + #desc <variable description> + #help <variable help> } -u16 sb40and80lr5ghpo { - srom >= 11 0x1B0 +u8 eu_edthresh5g { + #desc <variable description> + #help <variable help> } -u16 dot11agduphrpo { - srom >= 11 0x1B2 +u8 extpagain2g { + #desc <variable description> + #help <variable help> } -u16 dot11agduplrpo { - srom >= 11 0x1B4 +u8 extpagain5g { + #desc <variable description> + #help <variable help> } - -# Antenna gain -u8 ag0 { - srom 1-3 0x75 - srom 4-7 0x5F - srom 8-10 0x9F +u8 femctrl { + fmt decimal + #desc <variable description> + #help <variable help> } -u8 ag1 { - srom 1-3 0x74 - srom 4-7 0x5E - srom 8-10 0x9E +u8 freqoffset_corr { + #desc <variable description> + #help <variable help> } -u8 ag2 { - srom 4-7 0x61 - srom 8-10 0xA1 +u8 gainctrlsph { + fmt decimal + #desc <variable description> + #help <variable help> } -u8 ag3 { - srom 4-7 0x60 - srom 8-10 0xA0 +u8 hw_iqcal_en { + #desc <variable description> + #help <variable help> } - -u8 agbg0 { - srom >= 11 0xA2 +u8 il0macaddr[6] { + fmt macaddr + #desc <variable description> + #help <variable help> } -u8 agbg1 { - srom >= 11 0xA3 +u8 iqcal_swp_dis { + #desc <variable description> + #help <variable help> } -u8 agbg2 { - srom >= 11 0xA4 +u8 itt2ga0 { + #desc <variable description> + #help <variable help> } -u8 aga0 { - srom >= 11 0xA5 +u8 itt2ga1 { + #desc <variable description> + #help <variable help> } -u8 aga1 { - srom >= 11 0xA6 +u8 itt2ga2 { + #desc <variable description> + #help <variable help> } -u8 aga2 { - srom >= 11 0xA7 +u8 itt2ga3 { + #desc <variable description> + #help <variable help> } - -# Default country code (sromrev == 1) -u8 cc { - srom 1 0x5C (&0xF) +u8 itt5ga0 { + #desc <variable description> + #help <variable help> } - -# 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 +u8 itt5ga1 { + #desc <variable description> + #help <variable help> } -u16 cckbw20ul2gpo { - srom 9-10 0x142 - srom >= 11 0x152 +u8 itt5ga2 { + #desc <variable description> + #help <variable help> } - -# 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 +u8 itt5ga3 { + #desc <variable description> + #help <variable help> } - -# 2 byte; txchain, rxchain -u8 txchain { - all1 ignore - srom 4-7 0x7B (&0xF) - srom 8-10 0xA3 (&0xF) - srom >= 11 0xA9 (&0xF) +u8 ledbh0 { + all1 ignore + #desc <variable description> + #help <variable help> } -u8 rxchain { - all1 ignore - srom 4-7 0x7B (&0xF0, >>4) - srom 8-10 0xA3 (&0xF0, >>4) - srom >= 11 0xA9 (&0xF0, >>4) +u8 ledbh1 { + all1 ignore + #desc <variable description> + #help <variable help> } -u16 antswitch { - all1 ignore - srom 4-7 u8 0x7A - srom 8-10 u8 0xA2 - srom >= 11 u8 0xA8 +u8 ledbh2 { + all1 ignore + #desc <variable description> + #help <variable help> } - -u8 elna2g { - srom 8-10 0xBB +u8 ledbh3 { + all1 ignore + #desc <variable description> + #help <variable help> } - -u8 elna5g { - srom 8-10 0xBA +u32 leddc { + all1 ignore + fmt leddc + #desc <variable description> + #help <variable help> } - -# 11n front-end specification -u8 antswctl2g { - srom 8-10 0xAE (&0xF8, >>3) +u16 legofdm40duppo { + #desc <variable description> + #help <variable help> } -u8 triso2g { - srom 8-10 0xAE (&0x7) +u32 legofdmbw202gpo { + #desc <variable description> + #help <variable help> } -u8 pdetrange2g { - srom 8-10 0xAF (&0xF8, >>3) +u32 legofdmbw205ghpo { + #desc <variable description> + #help <variable help> } -u8 extpagain2g { - srom 8-10 0xAF (&0x6, >>1) +u32 legofdmbw205glpo { + #desc <variable description> + #help <variable help> } -u8 tssipos2g { - srom 8-10 0xAF (&0x1) +u32 legofdmbw205gmpo { + #desc <variable description> + #help <variable help> } -u8 antswctl5g { - srom 8-10 0xB0 (&0xF8, >>3) +u32 legofdmbw20ul2gpo { + #desc <variable description> + #help <variable help> } -u8 triso5g { - srom 8-10 0xB0 (&0x7) +u32 legofdmbw20ul5ghpo { + #desc <variable description> + #help <variable help> } -u8 pdetrange5g { - srom 8-10 0xB1 (&0xF8, >>3) +u32 legofdmbw20ul5glpo { + #desc <variable description> + #help <variable help> } -u8 extpagain5g { - srom 8-10 0xB1 (&0x6, >>1) +u32 legofdmbw20ul5gmpo { + #desc <variable description> + #help <variable help> } -u8 tssipos5g { - srom 8-10 0xB1 (&0x1) +u8 macaddr[6] { + fmt macaddr + #desc <variable description> + #help <variable help> } - -# FEM config -u8 femctrl { - sfmt decimal - srom >= 11 0xAA (&0xF8, >>3) +u8 maxp2ga0 { + #desc <variable description> + #help <variable help> } -u8 papdcap2g { - sfmt decimal - srom >= 11 0xAA (&0x4, >>2) +u8 maxp2ga1 { + #desc <variable description> + #help <variable help> } -u8 tworangetssi2g { - sfmt decimal - srom >= 11 0xAA (&0x2, >>1) +u8 maxp2ga2 { + #desc <variable description> + #help <variable help> } -u8 pdgain2g { - sfmt decimal - srom >= 11 u16 0xAA (&0x1F0, >>4) +u8 maxp2ga3 { + #desc <variable description> + #help <variable help> } -u8 epagain2g { - sfmt decimal - srom >= 11 0xAB (&0xE, >>1) +u8 maxp5ga0[4] { + #desc <variable description> + #help <variable help> } -u8 tssiposslope2g { - sfmt decimal - srom >= 11 0xAB (&0x1) +u8 maxp5ga1[4] { + #desc <variable description> + #help <variable help> } -u8 gainctrlsph { - sfmt decimal - srom >= 11 0xAC (&0xF8, >>3) +u8 maxp5ga2[4] { + #desc <variable description> + #help <variable help> } -u8 papdcap5g { - sfmt decimal - srom >= 11 0xAC (&0x4, >>2) +u8 maxp5ga3[1] { + #desc <variable description> + #help <variable help> } -u8 tworangetssi5g { - sfmt decimal - srom >= 11 0xAC (&0x2, >>1) +u8 maxp5gha0 { + #desc <variable description> + #help <variable help> } -u8 pdgain5g { - sfmt decimal - srom >= 11 u16 0xAC (&0x1F0, >>4) +u8 maxp5gha1 { + #desc <variable description> + #help <variable help> } -u8 epagain5g { - sfmt decimal - srom >= 11 0xAD (&0xE, >>1) +u8 maxp5gha2 { + #desc <variable description> + #help <variable help> } -u8 tssiposslope5g { - sfmt decimal - srom >= 11 0xAD (&0x1) +u8 maxp5gha3 { + #desc <variable description> + #help <variable help> } - -# 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 maxp5gla0 { + #desc <variable description> + #help <variable help> } - -# 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 maxp5gla1 { + #desc <variable description> + #help <variable help> } -u8 ledbh1 { - all1 ignore - srom 1-3 0x64 - srom 4 0x56 - srom 5-7 0x76 - srom 8-10 0x96 - srom >= 11 0x9A +u8 maxp5gla2 { + #desc <variable description> + #help <variable help> } -u8 ledbh2 { - all1 ignore - srom 1-3 0x67 - srom 4 0x59 - srom 5-7 0x79 - srom 8-10 0x99 - srom >= 11 0x9D +u8 maxp5gla3 { + #desc <variable description> + #help <variable help> } -u8 ledbh3 { - all1 ignore - srom 1-3 0x66 - srom 4 0x58 - srom 5-7 0x78 - srom 8-10 0x98 - srom >= 11 0x9C +u16 mcs2gpo0 { + #desc <variable description> + #help <variable help> } - -# 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 +u16 mcs2gpo1 { + #desc <variable description> + #help <variable help> } - -# 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 +u16 mcs2gpo2 { + #desc <variable description> + #help <variable help> } -u32 legofdmbw20ul2gpo { - srom 9-10 0x148 +u16 mcs2gpo3 { + #desc <variable description> + #help <variable help> } - -# 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 +u16 mcs2gpo4 { + #desc <variable description> + #help <variable help> } -u32 legofdmbw20ul5glpo { - srom 9-10 0x150 +u16 mcs2gpo5 { + #desc <variable description> + #help <variable help> } -u32 legofdmbw205gmpo { - srom 9-10 0x154 +u16 mcs2gpo6 { + #desc <variable description> + #help <variable help> } -u32 legofdmbw20ul5gmpo { - srom 9-10 0x158 +u16 mcs2gpo7 { + #desc <variable description> + #help <variable help> } -u32 legofdmbw205ghpo { - srom 9-10 0x15C +u16 mcs32po { + #desc <variable description> + #help <variable help> } -u32 legofdmbw20ul5ghpo { - srom 9-10 0x160 +u16 mcs5ghpo0 { + #desc <variable description> + #help <variable help> } - -# 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 mcs5ghpo1 { + #desc <variable description> + #help <variable help> } - -# 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 +u16 mcs5ghpo2 { + #desc <variable description> + #help <variable help> } -u32 mcsbw20ul2gpo { - srom 9-10 0x168 +u16 mcs5ghpo3 { + #desc <variable description> + #help <variable help> } -u32 mcsbw402gpo { - srom 9-10 0x16C - srom >= 11 0x158 +u16 mcs5ghpo4 { + #desc <variable description> + #help <variable 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 +u16 mcs5ghpo5 { + #desc <variable description> + #help <variable help> } -u32 mcsbw20ul5ghpo { - srom 9-10 0x18C +u16 mcs5ghpo6 { + #desc <variable description> + #help <variable help> } -u32 mcsbw405ghpo { - srom 9-10 0x190 - srom >= 11 0x184 +u16 mcs5ghpo7 { + #desc <variable description> + #help <variable 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 +u16 mcs5glpo0 { + #desc <variable description> + #help <variable help> } -u32 mcsbw20ul5glpo { - srom 9-10 0x174 +u16 mcs5glpo1 { + #desc <variable description> + #help <variable help> } -u32 mcsbw405glpo { - srom 9-10 0x178 - srom >= 11 0x164 +u16 mcs5glpo2 { + #desc <variable description> + #help <variable 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 +u16 mcs5glpo3 { + #desc <variable description> + #help <variable help> } -u32 mcsbw20ul5gmpo { - srom 9-10 0x180 +u16 mcs5glpo4 { + #desc <variable description> + #help <variable help> } -u32 mcsbw405gmpo { - srom 9-10 0x184 - srom >= 11 0x174 +u16 mcs5glpo5 { + #desc <variable description> + #help <variable 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 mcs5glpo6 { + #desc <variable description> + #help <variable help> } - -u8 measpower { - srom 8-10 0xB4 (&0xFE, >>1) - srom >= 11 0xB0 (&0xFE, >>1) +u16 mcs5glpo7 { + #desc <variable description> + #help <variable help> } -u8 measpower1 { - srom 8-10 0xBF (&0x7F) - srom >= 11 0xBB (&0x7F) +u16 mcs5gpo0 { + #desc <variable description> + #help <variable help> } -u8 measpower2 { - srom 8-10 u16 0xBE (&0x3F80, >>7) - srom >= 11 u16 0xBA (&0x3F80, >>7) +u16 mcs5gpo1 { + #desc <variable description> + #help <variable help> } -u16 rawtempsense { - srom 8-10 0xB4 (&0x1FF) - srom >= 11 0xB0 (&0x1FF) +u16 mcs5gpo2 { + #desc <variable description> + #help <variable help> } - -u8 noiselvl2ga0 { - sfmt decimal - srom 8-10 0x1AB (&0x1F) - srom >= 11 0x1BD (&0x1F) +u16 mcs5gpo3 { + #desc <variable description> + #help <variable help> } -u8 noiselvl2ga1 { - sfmt decimal - srom 8-10 u16 0x1AA (&0x3E0, >>5) - srom >= 11 u16 0x1BC (&0x3E0, >>5) +u16 mcs5gpo4 { + #desc <variable description> + #help <variable help> } -u8 noiselvl2ga2 { - sfmt decimal - srom 8-10 0x1AA (&0x7C, >>2) - srom >= 11 0x1BC (&0x7C, >>2) +u16 mcs5gpo5 { + #desc <variable description> + #help <variable help> } -u8[4] noiselvl5ga0 { - sfmt decimal - srom >= 11 u8 0x1BF (&0x1F), u8 0x1C1 (&0x1F), u8 0x1C3 (&0x1F), u8 0x1C5 (&0x1F) +u16 mcs5gpo6 { + #desc <variable description> + #help <variable help> } -u8[4] noiselvl5ga1 { - sfmt decimal - srom >= 11 u16[4] 0x1BE (&0x3E0, >>5) +u16 mcs5gpo7 { + #desc <variable description> + #help <variable help> } -u8[4] noiselvl5ga2 { - sfmt decimal - srom >= 11 u8 0x1BE (&0x7C, >>2), u8 0x1C0 (&0x7C, >>2), u8 0x1C2 (&0x7C, >>2), u8 0x1C4 (&0x7C, >>2) +u32 mcsbw202gpo { + #desc <variable description> + #help <variable help> } - -# paparambwver -u8 paparambwver { - sfmt decimal - srom >= 11 0x190 (&0xF0, >>4) +u32 mcsbw205ghpo { + #desc <variable description> + #help <variable help> } - -# PA parameters: 8 (sromrev == 1) -# or 9 (sromrev > 1) bytes -# -u16 pa0b0 { - sfmt decimal - srom 1-3 0x5E - srom 8-10 0xC2 +u32 mcsbw205glpo { + #desc <variable description> + #help <variable help> } -u16 pa0b1 { - sfmt decimal - srom 1-3 0x60 - srom 8-10 0xC4 +u32 mcsbw205gmpo { + #desc <variable description> + #help <variable help> } -u16 pa0b2 { - sfmt decimal - srom 1-3 0x62 - srom 8-10 0xC6 +u32 mcsbw20ul2gpo { + #desc <variable description> + #help <variable help> } -u8 pa0itssit { - sfmt decimal - srom 1-3 0x71 - srom 8-10 0xC0 +u32 mcsbw20ul5ghpo { + #desc <variable description> + #help <variable help> } -u8 pa0maxpwr { - sfmt decimal - srom 1-3 0x69 - srom 8-10 0xC1 +u32 mcsbw20ul5glpo { + #desc <variable description> + #help <variable help> } -u8 opo { - srom 2-3 0x79 - srom 8-10 0x143 +u32 mcsbw20ul5gmpo { + #desc <variable description> + #help <variable help> } - -# 5G PA params -u16 pa1b0 { - sfmt decimal - srom 1-3 0x6A - srom 8-10 0xCC +u32 mcsbw402gpo { + #desc <variable description> + #help <variable help> } -u16 pa1b1 { - sfmt decimal - srom 1-3 0x6C - srom 8-10 0xCE +u32 mcsbw405ghpo { + #desc <variable description> + #help <variable help> } -u16 pa1b2 { - sfmt decimal - srom 1-3 0x6E - srom 8-10 0xD0 +u32 mcsbw405glpo { + #desc <variable description> + #help <variable help> } -u16 pa1lob0 { - sfmt decimal - srom 2-3 0x3C - srom 8-10 0xD2 +u32 mcsbw405gmpo { + #desc <variable description> + #help <variable help> } -u16 pa1lob1 { - sfmt decimal - srom 2-3 0x3E - srom 8-10 0xD4 +u32 mcsbw805ghpo { + #desc <variable description> + #help <variable help> } -u16 pa1lob2 { - sfmt decimal - srom 2-3 0x40 - srom 8-10 0xD6 +u32 mcsbw805glpo { + #desc <variable description> + #help <variable help> } -u16 pa1hib0 { - sfmt decimal - srom 2-3 0x42 - srom 8-10 0xD8 +u32 mcsbw805gmpo { + #desc <variable description> + #help <variable help> } -u16 pa1hib1 { - sfmt decimal - srom 2-3 0x44 - srom 8-10 0xDA +u16 mcslr5ghpo { + #desc <variable description> + #help <variable help> } -u16 pa1hib2 { - sfmt decimal - srom 2-3 0x46 - srom 8-10 0xDC +u16 mcslr5glpo { + #desc <variable description> + #help <variable help> } -u8 pa1itssit { - sfmt decimal - srom 1-3 0x70 - srom 8-10 0xC8 +u16 mcslr5gmpo { + #desc <variable description> + #help <variable help> } -u8 pa1maxpwr { - sfmt decimal - srom 1-3 0x68 - srom 8-10 0xC9 +u8 measpower { + #desc <variable description> + #help <variable help> } -u8 pa1lomaxpwr { - sfmt decimal - srom 2-3 0x3A - srom 8-10 0xCA +u8 measpower1 { + #desc <variable description> + #help <variable help> } -u8 pa1himaxpwr { - sfmt decimal - srom 2-3 0x3B - srom 8-10 0xCB +u8 measpower2 { + #desc <variable description> + #help <variable help> } - -u16 pdoffset40ma0 { - srom >= 11 0xCA +u8 noisecaloffset { + #desc <variable description> + #help <variable help> } -u16 pdoffset40ma1 { - srom >= 11 0xCC +u8 noisecaloffset5g { + #desc <variable description> + #help <variable help> } -u16 pdoffset40ma2 { - srom >= 11 0xCE +u8 noiselvl2ga0 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 pdoffset80ma0 { - srom >= 11 0xD0 +u8 noiselvl2ga1 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 pdoffset80ma1 { - srom >= 11 0xD2 +u8 noiselvl2ga2 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 pdoffset80ma2 { - srom >= 11 0xD4 +u8 noiselvl5ga0[4] { + fmt decimal + #desc <variable description> + #help <variable help> } - -u8 pdoffset2g40ma0 { - srom >= 11 0xC9 (&0xF) +u8 noiselvl5ga1[4] { + fmt decimal + #desc <variable description> + #help <variable help> } -u8 pdoffset2g40ma1 { - srom >= 11 0xC9 (&0xF0, >>4) +u8 noiselvl5ga2[4] { + fmt decimal + #desc <variable description> + #help <variable help> } -u8 pdoffset2g40ma2 { - srom >= 11 0xC8 (&0xF) +u8 noiselvl5gha0 { + #desc <variable description> + #help <variable help> } -u8 pdoffset2g40mvalid { - srom >= 11 0xC8 (&0x80, >>7) +u8 noiselvl5gha1 { + #desc <variable description> + #help <variable help> } - -# 40Mhz channel 2g/5g power offset -u16 bw40po { - srom 4-7 0x18E - srom 8 0x196 +u8 noiselvl5gha2 { + #desc <variable description> + #help <variable help> } - -# 40Mhz channel dup 2g/5g power offset -u16 bwduppo { - srom 4-7 0x190 - srom 8 0x198 +u8 noiselvl5gla0 { + #desc <variable description> + #help <variable help> } - -# cck2g/ofdm2g/ofdm5g power offset -u16 cck2gpo { - srom 4-7 0x138 - srom 8 0x140 +u8 noiselvl5gla1 { + #desc <variable description> + #help <variable help> } -u32 ofdm2gpo { - srom 4-7 0x13A - srom 8 0x142 +u8 noiselvl5gla2 { + #desc <variable description> + #help <variable help> } -u32 ofdm5gpo { - srom 4-7 0x13E - srom 8 0x146 +u8 noiselvl5gma0 { + #desc <variable description> + #help <variable help> } -u32 ofdm5glpo { - srom 4-7 0x142 - srom 8 0x14A +u8 noiselvl5gma1 { + #desc <variable description> + #help <variable help> } -u32 ofdm5ghpo { - srom 4-7 0x146 - srom 8 0x14E +u8 noiselvl5gma2 { + #desc <variable description> + #help <variable help> } - -# cdd2g/5g power offset -u16 cddpo { - srom 4-7 0x18A - srom 8 0x192 +u8 noiselvl5gua0 { + #desc <variable description> + #help <variable help> } - -# mcs2g power offset -u16 mcs2gpo0 { - srom 4-7 0x14A - srom 8 0x152 +u8 noiselvl5gua1 { + #desc <variable description> + #help <variable help> } -u16 mcs2gpo1 { - srom 4-7 0x14C - srom 8 0x154 +u8 noiselvl5gua2 { + #desc <variable description> + #help <variable help> } -u16 mcs2gpo2 { - srom 4-7 0x14E - srom 8 0x156 +u32 ofdm2gpo { + #desc <variable description> + #help <variable help> } -u16 mcs2gpo3 { - srom 4-7 0x150 - srom 8 0x158 +u32 ofdm5ghpo { + #desc <variable description> + #help <variable help> } -u16 mcs2gpo4 { - srom 4-7 0x152 - srom 8 0x15A +u32 ofdm5glpo { + #desc <variable description> + #help <variable help> } -u16 mcs2gpo5 { - srom 4-7 0x154 - srom 8 0x15C +u32 ofdm5gpo { + #desc <variable description> + #help <variable help> } -u16 mcs2gpo6 { - srom 4-7 0x156 - srom 8 0x15E +u16 ofdmlrbw202gpo { + #desc <variable description> + #help <variable help> } -u16 mcs2gpo7 { - srom 4-7 0x158 - srom 8 0x160 +u8 opo { + fmt decimal + #desc <variable description> + #help <variable help> } - -# mcs5g low-high band power offset -u16 mcs5glpo0 { - srom 4-7 0x16A - srom 8 0x172 +i16 pa0b0 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5glpo1 { - srom 4-7 0x16C - srom 8 0x174 +i16 pa0b1 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5glpo2 { - srom 4-7 0x16E - srom 8 0x176 +i16 pa0b2 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5glpo3 { - srom 4-7 0x170 - srom 8 0x178 +u8 pa0itssit { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5glpo4 { - srom 4-7 0x172 - srom 8 0x17A +u8 pa0maxpwr { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5glpo5 { - srom 4-7 0x174 - srom 8 0x17C +i16 pa1b0 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5glpo6 { - srom 4-7 0x176 - srom 8 0x17E +i16 pa1b1 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5glpo7 { - srom 4-7 0x178 - srom 8 0x180 +i16 pa1b2 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5ghpo0 { - srom 4-7 0x17A - srom 8 0x182 +i16 pa1hib0 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5ghpo1 { - srom 4-7 0x17C - srom 8 0x184 +i16 pa1hib1 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5ghpo2 { - srom 4-7 0x17E - srom 8 0x186 +i16 pa1hib2 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5ghpo3 { - srom 4-7 0x180 - srom 8 0x188 +u8 pa1himaxpwr { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5ghpo4 { - srom 4-7 0x182 - srom 8 0x18A +u8 pa1itssit { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5ghpo5 { - srom 4-7 0x184 - srom 8 0x18C +i16 pa1lob0 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5ghpo6 { - srom 4-7 0x186 - srom 8 0x18E +i16 pa1lob1 { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5ghpo7 { - srom 4-7 0x188 - srom 8 0x190 +i16 pa1lob2 { + fmt decimal + #desc <variable description> + #help <variable help> } - -# mcs5g mid band power offset -u16 mcs5gpo0 { - srom 4-7 0x15A - srom 8 0x162 +u8 pa1lomaxpwr { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5gpo1 { - srom 4-7 0x15C - srom 8 0x164 +u8 pa1maxpwr { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5gpo2 { - srom 4-7 0x15E - srom 8 0x166 +i16 pa2ga0[3] { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5gpo3 { - srom 4-7 0x160 - srom 8 0x168 +i16 pa2ga1[3] { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5gpo4 { - srom 4-7 0x162 - srom 8 0x16A +i16 pa2ga2[3] { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5gpo5 { - srom 4-7 0x164 - srom 8 0x16C +i16 pa2ga3[3] { + fmt decimal + #desc <variable description> + #help <variable help> } -u16 mcs5gpo6 { - srom 4-7 0x166 - srom 8 0x16E +u16 pa2gccka0[3] { + #desc <variable description> + #help <variable help> } -u16 mcs5gpo7 { - srom 4-7 0x168 - srom 8 0x170 +u16 pa2gw0a0 { + #desc <variable description> + #help <variable help> } - -# stbc2g/5g power offset -u16 stbcpo { - srom 4-7 0x18C - srom 8 0x194 +u16 pa2gw0a1 { + #desc <variable description> + #help <variable help> } - -u8 regrev { - srom 3 0x78 - srom 4 0x55 - srom 5-7 0x47 - srom 8-10 0x95 - srom >= 11 0x99 +u16 pa2gw0a2 { + #desc <variable description> + #help <variable 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 pa2gw0a3 { + #desc <variable description> + #help <variable help> } -u8 rssismc2g { - srom 3 0x51 (&0xF0, >>4) - srom 8-10 0xA5 (&0xF0, >>4) +u16 pa2gw1a0 { + #desc <variable description> + #help <variable help> } -u8 rssisav2g { - srom 3 0x50 (&0x7) - srom 8-10 0xA4 (&0x7) +u16 pa2gw1a1 { + #desc <variable description> + #help <variable help> } -u8 bxa2g { - srom 3 0x50 (&0x18, >>3) - srom 8-10 0xA4 (&0x18, >>3) +u16 pa2gw1a2 { + #desc <variable description> + #help <variable 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 pa2gw1a3 { + #desc <variable description> + #help <variable help> } -u8 rssismc5g { - srom 3 0x53 (&0xF0, >>4) - srom 8-10 0xA7 (&0xF0, >>4) +u16 pa2gw2a0 { + #desc <variable description> + #help <variable help> } -u8 rssisav5g { - srom 3 0x52 (&0x7) - srom 8-10 0xA6 (&0x7) +u16 pa2gw2a1 { + #desc <variable description> + #help <variable help> } -u8 bxa5g { - srom 3 0x52 (&0x18, >>3) - srom 8-10 0xA6 (&0x18, >>3) +u16 pa2gw2a2 { + #desc <variable description> + #help <variable help> } - -u8 rxgainerr2ga0 { - srom 8-10 0x19B (&0x3F) - srom >= 11 0x1C7 (&0x3F) +u16 pa2gw2a3 { + #desc <variable description> + #help <variable help> } -u8 rxgainerr2ga1 { - srom 8-10 u16 0x19A (&0x7C0, >>6) - srom >= 11 u16 0x1C6 (&0x7C0, >>6) +u16 pa2gw3a0 { + #desc <variable description> + #help <variable help> } -u8 rxgainerr2ga2 { - srom 8-10 0x19A (&0xF8, >>3) - srom >= 11 0x1C6 (&0xF8, >>3) +u16 pa2gw3a1 { + #desc <variable description> + #help <variable help> } -u8[4] rxgainerr5ga0 { - srom >= 11 u8 0x1C9 (&0x3F), u8 0x1CB (&0x3F), u8 0x1CD (&0x3F), u8 0x1CF (&0x3F) +u16 pa2gw3a2 { + #desc <variable description> + #help <variable help> } -u8[4] rxgainerr5ga1 { - srom >= 11 u16[4] 0x1C8 (&0x7C0, >>6) +u16 pa2gw3a3 { + #desc <variable description> + #help <variable help> } -u8[4] rxgainerr5ga2 { - srom >= 11 u8 0x1C8 (&0xF8, >>3), u8 0x1CA (&0xF8, >>3), u8 0x1CC (&0xF8, >>3), u8 0x1CE (&0xF8, >>3) +i16 pa5ga0[12] { + fmt decimal + #desc <variable description> + #help <variable help> } -u8 rxgainerr5gha0 { - srom 8-10 0x1A1 (&0x3F) +i16 pa5ga1[12] { + fmt decimal + #desc <variable description> + #help <variable help> } -u8 rxgainerr5gha1 { - srom 8-10 u16 0x1A0 (&0x7C0, >>6) +i16 pa5ga2[12] { + fmt decimal + #desc <variable description> + #help <variable help> } -u8 rxgainerr5gha2 { - srom 8-10 0x1A0 (&0xF8, >>3) +i16 pa5ga3[12] { + fmt decimal + #desc <variable description> + #help <variable help> } -u8 rxgainerr5gla0 { - srom 8-10 0x19D (&0x3F) +u16 pa5gbw4080a0[12] { + #desc <variable description> + #help <variable help> } -u8 rxgainerr5gla1 { - srom 8-10 u16 0x19C (&0x7C0, >>6) +u16 pa5gbw4080a1[12] { + #desc <variable description> + #help <variable help> } -u8 rxgainerr5gla2 { - srom 8-10 0x19C (&0xF8, >>3) +u16 pa5gbw40a0[12] { + #desc <variable description> + #help <variable help> } -u8 rxgainerr5gma0 { - srom 8-10 0x19F (&0x3F) +u16 pa5gbw80a0[12] { + #desc <variable description> + #help <variable help> } -u8 rxgainerr5gma1 { - srom 8-10 u16 0x19E (&0x7C0, >>6) +u16 pa5ghw0a0 { + #desc <variable description> + #help <variable help> } -u8 rxgainerr5gma2 { - srom 8-10 0x19E (&0xF8, >>3) +u16 pa5ghw0a1 { + #desc <variable description> + #help <variable help> } -u8 rxgainerr5gua0 { - srom 8-10 0x1A3 (&0x3F) +u16 pa5ghw0a2 { + #desc <variable description> + #help <variable help> } -u8 rxgainerr5gua1 { - srom 8-10 u16 0x1A2 (&0x7C0, >>6) +u16 pa5ghw0a3 { + #desc <variable description> + #help <variable help> } -u8 rxgainerr5gua2 { - srom 8-10 0x1A2 (&0xF8, >>3) +u16 pa5ghw1a0 { + #desc <variable description> + #help <variable help> } - -# 4328 2G RX power offset -i8 rxpo2g { - sfmt decimal - srom 3 0x5B - srom 8-10 0xAD +u16 pa5ghw1a1 { + #desc <variable description> + #help <variable help> } - -# 4328 5G RX power offset -i8 rxpo5g { - sfmt decimal - srom 3 0x5A - srom 8-10 0xAC +u16 pa5ghw1a2 { + #desc <variable description> + #help <variable help> } - -u16 subband5gver { - srom 8-10 u8 0x1A5 (&0x7) - srom >= 11 0xD6 +u16 pa5ghw1a3 { + #desc <variable description> + #help <variable help> } - -# 2 bytes -# byte1 tempthresh -# byte2 period(msb 4 bits) | hysterisis(lsb 4 bits) -# -u8 tempthresh { - srom 8-10 0xB2 - srom >= 11 0xAE +u16 pa5ghw2a0 { + #desc <variable description> + #help <variable help> } -u8 temps_period { - sfmt decimal - srom 8-10 0xBC (&0xF) - srom >= 11 0xB8 (&0xF) +u16 pa5ghw2a1 { + #desc <variable description> + #help <variable help> } -u8 temps_hysteresis { - sfmt decimal - srom 8-10 0xBC (&0xF0, >>4) - srom >= 11 0xB8 (&0xF0, >>4) +u16 pa5ghw2a2 { + #desc <variable description> + #help <variable help> } -u8 tempoffset { - sfmt decimal - srom 8-10 0xB3 - srom >= 11 0xAF +u16 pa5ghw2a3 { + #desc <variable description> + #help <variable help> } -u8 tempsense_slope { - srom 8-10 0xB7 - srom >= 11 0xB3 +u16 pa5ghw3a0 { + #desc <variable description> + #help <variable help> } -u8 tempcorrx { - srom 8-10 0xB6 (&0xFC, >>2) - srom >= 11 0xB2 (&0xFC, >>2) +u16 pa5ghw3a1 { + #desc <variable description> + #help <variable help> } -u8 tempsense_option { - srom 8-10 0xB6 (&0x3) - srom >= 11 0xB2 (&0x3) +u16 pa5ghw3a2 { + #desc <variable description> + #help <variable help> } -u8 phycal_tempdelta { - sfmt decimal - srom 8-10 0xBD - srom >= 11 0xB9 +u16 pa5ghw3a3 { + #desc <variable description> + #help <variable help> } - -# 4328 2G TR isolation, 1 byte -u8 tri2g { - srom 3 0x55 - srom 8-10 0xA9 +u16 pa5glw0a0 { + #desc <variable description> + #help <variable help> } - -# 4328 5G TR isolation, 3 bytes -u8 tri5gl { - srom 3 0x57 - srom 8-10 0xAB +u16 pa5glw0a1 { + #desc <variable description> + #help <variable help> } -u8 tri5g { - srom 3 0x54 - srom 8-10 0xA8 +u16 pa5glw0a2 { + #desc <variable description> + #help <variable help> } -u8 tri5gh { - srom 3 0x56 - srom 8-10 0xAA +u16 pa5glw0a3 { + #desc <variable description> + #help <variable help> } - -# phy txbf rpcalvars -u16 rpcal2g { - srom >= 11 0x16C +u16 pa5glw1a0 { + #desc <variable description> + #help <variable help> } -u16 rpcal5gb0 { - srom >= 11 0x16E +u16 pa5glw1a1 { + #desc <variable description> + #help <variable help> } -u16 rpcal5gb1 { - srom >= 11 0x17C +u16 pa5glw1a2 { + #desc <variable description> + #help <variable help> } -u16 rpcal5gb2 { - srom >= 11 0x17E +u16 pa5glw1a3 { + #desc <variable description> + #help <variable help> } -u16 rpcal5gb3 { - srom >= 11 0x18C +u16 pa5glw2a0 { + #desc <variable description> + #help <variable help> } - -# Crystal frequency in kilohertz -u32 xtalfreq { - sfmt decimal - srom >= 11 u16 0xB4 +u16 pa5glw2a1 { + #desc <variable description> + #help <variable help> } - -# N-PHY tx power workaround -u8 txpid2ga0 { - srom 4-7 0x63 +u16 pa5glw2a2 { + #desc <variable description> + #help <variable help> } -u8 txpid2ga1 { - srom 4-7 0x62 +u16 pa5glw2a3 { + #desc <variable description> + #help <variable help> } -u8 txpid2ga2 { - srom 4-7 0x65 +u16 pa5glw3a0 { + #desc <variable description> + #help <variable help> } -u8 txpid2ga3 { - srom 4-7 0x64 +u16 pa5glw3a1 { + #desc <variable description> + #help <variable help> } -u8 txpid5ga0 { - srom 4-7 0x67 +u16 pa5glw3a2 { + #desc <variable description> + #help <variable help> } -u8 txpid5ga1 { - srom 4-7 0x66 +u16 pa5glw3a3 { + #desc <variable description> + #help <variable help> } -u8 txpid5ga2 { - srom 4-7 0x69 +u16 pa5gw0a0 { + #desc <variable description> + #help <variable help> } -u8 txpid5ga3 { - srom 4-7 0x68 +u16 pa5gw0a1 { + #desc <variable description> + #help <variable help> } -u8 txpid5gha0 { - srom 4-7 0x6F +u16 pa5gw0a2 { + #desc <variable description> + #help <variable help> } -u8 txpid5gha1 { - srom 4-7 0x6E +u16 pa5gw0a3 { + #desc <variable description> + #help <variable help> } -u8 txpid5gha2 { - srom 4-7 0x71 +u16 pa5gw1a0 { + #desc <variable description> + #help <variable help> } -u8 txpid5gha3 { - srom 4-7 0x70 +u16 pa5gw1a1 { + #desc <variable description> + #help <variable help> } -u8 txpid5gla0 { - srom 4-7 0x6B +u16 pa5gw1a2 { + #desc <variable description> + #help <variable help> } -u8 txpid5gla1 { - srom 4-7 0x6A +u16 pa5gw1a3 { + #desc <variable description> + #help <variable help> } -u8 txpid5gla2 { - srom 4-7 0x6D +u16 pa5gw2a0 { + #desc <variable description> + #help <variable help> } -u8 txpid5gla3 { - srom 4-7 0x6C +u16 pa5gw2a1 { + #desc <variable description> + #help <variable help> } - -u16 cckPwrOffset { - srom 10 0x1B4 +u16 pa5gw2a2 { + #desc <variable description> + #help <variable help> } -u8[6] et1macaddr { - sfmt macaddr - srom 0-2 u8 0x55, u8 0x54, u8 0x57, u8 0x56, u8 0x59, u8 0x58 +u16 pa5gw2a3 { + #desc <variable description> + #help <variable help> } -u8 eu_edthresh2g { - srom 8 0x1A9 - srom 9 0x199 - srom 10 0x199 - srom 11 0x1D1 +u16 pa5gw3a0 { + #desc <variable description> + #help <variable help> } -u8 eu_edthresh5g { - srom 8 0x1A8 - srom 9 0x198 - srom 10 0x198 - srom 11 0x1D0 +u16 pa5gw3a1 { + #desc <variable description> + #help <variable help> } -u8 freqoffset_corr { - srom 8-10 0xB9 (&0xF) +u16 pa5gw3a2 { + #desc <variable description> + #help <variable help> } -u8 hw_iqcal_en { - srom 8-10 0xB9 (&0x20, >>5) +u16 pa5gw3a3 { + #desc <variable description> + #help <variable help> } -u8[6] il0macaddr { - sfmt macaddr - srom 0-2 u8 0x49, u8 0x48, u8 0x51, u8 0x50, u8 0x53, u8 0x52 +u8 paparambwver { + fmt decimal + #desc <variable description> + #help <variable help> } -u8 iqcal_swp_dis { - srom 8-10 0xB9 (&0x10, >>4) +u8 papdcap2g { + fmt decimal + #desc <variable description> + #help <variable help> } - -u8 noisecaloffset { - srom 8-9 0x1B5 +u8 papdcap5g { + fmt decimal + #desc <variable description> + #help <variable help> } -u8 noisecaloffset5g { - srom 8-9 0x1B4 +u8 pcieingress_war { + #desc <variable description> + #help <variable help> } -u8 noiselvl5gha0 { - srom 8-10 0x1B1 (&0x1F) +u8 pdetrange2g { + #desc <variable description> + #help <variable help> } -u8 noiselvl5gha1 { - srom 8-10 u16 0x1B0 (&0x3E0, >>5) +u8 pdetrange5g { + #desc <variable description> + #help <variable help> } -u8 noiselvl5gha2 { - srom 8-10 0x1B0 (&0x7C, >>2) +u8 pdgain2g { + fmt decimal + #desc <variable description> + #help <variable help> } -u8 noiselvl5gla0 { - srom 8-10 0x1AD (&0x1F) +u8 pdgain5g { + fmt decimal + #desc <variable description> + #help <variable help> } -u8 noiselvl5gla1 { - srom 8-10 u16 0x1AC (&0x3E0, >>5) +u8 pdoffset2g40ma0 { + #desc <variable description> + #help <variable help> } -u8 noiselvl5gla2 { - srom 8-10 0x1AC (&0x7C, >>2) +u8 pdoffset2g40ma1 { + #desc <variable description> + #help <variable help> } -u8 noiselvl5gma0 { - srom 8-10 0x1AF (&0x1F) +u8 pdoffset2g40ma2 { + #desc <variable description> + #help <variable help> } -u8 noiselvl5gma1 { - srom 8-10 u16 0x1AE (&0x3E0, >>5) +u8 pdoffset2g40mvalid { + #desc <variable description> + #help <variable help> } -u8 noiselvl5gma2 { - srom 8-10 0x1AE (&0x7C, >>2) +u16 pdoffset40ma0 { + #desc <variable description> + #help <variable help> } -u8 noiselvl5gua0 { - srom 8-10 0x1B3 (&0x1F) +u16 pdoffset40ma1 { + #desc <variable description> + #help <variable help> } -u8 noiselvl5gua1 { - srom 8-10 u16 0x1B2 (&0x3E0, >>5) +u16 pdoffset40ma2 { + #desc <variable description> + #help <variable help> } -u8 noiselvl5gua2 { - srom 8-10 0x1B2 (&0x7C, >>2) +u16 pdoffset80ma0 { + #desc <variable description> + #help <variable help> } - -u8 pcieingress_war { - srom 8-10 0x1A7 (&0xF) +u16 pdoffset80ma1 { + #desc <variable description> + #help <variable help> } - -u8 pdoffsetcckma0 { - srom >= 11 0x18F (&0xF) +u16 pdoffset80ma2 { + #desc <variable description> + #help <variable help> } -u8 pdoffsetcckma1 { - srom >= 11 0x18F (&0xF0, >>4) +u8 pdoffsetcckma0 { + #desc <variable description> + #help <variable help> } -u8 pdoffsetcckma2 { - srom >= 11 0x18E (&0xF) +u8 pdoffsetcckma1 { + #desc <variable description> + #help <variable help> } - -u8 sar2g { - srom 9-10 0x1A9 - srom >= 11 0x1BB +u8 pdoffsetcckma2 { + #desc <variable description> + #help <variable help> } -u8 sar5g { - srom 9-10 0x1A8 - srom >= 11 0x1BA +u8 phycal_tempdelta { + fmt decimal + #desc <variable description> + #help <variable help> } - -u32[5] swctrlmap_2g { - srom 10 u32[4] 0x1B8, u16 0x1C8 +u16 rawtempsense { + #desc <variable description> + #help <variable help> } - -u16 tssifloor2g { - srom >= 11 0xBE (&0x3FF) +u8 regrev { + fmt decimal + #desc <variable description> + #help <variable help> } -u16[4] tssifloor5g { - srom >= 11 0xC0 (&0x3FF) +u32 rmax { + fmt decimal + #desc <variable description> + #help <variable help> } - -u8 txidxcap2g { - srom >= 11 u16 0x1A8 (&0xFF0, >>4) +u32 rmin { + fmt decimal + #desc <variable description> + #help <variable help> } -u8 txidxcap5g { - srom >= 11 u16 0x1AC (&0xFF0, >>4) +u16 rpcal2g { + #desc <variable description> + #help <variable help> +} +u16 rpcal5gb0 { + #desc <variable description> + #help <variable help> +} +u16 rpcal5gb1 { + #desc <variable description> + #help <variable help> +} +u16 rpcal5gb2 { + #desc <variable description> + #help <variable help> +} +u16 rpcal5gb3 { + #desc <variable description> + #help <variable help> +} +u8 rssisav2g { + #desc <variable description> + #help <variable help> +} +u8 rssisav5g { + #desc <variable description> + #help <variable help> +} +u8 rssismc2g { + #desc <variable description> + #help <variable help> +} +u8 rssismc5g { + #desc <variable description> + #help <variable help> +} +u8 rssismf2g { + #desc <variable description> + #help <variable help> +} +u8 rssismf5g { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr2ga0 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr2ga1 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr2ga2 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5ga0[4] { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5ga1[4] { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5ga2[4] { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5gha0 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5gha1 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5gha2 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5gla0 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5gla1 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5gla2 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5gma0 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5gma1 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5gma2 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5gua0 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5gua1 { + #desc <variable description> + #help <variable help> +} +u8 rxgainerr5gua2 { + #desc <variable description> + #help <variable help> +} +u8 rxgains2gelnagaina0 { + #desc <variable description> + #help <variable help> +} +u8 rxgains2gelnagaina1 { + #desc <variable description> + #help <variable help> +} +u8 rxgains2gelnagaina2 { + #desc <variable description> + #help <variable help> +} +u8 rxgains2gelnagaina3 { + #desc <variable description> + #help <variable help> +} +u8 rxgains2gtrelnabypa0 { + #desc <variable description> + #help <variable help> +} +u8 rxgains2gtrelnabypa1 { + #desc <variable description> + #help <variable help> +} +u8 rxgains2gtrelnabypa2 { + #desc <variable description> + #help <variable help> +} +u8 rxgains2gtrelnabypa3 { + #desc <variable description> + #help <variable help> +} +u8 rxgains2gtrisoa0 { + #desc <variable description> + #help <variable help> +} +u8 rxgains2gtrisoa1 { + #desc <variable description> + #help <variable help> +} +u8 rxgains2gtrisoa2 { + #desc <variable description> + #help <variable help> +} +u8 rxgains2gtrisoa3 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gelnagaina0 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gelnagaina1 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gelnagaina2 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gelnagaina3 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5ghelnagaina0 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5ghelnagaina1 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5ghelnagaina2 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5ghelnagaina3 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5ghtrelnabypa0 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5ghtrelnabypa1 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5ghtrelnabypa2 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5ghtrelnabypa3 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5ghtrisoa0 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5ghtrisoa1 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5ghtrisoa2 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5ghtrisoa3 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gmelnagaina0 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gmelnagaina1 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gmelnagaina2 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gmelnagaina3 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gmtrelnabypa0 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gmtrelnabypa1 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gmtrelnabypa2 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gmtrelnabypa3 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gmtrisoa0 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gmtrisoa1 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gmtrisoa2 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gmtrisoa3 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gtrelnabypa0 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gtrelnabypa1 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gtrelnabypa2 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gtrelnabypa3 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gtrisoa0 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gtrisoa1 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gtrisoa2 { + #desc <variable description> + #help <variable help> +} +u8 rxgains5gtrisoa3 { + #desc <variable description> + #help <variable help> +} +i8 rxpo2g { + fmt decimal + #desc <variable description> + #help <variable help> +} +i8 rxpo5g { + fmt decimal + #desc <variable description> + #help <variable help> +} +u8 sar2g { + #desc <variable description> + #help <variable help> +} +u8 sar5g { + #desc <variable description> + #help <variable help> +} +u16 sb20in40hrpo { + #desc <variable description> + #help <variable help> +} +u16 sb20in40lrpo { + #desc <variable description> + #help <variable help> +} +u16 sb20in80and160hr5ghpo { + #desc <variable description> + #help <variable help> +} +u16 sb20in80and160hr5glpo { + #desc <variable description> + #help <variable help> +} +u16 sb20in80and160hr5gmpo { + #desc <variable description> + #help <variable help> +} +u16 sb20in80and160lr5ghpo { + #desc <variable description> + #help <variable help> +} +u16 sb20in80and160lr5glpo { + #desc <variable description> + #help <variable help> +} +u16 sb20in80and160lr5gmpo { + #desc <variable description> + #help <variable help> +} +u16 sb40and80hr5ghpo { + #desc <variable description> + #help <variable help> +} +u16 sb40and80hr5glpo { + #desc <variable description> + #help <variable help> +} +u16 sb40and80hr5gmpo { + #desc <variable description> + #help <variable help> +} +u16 sb40and80lr5ghpo { + #desc <variable description> + #help <variable help> +} +u16 sb40and80lr5glpo { + #desc <variable description> + #help <variable help> +} +u16 sb40and80lr5gmpo { + #desc <variable description> + #help <variable help> +} +u8 sromrev { + #desc <variable description> + #help <variable help> +} +u16 stbcpo { + #desc <variable description> + #help <variable help> +} +u16 subband5gver { + #desc <variable description> + #help <variable help> +} +u16 subvid { + #desc <variable description> + #help <variable help> +} +u32 swctrlmap_2g[5] { + #desc <variable description> + #help <variable help> +} +u8 tempcorrx { + #desc <variable description> + #help <variable help> +} +u8 tempoffset { + fmt decimal + #desc <variable description> + #help <variable help> +} +u8 temps_hysteresis { + fmt decimal + #desc <variable description> + #help <variable help> +} +u8 temps_period { + fmt decimal + #desc <variable description> + #help <variable help> +} +u8 tempsense_option { + #desc <variable description> + #help <variable help> +} +u8 tempsense_slope { + #desc <variable description> + #help <variable help> +} +u8 tempthresh { + #desc <variable description> + #help <variable help> +} +u8 tri2g { + #desc <variable description> + #help <variable help> +} +u8 tri5g { + #desc <variable description> + #help <variable help> +} +u8 tri5gh { + #desc <variable description> + #help <variable help> +} +u8 tri5gl { + #desc <variable description> + #help <variable help> +} +u8 triso2g { + #desc <variable description> + #help <variable help> +} +u8 triso5g { + #desc <variable description> + #help <variable help> +} +u16 tssifloor2g { + #desc <variable description> + #help <variable help> +} +u16 tssifloor5g[4] { + #desc <variable description> + #help <variable help> +} +u8 tssipos2g { + #desc <variable description> + #help <variable help> +} +u8 tssipos5g { + #desc <variable description> + #help <variable help> +} +u8 tssiposslope2g { + fmt decimal + #desc <variable description> + #help <variable help> +} +u8 tssiposslope5g { + fmt decimal + #desc <variable description> + #help <variable help> +} +u8 tworangetssi2g { + fmt decimal + #desc <variable description> + #help <variable help> +} +u8 tworangetssi5g { + fmt decimal + #desc <variable description> + #help <variable help> +} +u8 txidxcap2g { + #desc <variable description> + #help <variable help> +} +u8 txidxcap5g { + #desc <variable description> + #help <variable help> +} +u8 txpid2ga0 { + #desc <variable description> + #help <variable help> +} +u8 txpid2ga1 { + #desc <variable description> + #help <variable help> +} +u8 txpid2ga2 { + #desc <variable description> + #help <variable help> +} +u8 txpid2ga3 { + #desc <variable description> + #help <variable help> +} +u8 txpid5ga0 { + #desc <variable description> + #help <variable help> +} +u8 txpid5ga1 { + #desc <variable description> + #help <variable help> +} +u8 txpid5ga2 { + #desc <variable description> + #help <variable help> +} +u8 txpid5ga3 { + #desc <variable description> + #help <variable help> +} +u8 txpid5gha0 { + #desc <variable description> + #help <variable help> +} +u8 txpid5gha1 { + #desc <variable description> + #help <variable help> +} +u8 txpid5gha2 { + #desc <variable description> + #help <variable help> +} +u8 txpid5gha3 { + #desc <variable description> + #help <variable help> +} +u8 txpid5gla0 { + #desc <variable description> + #help <variable help> +} +u8 txpid5gla1 { + #desc <variable description> + #help <variable help> +} +u8 txpid5gla2 { + #desc <variable description> + #help <variable help> +} +u8 txpid5gla3 { + #desc <variable description> + #help <variable help> +} +u32 xtalfreq { + fmt decimal + #desc <variable description> + #help <variable 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 - } +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 <sromcrc> +} - # AC-PHY rxgains - u8 rxgains5ghtrelnabypa { - srom >= 11 0x8 (&0x80, >>7) - } - u8 rxgains5ghelnagaina { - srom >= 11 0x8 (&0x7) - } - u8 rxgains5gelnagaina { - srom >= 11 0xA (&0x7) +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 rxgains5gmtrelnabypa { - srom >= 11 0x9 (&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 rxgains2gtrelnabypa { - srom >= 11 0xB (&0x80, >>7) + 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 rxgains5gmtrisoa { - srom >= 11 0x9 (&0x78, >>3) + srom 3 { + 0x072: u32 boardflags { + +0x0: u16 | + +0x8: u16 (<<16) + } } - u8 rxgains5gmelnagaina { - srom >= 11 0x9 (&0x7) - } - u8 rxgains2gelnagaina { - srom >= 11 0xB (&0x7) - } - u8 rxgains5gtrisoa { - srom >= 11 0xA (&0x78, >>3) - } - u8 rxgains5gtrelnabypa { - srom >= 11 0xA (&0x80, >>7) - } - u8 rxgains2gtrisoa { - srom >= 11 0xB (&0x78, >>3) - } - u8 rxgains5ghtrisoa { - srom >= 11 0x8 (&0x78, >>3) + 0x074: u8 ag0 + 0x075: u8 ag1 + 0x076: char ccode[2] { +0x1, +0x0 } + 0x078: u8 opo + srom 3 { + 0x079: u8 regrev + 0x07C: u16 leddc } + 0x07E: u8 sromrev + 0x07F: u8 <sromcrc> +} - # 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 + +srom 4 { + 0x004: u16 boardtype + 0x006: u16 subvid + 0x040: u16 <sromsig> (=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 <sromcrc> +} + + +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 <sromcrc> +} + + +srom 8 { + 0x004: u16 boardtype + 0x006: u16 subvid + 0x060: u16 devid + 0x080: u16 <sromsig> (=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 <sromcrc> +} + + +srom 9-10 { + 0x004: u16 boardtype + 0x006: u16 subvid + 0x060: u16 devid + srom 9 { + 0x080: u16 <sromsig> (=0x5372) } - u8 itt5ga { - srom 4-7 0xA - srom 8-10 0x8 + 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 <sromcrc> } - u8 itt2ga { - srom 4-7 0x0 - srom 8-10 0x0 + srom 10 { + 0x1B4: u16 cckPwrOffset + 0x1B6: u16 <sromsig> (=0x5372) + 0x1B8: u32 swctrlmap_2g[5] { + +0x0: u32[4], + +0x10: u16 + } + 0x1CA: u8 sromrev + 0x1CB: u8 <sromcrc> } } + + +srom 11 { + 0x004: u16 boardtype + 0x006: u16 subvid + 0x060: u16 devid + 0x080: u16 <sromsig> (=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 <sromcrc> +} |
