aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/bhnd/nvram
diff options
context:
space:
mode:
authorLandon J. Fuller <landonf@FreeBSD.org>2016-11-26 23:22:32 +0000
committerLandon J. Fuller <landonf@FreeBSD.org>2016-11-26 23:22:32 +0000
commit77cb4d3e5016a2fd090d07ed3e01a199723641d9 (patch)
treeeefb722db9e959216f82ba0d0d320df9d68a9a4e /sys/dev/bhnd/nvram
parentb84ef73179e9018f9197ba5293c41459f3b468d3 (diff)
Notes
Diffstat (limited to 'sys/dev/bhnd/nvram')
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram.c189
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram.h88
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_cfe.c373
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_common.c661
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_common.h180
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_data.c527
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_data.h137
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c748
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c380
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h73
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h (renamed from sys/dev/bhnd/nvram/bhnd_sprom_parser.h)62
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c586
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c1988
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h150
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c662
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_data_tlvreg.h (renamed from sys/dev/bhnd/nvram/bhnd_nvram_parserreg.h)41
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_datavar.h188
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_io.c205
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_io.h79
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_iobuf.c341
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_iores.c302
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_iovar.h106
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_parser.c1578
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_parser.h101
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_parservar.h86
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_private.h402
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_store.c572
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_store.h (renamed from sys/dev/bhnd/nvram/bhnd_nvramvar.h)54
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_storevar.h111
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_subr.c1271
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_value.c1313
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_value.h224
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c1032
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c883
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_valuevar.h100
-rw-r--r--sys/dev/bhnd/nvram/bhnd_sprom.c98
-rw-r--r--sys/dev/bhnd/nvram/bhnd_sprom_parser.c756
-rw-r--r--sys/dev/bhnd/nvram/bhnd_sprom_parservar.h64
-rw-r--r--sys/dev/bhnd/nvram/bhnd_spromvar.h9
-rw-r--r--sys/dev/bhnd/nvram/nvram_map3877
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>
+}