Page MenuHomeFreeBSD

D8645.id22521.diff
No OneTemporary

D8645.id22521.diff

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: sys/conf/files
===================================================================
--- sys/conf/files
+++ sys/conf/files
@@ -1220,14 +1220,22 @@
dev/bhnd/cores/pmu/bhnd_pmu_core.c optional bhnd
dev/bhnd/cores/pmu/bhnd_pmu_if.m optional bhnd
dev/bhnd/cores/pmu/bhnd_pmu_subr.c optional bhnd
-dev/bhnd/nvram/bhnd_nvram.c optional bhnd
-dev/bhnd/nvram/bhnd_nvram_common.c optional bhnd
-dev/bhnd/nvram/bhnd_nvram_cfe.c optional bhnd siba_nexus cfe | \
- bhnd bcma_nexus cfe
+dev/bhnd/nvram/bhnd_nvram_data.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_data_bcm.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_data_btxt.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_data_sprom.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_data_tlv.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_if.m optional bhnd
-dev/bhnd/nvram/bhnd_nvram_parser.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_io.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_iobuf.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_iores.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_store.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_subr.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_value.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_value_fmts.c optional bhnd
+dev/bhnd/nvram/bhnd_nvram_value_prf.c optional bhnd
dev/bhnd/nvram/bhnd_sprom.c optional bhnd
-dev/bhnd/nvram/bhnd_sprom_parser.c optional bhnd
dev/bhnd/siba/siba.c optional siba bhnd
dev/bhnd/siba/siba_bhndb.c optional siba bhnd bhndb
dev/bhnd/siba/siba_erom.c optional siba bhnd
Index: sys/dev/bhnd/bhnd_subr.c
===================================================================
--- sys/dev/bhnd/bhnd_subr.c
+++ sys/dev/bhnd/bhnd_subr.c
@@ -1057,7 +1057,8 @@
int error;
larg = len;
- error = bhnd_nvram_getvar(dev, name, buf, &larg, BHND_NVRAM_TYPE_CSTR);
+ error = bhnd_nvram_getvar(dev, name, buf, &larg,
+ BHND_NVRAM_TYPE_STRING);
if (rlen != NULL)
*rlen = larg;
Index: sys/dev/bhnd/nvram/bhnd_nvram.h
===================================================================
--- sys/dev/bhnd/nvram/bhnd_nvram.h
+++ sys/dev/bhnd/nvram/bhnd_nvram.h
@@ -32,11 +32,17 @@
#ifndef _BHND_NVRAM_BHND_NVRAM_H_
#define _BHND_NVRAM_BHND_NVRAM_H_
+#ifdef _KERNEL
+#include <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 @@
*/
} bhnd_nvram_src;
-/** Supported NVRAM formats. */
+/**
+ * NVRAM data types.
+ *
+ * @internal
+ *
+ * All primitive (non-array) constants should be representable as a 4-bit
+ * integer (e.g. 0-15) to support SPROM_OPCODE_TYPE_IMM encoding as used by
+ * nvram_map_gen.awk.
+ */
typedef enum {
- BHND_NVRAM_FMT_BCM = 0, /**< Broadcom NUL-delimited key=value pairs */
- BHND_NVRAM_FMT_TLV = 1, /**< CFE TLV encoding, as used on WGT634U */
- BHND_NVRAM_FMT_BTXT = 2, /**< Broadcom board text file. This is used
- to provide external NVRAM data for some
- fullmac WiFi devices. */
- BHND_NVRAM_FMT_SPROM = 3, /**< SPROM/OTP-specific encoding used by
- Broadcom network adapters */
- BHND_NVRAM_FMT_CIS = 4, /**< A mostly CIS-compatible encoding used
- on some Broadcom network adapters */
- BHND_NVRAM_FMT_UNKNOWN = 5 /**< Unknown or unrecognized format */
-} bhnd_nvram_format;
-
-
-/** bhnd_nvram_type bit flags */
-enum {
- BHND_NVRAM_TF_SIGNED = (1<<7),
-};
+ BHND_NVRAM_TYPE_UINT8 = 0, /**< unsigned 8-bit integer */
+ BHND_NVRAM_TYPE_UINT16 = 1, /**< unsigned 16-bit integer */
+ BHND_NVRAM_TYPE_UINT32 = 2, /**< unsigned 32-bit integer */
+ BHND_NVRAM_TYPE_UINT64 = 3, /**< signed 64-bit integer */
+ BHND_NVRAM_TYPE_INT8 = 4, /**< signed 8-bit integer */
+ BHND_NVRAM_TYPE_INT16 = 5, /**< signed 16-bit integer */
+ BHND_NVRAM_TYPE_INT32 = 6, /**< signed 32-bit integer */
+ BHND_NVRAM_TYPE_INT64 = 7, /**< signed 64-bit integer */
+ BHND_NVRAM_TYPE_CHAR = 8, /**< ASCII/UTF-8 character */
+ BHND_NVRAM_TYPE_STRING = 9, /**< ASCII/UTF-8 NUL-terminated
+ string */
-#define BHND_NVRAM_TYPE_ID_MASK 0xF
-#define BHND_NVRAM_TYPE_FLAGS_MASK 0x70
+ /* 10-15 reserved for primitive (non-array) types */
-#define BHND_NVRAM_TYPE_ID(_id, _flags) \
- (((_id) & BHND_NVRAM_TYPE_ID_MASK) | \
- ((_flags) & BHND_NVRAM_TYPE_FLAGS_MASK))
-
-/** Supported NVRAM data types */
-typedef enum {
- BHND_NVRAM_TYPE_UINT8 = BHND_NVRAM_TYPE_ID(0, 0), /**< unsigned 8-bit integer */
- BHND_NVRAM_TYPE_UINT16 = BHND_NVRAM_TYPE_ID(1, 0), /**< unsigned 16-bit integer */
- BHND_NVRAM_TYPE_UINT32 = BHND_NVRAM_TYPE_ID(2, 0), /**< unsigned 32-bit integer */
- BHND_NVRAM_TYPE_INT8 = BHND_NVRAM_TYPE_ID(4, BHND_NVRAM_TF_SIGNED), /**< signed 8-bit integer */
- BHND_NVRAM_TYPE_INT16 = BHND_NVRAM_TYPE_ID(5, BHND_NVRAM_TF_SIGNED), /**< signed 16-bit integer */
- BHND_NVRAM_TYPE_INT32 = BHND_NVRAM_TYPE_ID(6, BHND_NVRAM_TF_SIGNED), /**< signed 32-bit integer */
- BHND_NVRAM_TYPE_CHAR = BHND_NVRAM_TYPE_ID(7, BHND_NVRAM_TF_SIGNED), /**< ASCII character */
- BHND_NVRAM_TYPE_CSTR = BHND_NVRAM_TYPE_ID(8, 0), /**< NUL-terminated C string */
+ BHND_NVRAM_TYPE_UINT8_ARRAY = 16, /**< array of uint8 integers */
+ BHND_NVRAM_TYPE_UINT16_ARRAY = 17, /**< array of uint16 integers */
+ BHND_NVRAM_TYPE_UINT32_ARRAY = 18, /**< array of uint32 integers */
+ BHND_NVRAM_TYPE_UINT64_ARRAY = 19, /**< array of uint64 integers */
+ BHND_NVRAM_TYPE_INT8_ARRAY = 20, /**< array of int8 integers */
+ BHND_NVRAM_TYPE_INT16_ARRAY = 21, /**< array of int16 integers */
+ BHND_NVRAM_TYPE_INT32_ARRAY = 22, /**< array of int32 integers */
+ BHND_NVRAM_TYPE_INT64_ARRAY = 23, /**< array of int64 integers */
+ BHND_NVRAM_TYPE_CHAR_ARRAY = 24, /**< array of ASCII/UTF-8
+ characters */
+ BHND_NVRAM_TYPE_STRING_ARRAY = 25, /**< array of ASCII/UTF-8
+ NUL-terminated strings */
} bhnd_nvram_type;
-#undef BHND_NVRAM_TYPE_ID_MASK
-#undef BHND_NVRAM_TYPE_FLAGS_MASK
-#undef BHND_NVRAM_TYPE_ID
+const char *bhnd_nvram_string_array_next(const char *inp, size_t ilen,
+ const char *prev);
-#define BHND_NVRAM_SIGNED_TYPE(_type) \
- (((_type) & BHND_NVRAM_TF_SIGNED) == BHND_NVRAM_TF_SIGNED)
+bool bhnd_nvram_is_signed_type(bhnd_nvram_type type);
+bool bhnd_nvram_is_unsigned_type(bhnd_nvram_type type);
+bool bhnd_nvram_is_int_type(bhnd_nvram_type type);
+bool bhnd_nvram_is_array_type(bhnd_nvram_type type);
+bhnd_nvram_type bhnd_nvram_base_type(bhnd_nvram_type type);
+const char *bhnd_nvram_type_name(bhnd_nvram_type type);
#endif /* _BHND_NVRAM_BHND_NVRAM_H_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram.c
===================================================================
--- sys/dev/bhnd/nvram/bhnd_nvram.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/*-
- * Copyright (c) 2016 Landon Fuller <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));
Index: sys/dev/bhnd/nvram/bhnd_nvram_cfe.c
===================================================================
--- sys/dev/bhnd/nvram/bhnd_nvram_cfe.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/*-
- * Copyright (c) 2016 Landon Fuller <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);
Index: sys/dev/bhnd/nvram/bhnd_nvram_common.h
===================================================================
--- sys/dev/bhnd/nvram/bhnd_nvram_common.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/*-
- * Copyright (c) 2015-2016 Landon Fuller <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_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_common.c
===================================================================
--- sys/dev/bhnd/nvram/bhnd_nvram_common.c
+++ /dev/null
@@ -1,661 +0,0 @@
-/*-
- * Copyright (c) 2016 Landon Fuller <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);
-}
Index: sys/dev/bhnd/nvram/bhnd_nvram_data.h
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_data.h
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_data.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_data.c
@@ -0,0 +1,527 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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));
+}
Index: sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c
@@ -0,0 +1,748 @@
+/*-
+ * Copyright (c) 2016 Landon Fuller <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]);
+}
Index: sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c
@@ -0,0 +1,380 @@
+/*-
+ * Copyright (c) 2016 Landon Fuller <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);
+}
Index: sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h
===================================================================
--- sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h
+++ sys/dev/bhnd/nvram/bhnd_nvram_data_bcmreg.h
@@ -25,51 +25,49 @@
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
- *
+ *
* $FreeBSD$
*/
-#ifndef _BHND_NVRAM_BHND_NVRAM_PARSERREG_H_
-#define _BHND_NVRAM_BHND_NVRAM_PARSERREG_H_
-
-
+#ifndef _BHND_NVRAM_BHND_NVRAM_BCMREG_H_
+#define _BHND_NVRAM_BHND_NVRAM_BCMREG_H_
-#define NVRAM_GET_BITS(_value, _field) \
- ((_value & _field ## _MASK) >> _field ## _SHIFT)
+#define BCM_NVRAM_GET_BITS(_value, _field) \
+ ((_value & _field ## _MASK) >> _field ## _SHIFT)
-/* NVRAM header fields */
-#define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */
-#define NVRAM_VERSION 1
+/* BCM NVRAM header fields */
+#define BCM_NVRAM_MAGIC 0x48534C46 /* 'FLSH' */
+#define BCM_NVRAM_VERSION 1
-#define NVRAM_CRC_SKIP 9 /* skip magic, size, and crc8 */
+#define BCM_NVRAM_CRC_SKIP 9 /* skip magic, size, and crc8 */
-#define NVRAM_CFG0_CRC_MASK 0x000000FF
-#define NVRAM_CFG0_CRC_SHIFT 0
-#define NVRAM_CFG0_VER_MASK 0x0000FF00
-#define NVRAM_CFG0_VER_SHIFT 8
-#define NVRAM_CFG0_SDRAM_INIT_MASK 0xFFFF0000
-#define NVRAM_CFG0_SDRAM_INIT_SHIFT 16
-#define NVRAM_CFG0_SDRAM_INIT_VAR "sdram_init"
-#define NVRAM_CFG0_SDRAM_INIT_FMT "0x%04x"
+#define BCM_NVRAM_CFG0_CRC_MASK 0x000000FF
+#define BCM_NVRAM_CFG0_CRC_SHIFT 0
+#define BCM_NVRAM_CFG0_VER_MASK 0x0000FF00
+#define BCM_NVRAM_CFG0_VER_SHIFT 8
-#define NVRAM_CFG1_SDRAM_CFG_MASK 0x0000FFFF
-#define NVRAM_CFG1_SDRAM_CFG_SHIFT 0
-#define NVRAM_CFG1_SDRAM_CFG_VAR "sdram_config"
-#define NVRAM_CFG1_SDRAM_CFG_FMT "0x%04x"
+#define BCM_NVRAM_CFG0_SDRAM_INIT_FIELD cfg0
+#define BCM_NVRAM_CFG0_SDRAM_INIT_MASK 0xFFFF0000
+#define BCM_NVRAM_CFG0_SDRAM_INIT_SHIFT 16
+#define BCM_NVRAM_CFG0_SDRAM_INIT_VAR "sdram_init"
+#define BCM_NVRAM_CFG0_SDRAM_INIT_FMT "0x%04x"
-#define NVRAM_CFG1_SDRAM_REFRESH_MASK 0xFFFF0000
-#define NVRAM_CFG1_SDRAM_REFRESH_SHIFT 16
-#define NVRAM_CFG1_SDRAM_REFRESH_VAR "sdram_refresh"
-#define NVRAM_CFG1_SDRAM_REFRESH_FMT "0x%04x"
+#define BCM_NVRAM_CFG1_SDRAM_CFG_FIELD cfg1
+#define BCM_NVRAM_CFG1_SDRAM_CFG_MASK 0x0000FFFF
+#define BCM_NVRAM_CFG1_SDRAM_CFG_SHIFT 0
+#define BCM_NVRAM_CFG1_SDRAM_CFG_VAR "sdram_config"
+#define BCM_NVRAM_CFG1_SDRAM_CFG_FMT "0x%04x"
-#define NVRAM_SDRAM_NCDL_MASK UINT32_MAX
-#define NVRAM_SDRAM_NCDL_SHIFT 0
-#define NVRAM_SDRAM_NCDL_VAR "sdram_ncdl"
-#define NVRAM_SDRAM_NCDL_FMT "0x%08x"
+#define BCM_NVRAM_CFG1_SDRAM_REFRESH_FIELD cfg1
+#define BCM_NVRAM_CFG1_SDRAM_REFRESH_MASK 0xFFFF0000
+#define BCM_NVRAM_CFG1_SDRAM_REFRESH_SHIFT 16
+#define BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR "sdram_refresh"
+#define BCM_NVRAM_CFG1_SDRAM_REFRESH_FMT "0x%04x"
-/* WGT634U-specific TLV encoding */
-#define NVRAM_TLV_TF_U8_LEN 0x01 /**< type has 8-bit length */
-#define NVRAM_TLV_TYPE_END 0x00 /**< end of table */
-#define NVRAM_TLV_TYPE_ENV 0x01 /**< variable record */
+#define BCM_NVRAM_SDRAM_NCDL_FIELD sdram_ncdl
+#define BCM_NVRAM_SDRAM_NCDL_MASK UINT32_MAX
+#define BCM_NVRAM_SDRAM_NCDL_SHIFT 0
+#define BCM_NVRAM_SDRAM_NCDL_VAR "sdram_ncdl"
+#define BCM_NVRAM_SDRAM_NCDL_FMT "0x%08x"
-#endif /* _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_ */
+#endif /* _BHND_NVRAM_BHND_NVRAM_BCMREG_H_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h
===================================================================
--- sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h
+++ sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2016 Landon Fuller <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_SPROM_PARSERVAR_H_
-#define _BHND_NVRAM_SPROM_PARSERVAR_H_
-
-#include "bhnd_sprom_parser.h"
-
-#define SPROM_SZ_R1_3 128 /**< SPROM image size (rev 1-3) */
-#define SPROM_SZ_R4_8_9 440 /**< SPROM image size (rev 4, 8-9) */
-#define SPROM_SZ_R10 460 /**< SPROM image size (rev 10) */
-#define SPROM_SZ_R11 468 /**< SPROM image size (rev 11) */
-
-/** Maximum supported SPROM image size */
-#define SPROM_SZ_MAX SPROM_SZ_R11
+#ifndef _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_
+#define _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_
-#define SPROM_SIG_NONE 0x0
-#define SPROM_SIG_NONE_OFF 0x0
-
-/** SPROM signature (rev 4) */
-#define SPROM_SIG_R4 0x5372
-#define SPROM_SIG_R4_OFF 64 /**< SPROM signature offset (rev 4) */
-
-/** SPROM signature (rev 8, 9) */
-#define SPROM_SIG_R8_9 SPROM_SIG_R4
-#define SPROM_SIG_R8_9_OFF 128 /**< SPROM signature offset (rev 8-9) */
+/**
+ * BCM NVRAM header value data.
+ */
+union bhnd_nvram_bcm_hvar_value {
+ uint16_t u16;
+ uint32_t u32;
+};
-/** SPROM signature (rev 10) */
-#define SPROM_SIG_R10 SPROM_SIG_R4
-#define SPROM_SIG_R10_OFF 438 /**< SPROM signature offset (rev 10) */
+/**
+ * Internal representation of BCM NVRAM values that mirror (and must be
+ * vended as) NVRAM variables.
+ */
+struct bhnd_nvram_bcm_hvar {
+ const char *name; /**< variable name */
+ bhnd_nvram_type type; /**< value type */
+ size_t nelem; /**< value element count */
+ size_t len; /**< value length */
+ const char *envp; /**< Pointer to the NVRAM variable mirroring
+ this header value, or NULL. */
+ bool stale; /**< header value does not match
+ mirrored NVRAM value */
-/** SPROM signature (rev 11) */
-#define SPROM_SIG_R11 0x0634
-#define SPROM_SIG_R11_OFF 128 /**< SPROM signature offset (rev 11) */
+ /** variable data */
+ union bhnd_nvram_bcm_hvar_value value;
+};
+
+/** BCM NVRAM header */
+struct bhnd_nvram_bcmhdr {
+ uint32_t magic;
+ uint32_t size;
+ uint32_t cfg0; /**< crc:8, version:8, sdram_init:16 */
+ uint32_t cfg1; /**< sdram_config:16, sdram_refresh:16 */
+ uint32_t sdram_ncdl; /**< sdram_ncdl */
+} __packed;
-#endif /* _BHND_NVRAM_SPROM_PARSERVAR_H_ */
+#endif /* _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c
@@ -0,0 +1,586 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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);
+}
Index: sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c
@@ -0,0 +1,1988 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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);
+}
Index: sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h
@@ -0,0 +1,150 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c
@@ -0,0 +1,662 @@
+/*-
+ * Copyright (c) 2016 Landon Fuller <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);
+}
Index: sys/dev/bhnd/nvram/bhnd_nvram_data_tlvreg.h
===================================================================
--- sys/dev/bhnd/nvram/bhnd_nvram_data_tlvreg.h
+++ sys/dev/bhnd/nvram/bhnd_nvram_data_tlvreg.h
@@ -29,47 +29,12 @@
* $FreeBSD$
*/
-#ifndef _BHND_NVRAM_BHND_NVRAM_PARSERREG_H_
-#define _BHND_NVRAM_BHND_NVRAM_PARSERREG_H_
-
-
-
-#define NVRAM_GET_BITS(_value, _field) \
- ((_value & _field ## _MASK) >> _field ## _SHIFT)
-
-/* NVRAM header fields */
-#define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */
-#define NVRAM_VERSION 1
-
-#define NVRAM_CRC_SKIP 9 /* skip magic, size, and crc8 */
-
-#define NVRAM_CFG0_CRC_MASK 0x000000FF
-#define NVRAM_CFG0_CRC_SHIFT 0
-#define NVRAM_CFG0_VER_MASK 0x0000FF00
-#define NVRAM_CFG0_VER_SHIFT 8
-#define NVRAM_CFG0_SDRAM_INIT_MASK 0xFFFF0000
-#define NVRAM_CFG0_SDRAM_INIT_SHIFT 16
-#define NVRAM_CFG0_SDRAM_INIT_VAR "sdram_init"
-#define NVRAM_CFG0_SDRAM_INIT_FMT "0x%04x"
-
-#define NVRAM_CFG1_SDRAM_CFG_MASK 0x0000FFFF
-#define NVRAM_CFG1_SDRAM_CFG_SHIFT 0
-#define NVRAM_CFG1_SDRAM_CFG_VAR "sdram_config"
-#define NVRAM_CFG1_SDRAM_CFG_FMT "0x%04x"
-
-#define NVRAM_CFG1_SDRAM_REFRESH_MASK 0xFFFF0000
-#define NVRAM_CFG1_SDRAM_REFRESH_SHIFT 16
-#define NVRAM_CFG1_SDRAM_REFRESH_VAR "sdram_refresh"
-#define NVRAM_CFG1_SDRAM_REFRESH_FMT "0x%04x"
-
-#define NVRAM_SDRAM_NCDL_MASK UINT32_MAX
-#define NVRAM_SDRAM_NCDL_SHIFT 0
-#define NVRAM_SDRAM_NCDL_VAR "sdram_ncdl"
-#define NVRAM_SDRAM_NCDL_FMT "0x%08x"
+#ifndef _BHND_NVRAM_BHND_NVRAM_TLVREG_H_
+#define _BHND_NVRAM_BHND_NVRAM_TLVREG_H_
/* WGT634U-specific TLV encoding */
#define NVRAM_TLV_TF_U8_LEN 0x01 /**< type has 8-bit length */
#define NVRAM_TLV_TYPE_END 0x00 /**< end of table */
#define NVRAM_TLV_TYPE_ENV 0x01 /**< variable record */
-#endif /* _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_ */
+#endif /* _BHND_NVRAM_BHND_NVRAM_TLVREG_H_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_datavar.h
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_datavar.h
@@ -0,0 +1,188 @@
+/*-
+ * Copyright (c) 2016 Landon Fuller <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_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_io.h
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_io.h
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 2016 Landon Fuller <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,
+ size_t offset, size_t size, u_int bus_width);
+#endif /* _KERNEL */
+
+size_t bhnd_nvram_io_getsize(struct bhnd_nvram_io *io);
+int bhnd_nvram_io_setsize(struct bhnd_nvram_io *io,
+ size_t size);
+
+int bhnd_nvram_io_read(struct bhnd_nvram_io *io,
+ size_t offset, void *buffer, size_t nbytes);
+int bhnd_nvram_io_read_ptr(struct bhnd_nvram_io *io,
+ size_t offset, const void **ptr, size_t nbytes,
+ size_t *navail);
+
+int bhnd_nvram_io_write(struct bhnd_nvram_io *io,
+ size_t offset, void *buffer, size_t nbytes);
+int bhnd_nvram_io_write_ptr(struct bhnd_nvram_io *io,
+ size_t offset, void **ptr, size_t nbytes,
+ size_t *navail);
+
+void bhnd_nvram_io_free(struct bhnd_nvram_io *io);
+
+#endif /* _BHND_NVRAM_BHND_NVRAM_IO_H_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_io.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_io.c
@@ -0,0 +1,205 @@
+/*-
+ * Copyright (c) 2016 Landon Fuller <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));
+}
+
Index: sys/dev/bhnd/nvram/bhnd_nvram_iobuf.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_iobuf.c
@@ -0,0 +1,341 @@
+/*-
+ * Copyright (c) 2016 Landon Fuller <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);
+}
Index: sys/dev/bhnd/nvram/bhnd_nvram_iores.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_iores.c
@@ -0,0 +1,302 @@
+/*-
+ * Copyright (c) 2016 Landon Fuller <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);
+}
Index: sys/dev/bhnd/nvram/bhnd_nvram_iovar.h
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_iovar.h
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 2016 Landon Fuller <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_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_parser.h
===================================================================
--- sys/dev/bhnd/nvram/bhnd_nvram_parser.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*-
- * Copyright (c) 2015-2016 Landon Fuller <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_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_parser.c
===================================================================
--- sys/dev/bhnd/nvram/bhnd_nvram_parser.c
+++ /dev/null
@@ -1,1578 +0,0 @@
-/*-
- * Copyright (c) 2015-2016 Landon Fuller <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);
-}
Index: sys/dev/bhnd/nvram/bhnd_nvram_parservar.h
===================================================================
--- sys/dev/bhnd/nvram/bhnd_nvram_parservar.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*-
- * Copyright (c) 2015-2016 Landon Fuller <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_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_private.h
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_private.h
@@ -0,0 +1,402 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_store.h
===================================================================
--- sys/dev/bhnd/nvram/bhnd_nvram_store.h
+++ sys/dev/bhnd/nvram/bhnd_nvram_store.h
@@ -29,40 +29,40 @@
* $FreeBSD$
*/
-#ifndef _BHND_NVRAM_BHND_NVRAMVAR_H_
-#define _BHND_NVRAM_BHND_NVRAMVAR_H_
+#ifndef _BHND_NVRAM_BHND_NVRAM_STORE_H_
+#define _BHND_NVRAM_BHND_NVRAM_STORE_H_
+#ifdef _KERNEL
#include <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_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_store.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_store.c
@@ -0,0 +1,572 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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);
+}
Index: sys/dev/bhnd/nvram/bhnd_nvram_storevar.h
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_storevar.h
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_subr.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_subr.c
@@ -0,0 +1,1271 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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);
+}
Index: sys/dev/bhnd/nvram/bhnd_nvram_value.h
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_value.h
@@ -0,0 +1,224 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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_ */
Index: sys/dev/bhnd/nvram/bhnd_nvram_value.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_value.c
@@ -0,0 +1,1313 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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);
+}
Index: sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c
@@ -0,0 +1,1032 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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);
+}
Index: sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c
@@ -0,0 +1,883 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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);
+}
Index: sys/dev/bhnd/nvram/bhnd_nvram_valuevar.h
===================================================================
--- /dev/null
+++ sys/dev/bhnd/nvram/bhnd_nvram_valuevar.h
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <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_ */
Index: sys/dev/bhnd/nvram/bhnd_sprom.c
===================================================================
--- sys/dev/bhnd/nvram/bhnd_sprom.c
+++ sys/dev/bhnd/nvram/bhnd_sprom.c
@@ -52,15 +52,9 @@
#include "bhnd_nvram_if.h"
-#include "bhnd_spromvar.h"
+#include "bhnd_nvram_io.h"
-#define SPROM_LOCK_INIT(sc) \
- mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
- "BHND SPROM lock", MTX_DEF)
-#define SPROM_LOCK(sc) mtx_lock(&(sc)->mtx)
-#define SPROM_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
-#define SPROM_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what)
-#define SPROM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
+#include "bhnd_spromvar.h"
/**
* Default bhnd sprom driver implementation of DEVICE_PROBE().
@@ -68,9 +62,6 @@
int
bhnd_sprom_probe(device_t dev)
{
- /* Quiet by default */
- if (!bootverbose)
- device_quiet(dev);
device_set_desc(dev, "SPROM/OTP");
/* Refuse wildcard attachments */
@@ -100,32 +91,62 @@
bhnd_sprom_attach(device_t dev, bus_size_t offset)
{
struct bhnd_sprom_softc *sc;
- int error;
-
+ struct bhnd_nvram_io *io;
+ struct bhnd_resource *r;
+ bus_size_t r_size, sprom_size;
+ int rid;
+ int error;
+
sc = device_get_softc(dev);
sc->dev = dev;
+ io = NULL;
+
/* Allocate SPROM resource */
- sc->sprom_rid = 0;
- sc->sprom_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY,
- &sc->sprom_rid, RF_ACTIVE);
- if (sc->sprom_res == NULL) {
+ rid = 0;
+ r = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (r == NULL) {
device_printf(dev, "failed to allocate resources\n");
return (ENXIO);
}
- /* Initialize SPROM shadow */
- if ((error = bhnd_sprom_init(&sc->shadow, sc->sprom_res, offset)))
+ /* Determine SPROM size */
+ r_size = rman_get_size(r->res);
+ if (r_size <= offset || (r_size - offset) > BUS_SPACE_MAXSIZE) {
+ device_printf(dev, "invalid sprom offset\n");
+ error = ENXIO;
goto failed;
+ }
- /* Initialize mutex */
- SPROM_LOCK_INIT(sc);
+ sprom_size = r_size - offset;
+
+ /* Allocate an I/O context for the SPROM parser. All SPROM reads
+ * must be 16-bit aligned */
+ io = bhnd_nvram_iores_new(r, offset, sprom_size, sizeof(uint16_t));
+ if (io == NULL) {
+ error = ENXIO;
+ goto failed;
+ }
+
+ /* Initialize NVRAM data store */
+ error = bhnd_nvram_store_parse_new(&sc->store, io,
+ &bhnd_nvram_sprom_class);
+ if (error)
+ goto failed;
+
+ /* Clean up our temporary I/O context and its backing resource */
+ bhnd_nvram_io_free(io);
+ bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
return (0);
-
+
failed:
- bhnd_release_resource(dev, SYS_RES_MEMORY, sc->sprom_rid,
- sc->sprom_res);
+ /* Clean up I/O context before releasing its backing resource */
+ if (io != NULL)
+ bhnd_nvram_io_free(io);
+
+ bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
+
return (error);
}
@@ -157,10 +178,7 @@
sc = device_get_softc(dev);
- bhnd_release_resource(dev, SYS_RES_MEMORY, sc->sprom_rid,
- sc->sprom_res);
- bhnd_sprom_fini(&sc->shadow);
- SPROM_LOCK_DESTROY(sc);
+ bhnd_nvram_store_free(sc->store);
return (0);
}
@@ -172,16 +190,9 @@
bhnd_sprom_getvar_method(device_t dev, const char *name, void *buf, size_t *len,
bhnd_nvram_type type)
{
- struct bhnd_sprom_softc *sc;
- int error;
-
- sc = device_get_softc(dev);
-
- SPROM_LOCK(sc);
- error = bhnd_sprom_getvar(&sc->shadow, name, buf, len, type);
- SPROM_UNLOCK(sc);
+ struct bhnd_sprom_softc *sc = device_get_softc(dev);
- return (error);
+ return (bhnd_nvram_store_getvar(sc->store, name, buf, len, type));
}
/**
@@ -191,16 +202,9 @@
bhnd_sprom_setvar_method(device_t dev, const char *name, const void *buf,
size_t len, bhnd_nvram_type type)
{
- struct bhnd_sprom_softc *sc;
- int error;
-
- sc = device_get_softc(dev);
-
- SPROM_LOCK(sc);
- error = bhnd_sprom_setvar(&sc->shadow, name, buf, len, type);
- SPROM_UNLOCK(sc);
+ struct bhnd_sprom_softc *sc = device_get_softc(dev);
- return (error);
+ return (bhnd_nvram_store_setvar(sc->store, name, buf, len, type));
}
static device_method_t bhnd_sprom_methods[] = {
@@ -218,5 +222,5 @@
DEVMETHOD_END
};
-DEFINE_CLASS_0(bhnd_nvram, bhnd_sprom_driver, bhnd_sprom_methods, sizeof(struct bhnd_sprom_softc));
+DEFINE_CLASS_0(bhnd_nvram_store, bhnd_sprom_driver, bhnd_sprom_methods, sizeof(struct bhnd_sprom_softc));
MODULE_VERSION(bhnd_sprom, 1);
Index: sys/dev/bhnd/nvram/bhnd_sprom_parser.c
===================================================================
--- sys/dev/bhnd/nvram/bhnd_sprom_parser.c
+++ /dev/null
@@ -1,756 +0,0 @@
-/*-
- * Copyright (c) 2015-2016 Landon Fuller <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 (',');
- }
-}
Index: sys/dev/bhnd/nvram/bhnd_spromvar.h
===================================================================
--- sys/dev/bhnd/nvram/bhnd_spromvar.h
+++ sys/dev/bhnd/nvram/bhnd_spromvar.h
@@ -34,7 +34,7 @@
#include <dev/bhnd/bhnd.h>
-#include "bhnd_sprom_parser.h"
+#include "bhnd_nvram_store.h"
DECLARE_CLASS(bhnd_sprom_driver);
@@ -49,11 +49,8 @@
* softc structures.
*/
struct bhnd_sprom_softc {
- device_t dev;
- struct bhnd_resource *sprom_res; /**< SPROM resource */
- int sprom_rid; /**< SPROM RID */
- struct bhnd_sprom shadow; /**< SPROM shadow */
- struct mtx mtx; /**< SPROM shadow mutex */
+ device_t dev;
+ struct bhnd_nvram_store *store; /**< nvram store */
};
#endif /* _BHND_NVRAM_BHND_SPROMVAR_H_ */
Index: sys/dev/bhnd/nvram/nvram_map
===================================================================
--- sys/dev/bhnd/nvram/nvram_map
+++ sys/dev/bhnd/nvram/nvram_map
@@ -21,1445 +21,3054 @@
# $FreeBSD$
#
-# NVRAM variable definitions and revision-specific SPROM offsets.
+# NVRAM variable and SPROM layout descriptions.
#
-# Processed by nvram_map_gen.awk to produce bhnd_nvram_map.h
+# Process with nvram_map_gen.awk to produce bhnd_nvram_map.h and
+# bhnd_nvram_map_data.h
#
# NOTE: file was originally generated automatically by using libclang
# to analyze and extract format information and descriptions from Broadcom's
# available ISC-licensed CIS and SROM code and associated headers.
#
-# Board Info
-#
-
-u16 boardvendor {} # PCI vendor ID (SoC NVRAM-only)
-u16 subvid { srom >= 2 0x6 } # PCI subvendor ID
-u16 devid { srom >= 8 0x60 } # PCI device ID
-
-u32 boardflags {
- srom 1 u16 0x72
- srom 2 u16 0x72 | u16 0x38 (<<16)
- srom 3 u16 0x72 | u16 0x7A (<<16)
- srom 4 0x44
- srom 5-7 0x4A
- srom >= 8 0x84
-}
-u32 boardflags2 {
- srom 4 0x48
- srom 5-7 0x4E
- srom >= 8 0x88
-}
-u32 boardflags3 {
- srom >= 11 0x8C
-}
-
-# Board serial number, independent of mac addr
-u16 boardnum {
- srom 1-2 0x4C
- srom 3 0x4E
- srom 4 0x50
- srom 5-7 0x56
- srom 8-10 0x90
- srom >= 11 0x94
-}
-
-# Board revision
-u16 boardrev {
- srom 1-3 u8 0x5D
- srom 4-7 0x42
- srom >= 8 0x82
-}
-
-# Board type
-u16 boardtype {
- srom >= 2 0x4
-}
-
-# SROM revision
-u8 sromrev {
- srom 1-3 0x74
- srom 4-9 0x1B6
- srom 10 0x1CA
- srom 11 0x1D2
-}
-
-
-# PMU Info
-#
-
-# PMU min resource mask (embedded-only).
-u32 rmin {
- sfmt decimal
-}
-
-# PMU min resource max (embedded-only).
-u32 rmax {
- sfmt decimal
-}
-
-
-# Antennas available
-u8 aa2g {
- srom 1-3 0x5C (&0x30, >>4)
- srom 4-7 0x5D
- srom 8-10 0x9D
- srom >= 11 0xA1
-}
-u8 aa5g {
- srom 1-3 0x5C (&0xC0, >>6)
- srom 4-7 0x5C
- srom 8-10 0x9C
- srom >= 11 0xA0
-}
-
-# ACPHY PA trimming parameters: 40
-u16[12] pa5gbw40a0 {
- srom >= 11 0x110
-}
-
-# ACPHY PA trimming parameters: 80
-u16[12] pa5gbw80a0 {
- srom >= 11 0x138
-}
-
-# ACPHY PA trimming parameters: 40/80
-u16[12] pa5gbw4080a0 {
- srom >= 11 0x138
-}
-u16[12] pa5gbw4080a1 {
- srom >= 11 u16 0xB6, u16 0xBC, u16 0xCE, u16 0xD4, u16[8] 0x128
-}
-
-# ACPHY PA trimming parameters: CCK
-u16[3] pa2gccka0 {
- srom >= 11 0x102
-}
-
-# ACPHY Power-per-rate 2gpo
-u16 dot11agofdmhrbw202gpo {
- srom >= 11 0x15C
-}
-u16 ofdmlrbw202gpo {
- srom >= 11 0x15E
-}
-
-# ACPHY Power-per-rate 5gpo
-u32 mcsbw805glpo {
- srom >= 11 0x168
-}
-u32 mcsbw805gmpo {
- srom >= 11 0x178
-}
-u32 mcsbw805ghpo {
- srom >= 11 0x188
-}
-u16 mcslr5glpo {
- srom >= 11 0x190 (&0xFFF)
-}
-u16 mcslr5gmpo {
- srom >= 11 0x192
-}
-u16 mcslr5ghpo {
- srom >= 11 0x194
-}
-
-# ACPHY Power-per-rate sbpo
-u16 sb20in40hrpo {
- srom >= 11 0x196
-}
-u16 sb20in80and160hr5glpo {
- srom >= 11 0x198
-}
-u16 sb40and80hr5glpo {
- srom >= 11 0x19A
-}
-u16 sb20in80and160hr5gmpo {
- srom >= 11 0x19C
-}
-u16 sb40and80hr5gmpo {
- srom >= 11 0x19E
-}
-u16 sb20in80and160hr5ghpo {
- srom >= 11 0x1A0
-}
-u16 sb40and80hr5ghpo {
- srom >= 11 0x1A2
-}
-u16 sb20in40lrpo {
- srom >= 11 0x1A4
-}
-u16 sb20in80and160lr5glpo {
- srom >= 11 0x1A6
-}
-u16 sb40and80lr5glpo {
- srom >= 11 0x1A8
-}
-u16 sb20in80and160lr5gmpo {
- srom >= 11 0x1AA
-}
-u16 sb40and80lr5gmpo {
- srom >= 11 0x1AC
-}
-u16 sb20in80and160lr5ghpo {
- srom >= 11 0x1AE
-}
-u16 sb40and80lr5ghpo {
- srom >= 11 0x1B0
-}
-u16 dot11agduphrpo {
- srom >= 11 0x1B2
-}
-u16 dot11agduplrpo {
- srom >= 11 0x1B4
-}
-
-# Antenna gain
-u8 ag0 {
- srom 1-3 0x75
- srom 4-7 0x5F
- srom 8-10 0x9F
-}
-u8 ag1 {
- srom 1-3 0x74
- srom 4-7 0x5E
- srom 8-10 0x9E
-}
-u8 ag2 {
- srom 4-7 0x61
- srom 8-10 0xA1
-}
-u8 ag3 {
- srom 4-7 0x60
- srom 8-10 0xA0
-}
-
-u8 agbg0 {
- srom >= 11 0xA2
-}
-u8 agbg1 {
- srom >= 11 0xA3
-}
-u8 agbg2 {
- srom >= 11 0xA4
-}
-u8 aga0 {
- srom >= 11 0xA5
-}
-u8 aga1 {
- srom >= 11 0xA6
-}
-u8 aga2 {
- srom >= 11 0xA7
-}
-
-# Default country code (sromrev == 1)
-u8 cc {
- srom 1 0x5C (&0xF)
-}
-
-# 2 bytes each
-# CCK Power offsets for 20 MHz rates (11, 5.5, 2, 1Mbps)
-# cckbw202gpo cckbw20ul2gpo
-#
-u16 cckbw202gpo {
- srom 9-10 0x140
- srom >= 11 0x150
-}
-u16 cckbw20ul2gpo {
- srom 9-10 0x142
- srom >= 11 0x152
-}
-
-# Country code (2 bytes ascii + 1 byte cctl)
-# in rev 2
-#
-char[2] ccode {
- sfmt ccode
- srom 0-3 0x76
- srom 4 0x52
- srom 5-7 0x44
- srom 8-10 0x92
- srom >= 11 0x96
-}
-
-# 2 byte; txchain, rxchain
-u8 txchain {
- all1 ignore
- srom 4-7 0x7B (&0xF)
- srom 8-10 0xA3 (&0xF)
- srom >= 11 0xA9 (&0xF)
-}
-u8 rxchain {
- all1 ignore
- srom 4-7 0x7B (&0xF0, >>4)
- srom 8-10 0xA3 (&0xF0, >>4)
- srom >= 11 0xA9 (&0xF0, >>4)
-}
-u16 antswitch {
- all1 ignore
- srom 4-7 u8 0x7A
- srom 8-10 u8 0xA2
- srom >= 11 u8 0xA8
-}
-
-u8 elna2g {
- srom 8-10 0xBB
-}
-
-u8 elna5g {
- srom 8-10 0xBA
-}
-
-# 11n front-end specification
-u8 antswctl2g {
- srom 8-10 0xAE (&0xF8, >>3)
-}
-u8 triso2g {
- srom 8-10 0xAE (&0x7)
-}
-u8 pdetrange2g {
- srom 8-10 0xAF (&0xF8, >>3)
-}
-u8 extpagain2g {
- srom 8-10 0xAF (&0x6, >>1)
-}
-u8 tssipos2g {
- srom 8-10 0xAF (&0x1)
-}
-u8 antswctl5g {
- srom 8-10 0xB0 (&0xF8, >>3)
-}
-u8 triso5g {
- srom 8-10 0xB0 (&0x7)
-}
-u8 pdetrange5g {
- srom 8-10 0xB1 (&0xF8, >>3)
-}
-u8 extpagain5g {
- srom 8-10 0xB1 (&0x6, >>1)
-}
-u8 tssipos5g {
- srom 8-10 0xB1 (&0x1)
-}
-
-# FEM config
-u8 femctrl {
- sfmt decimal
- srom >= 11 0xAA (&0xF8, >>3)
-}
-u8 papdcap2g {
- sfmt decimal
- srom >= 11 0xAA (&0x4, >>2)
-}
-u8 tworangetssi2g {
- sfmt decimal
- srom >= 11 0xAA (&0x2, >>1)
-}
-u8 pdgain2g {
- sfmt decimal
- srom >= 11 u16 0xAA (&0x1F0, >>4)
-}
-u8 epagain2g {
- sfmt decimal
- srom >= 11 0xAB (&0xE, >>1)
-}
-u8 tssiposslope2g {
- sfmt decimal
- srom >= 11 0xAB (&0x1)
-}
-u8 gainctrlsph {
- sfmt decimal
- srom >= 11 0xAC (&0xF8, >>3)
-}
-u8 papdcap5g {
- sfmt decimal
- srom >= 11 0xAC (&0x4, >>2)
-}
-u8 tworangetssi5g {
- sfmt decimal
- srom >= 11 0xAC (&0x2, >>1)
-}
-u8 pdgain5g {
- sfmt decimal
- srom >= 11 u16 0xAC (&0x1F0, >>4)
-}
-u8 epagain5g {
- sfmt decimal
- srom >= 11 0xAD (&0xE, >>1)
-}
-u8 tssiposslope5g {
- sfmt decimal
- srom >= 11 0xAD (&0x1)
-}
+group "Antenna Configuration" {
+ u8 aa2g {
+ desc "Available 2.4GHz Antennas"
+ help "Antennas 0-3 are marked as available if the
+ corresponding bit is set."
+ }
+ u8 aa5g {
+ desc "Available 5GHz Antennas"
+ help "Antennas 0-3 are marked as available if the
+ corresponding bit is set."
+ }
-# LED duty cycle
-u8[2] leddc {
- sfmt led_dc
- all1 ignore
- srom 3 0x7C
- srom 4 0x5A
- srom 5-7 0x5A
- srom 8-10 0x9A
- srom >= 11 0x9E
-}
+ u8 ag0 {
+ desc "Antenna 0 Gain"
+ help "The lower 6 bits represent dB as a signed number.
+ The high 2 bits represent a positive number of
+ quarter dBs to be added to the dB value"
+ }
+ u8 ag1 {
+ desc "Antenna 1 Gain"
+ help "The lower 6 bits represent dB as a signed number.
+ The high 2 bits represent a positive number of
+ quarter dBs to be added to the dB value"
+ }
+ u8 ag2 {
+ desc "Antenna 2 Gain"
+ help "The lower 6 bits represent dB as a signed number.
+ The high 2 bits represent a positive number of
+ quarter dBs to be added to the dB value"
+ }
+ u8 ag3 {
+ desc "Antenna 3 Gain"
+ help "The lower 6 bits represent dB as a signed number.
+ The high 2 bits represent a positive number of
+ quarter dBs to be added to the dB value"
+ }
-# LED set
-u8 ledbh0 {
- all1 ignore
- srom 1-3 0x65
- srom 4 0x57
- srom 5-7 0x77
- srom 8-10 0x97
- srom >= 11 0x9B
-}
-u8 ledbh1 {
- all1 ignore
- srom 1-3 0x64
- srom 4 0x56
- srom 5-7 0x76
- srom 8-10 0x96
- srom >= 11 0x9A
-}
-u8 ledbh2 {
- all1 ignore
- srom 1-3 0x67
- srom 4 0x59
- srom 5-7 0x79
- srom 8-10 0x99
- srom >= 11 0x9D
-}
-u8 ledbh3 {
- all1 ignore
- srom 1-3 0x66
- srom 4 0x58
- srom 5-7 0x78
- srom 8-10 0x98
- srom >= 11 0x9C
-}
+ u8 txchain {
+ desc "Available TX Chains"
+ help "TX chains 0-3 are marked as available if the
+ corresponding bit is set."
-# 2 bytes total
-# Additional power offset for Legacy Dup40 transmissions.
-# Applied in addition to legofdmbw20ulXpo, X=2g, 5gl, 5gm, or 5gh.
-# LSB nibble: 2G band, MSB nibble: 5G band high subband.
-# leg40dup5ghpo, leg40dup5gmpo, leg40dup5glpo, leg40dup2gpo
-#
-u16 legofdm40duppo {
- srom 9-10 0x196
-}
+ all1 ignore
+ }
-# 4 bytes each
-# OFDM power offsets for 20 MHz Legacy rates
-# (54, 48, 36, 24, 18, 12, 9, 6 Mbps)
-# legofdmbw202gpo legofdmbw20ul2gpo
-#
-u32 legofdmbw202gpo {
- srom 9-10 0x144
-}
-u32 legofdmbw20ul2gpo {
- srom 9-10 0x148
-}
+ u8 rxchain {
+ desc "Available RX Chains"
+ help "RX chains 0-3 are marked as available if the
+ corresponding bit is set."
-# 4 bytes each
-# 5G band: OFDM power offsets for 20 MHz Legacy rates
-# (54, 48, 36, 24, 18, 12, 9, 6 Mbps)
-# low subband : legofdmbw205glpo legofdmbw20ul2glpo
-# mid subband :legofdmbw205gmpo legofdmbw20ul2gmpo
-# high subband :legofdmbw205ghpo legofdmbw20ul2ghpo
-#
-u32 legofdmbw205glpo {
- srom 9-10 0x14C
-}
-u32 legofdmbw20ul5glpo {
- srom 9-10 0x150
-}
-u32 legofdmbw205gmpo {
- srom 9-10 0x154
-}
-u32 legofdmbw20ul5gmpo {
- srom 9-10 0x158
-}
-u32 legofdmbw205ghpo {
- srom 9-10 0x15C
-}
-u32 legofdmbw20ul5ghpo {
- srom 9-10 0x160
-}
+ all1 ignore
+ }
-# mac addr override for the standard CIS LAN_NID
-u8[6] macaddr {
- sfmt macaddr
- srom 3 u8 0x4B, u8 0x4A, u8 0x4D, u8 0x4C, u8 0x4F, u8 0x4E
- srom 4 u8 0x4D, u8 0x4C, u8 0x4F, u8 0x4E, u8 0x51, u8 0x50
- srom 5-7 u8 0x53, u8 0x52, u8 0x55, u8 0x54, u8 0x57, u8 0x56
- srom 8-10 u8 0x8D, u8 0x8C, u8 0x8F, u8 0x8E, u8 0x91, u8 0x90
- srom >= 11 u8 0x91, u8 0x90, u8 0x93, u8 0x92, u8 0x95, u8 0x94
+ u16 antswitch {
+ desc "Antenna Diversity Switch Type"
+ help "The antenna diversity switch configuration used by
+ this device. The value is hardware-specific."
+ all1 ignore
+ }
}
-# 4 bytes each
-# mcs 0-7 power-offset. LSB nibble: m0, MSB nibble: m7
-# mcsbw202gpo mcsbw20ul2gpo mcsbw402gpo
-#
-u32 mcsbw202gpo {
- srom 9-10 0x164
- srom >= 11 0x154
+u8 aga0 {
+ #desc <variable description>
+ #help <variable help>
}
-u32 mcsbw20ul2gpo {
- srom 9-10 0x168
+u8 aga1 {
+ #desc <variable description>
+ #help <variable help>
}
-u32 mcsbw402gpo {
- srom 9-10 0x16C
- srom >= 11 0x158
+u8 aga2 {
+ #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
+u8 agbg0 {
+ #desc <variable description>
+ #help <variable help>
}
-u32 mcsbw20ul5ghpo {
- srom 9-10 0x18C
+u8 agbg1 {
+ #desc <variable description>
+ #help <variable help>
}
-u32 mcsbw405ghpo {
- srom 9-10 0x190
- srom >= 11 0x184
+u8 agbg2 {
+ #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
+u8 antswctl2g {
+ #desc <variable description>
+ #help <variable help>
}
-u32 mcsbw20ul5glpo {
- srom 9-10 0x174
+u8 antswctl5g {
+ #desc <variable description>
+ #help <variable help>
}
-u32 mcsbw405glpo {
- srom 9-10 0x178
- srom >= 11 0x164
+u32 boardflags {
+ #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
+u32 boardflags2 {
+ #desc <variable description>
+ #help <variable help>
}
-u32 mcsbw20ul5gmpo {
- srom 9-10 0x180
+u32 boardflags3 {
+ #desc <variable description>
+ #help <variable help>
}
-u32 mcsbw405gmpo {
- srom 9-10 0x184
- srom >= 11 0x174
+u16 boardnum {
+ fmt decimal
+ #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 boardrev {
+ #desc <variable description>
+ #help <variable help>
}
-
-u8 measpower {
- srom 8-10 0xB4 (&0xFE, >>1)
- srom >= 11 0xB0 (&0xFE, >>1)
+u16 boardtype {
+ #desc <variable description>
+ #help <variable help>
}
-u8 measpower1 {
- srom 8-10 0xBF (&0x7F)
- srom >= 11 0xBB (&0x7F)
+u16 boardvendor {
+ #desc <variable description>
+ #help <variable help>
}
-u8 measpower2 {
- srom 8-10 u16 0xBE (&0x3F80, >>7)
- srom >= 11 u16 0xBA (&0x3F80, >>7)
+u16 bw40po {
+ #desc <variable description>
+ #help <variable help>
}
-u16 rawtempsense {
- srom 8-10 0xB4 (&0x1FF)
- srom >= 11 0xB0 (&0x1FF)
+u16 bwduppo {
+ #desc <variable description>
+ #help <variable help>
}
-
-u8 noiselvl2ga0 {
- sfmt decimal
- srom 8-10 0x1AB (&0x1F)
- srom >= 11 0x1BD (&0x1F)
+u8 bxa2g {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl2ga1 {
- sfmt decimal
- srom 8-10 u16 0x1AA (&0x3E0, >>5)
- srom >= 11 u16 0x1BC (&0x3E0, >>5)
+u8 bxa5g {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl2ga2 {
- sfmt decimal
- srom 8-10 0x1AA (&0x7C, >>2)
- srom >= 11 0x1BC (&0x7C, >>2)
+u8 cc {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u16 cck2gpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 cckPwrOffset {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 cckbw202gpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 cckbw20ul2gpo {
+ #desc <variable description>
+ #help <variable help>
+}
+char ccode[2] {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 cddpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 devid {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 dot11agduphrpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 dot11agduplrpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 dot11agofdmhrbw202gpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 elna2g {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 elna5g {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 epagain2g {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 epagain5g {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 et1macaddr[6] {
+ fmt macaddr
+ #desc <variable description>
+ #help <variable help>
+}
+u8 eu_edthresh2g {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 eu_edthresh5g {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 extpagain2g {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 extpagain5g {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 femctrl {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 freqoffset_corr {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 gainctrlsph {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 hw_iqcal_en {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 il0macaddr[6] {
+ fmt macaddr
+ #desc <variable description>
+ #help <variable help>
+}
+u8 iqcal_swp_dis {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 itt2ga0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 itt2ga1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 itt2ga2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 itt2ga3 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 itt5ga0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 itt5ga1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 itt5ga2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 itt5ga3 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 ledbh0 {
+ all1 ignore
+ #desc <variable description>
+ #help <variable help>
+}
+u8 ledbh1 {
+ all1 ignore
+ #desc <variable description>
+ #help <variable help>
+}
+u8 ledbh2 {
+ all1 ignore
+ #desc <variable description>
+ #help <variable help>
+}
+u8 ledbh3 {
+ all1 ignore
+ #desc <variable description>
+ #help <variable help>
+}
+u32 leddc {
+ all1 ignore
+ fmt leddc
+ #desc <variable description>
+ #help <variable help>
+}
+u16 legofdm40duppo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 legofdmbw202gpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 legofdmbw205ghpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 legofdmbw205glpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 legofdmbw205gmpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 legofdmbw20ul2gpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 legofdmbw20ul5ghpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 legofdmbw20ul5glpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 legofdmbw20ul5gmpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 macaddr[6] {
+ fmt macaddr
+ #desc <variable description>
+ #help <variable help>
+}
+u8 maxp2ga0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 maxp2ga1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 maxp2ga2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 maxp2ga3 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 maxp5ga0[4] {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 maxp5ga1[4] {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 maxp5ga2[4] {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 maxp5ga3[1] {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 maxp5gha0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 maxp5gha1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 maxp5gha2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 maxp5gha3 {
+ #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)
+u8 maxp5gla0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8[4] noiselvl5ga1 {
- sfmt decimal
- srom >= 11 u16[4] 0x1BE (&0x3E0, >>5)
+u8 maxp5gla1 {
+ #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)
+u8 maxp5gla2 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# paparambwver
-u8 paparambwver {
- sfmt decimal
- srom >= 11 0x190 (&0xF0, >>4)
+u8 maxp5gla3 {
+ #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
-}
-u16 pa0b1 {
- sfmt decimal
- srom 1-3 0x60
- srom 8-10 0xC4
-}
-u16 pa0b2 {
- sfmt decimal
- srom 1-3 0x62
- srom 8-10 0xC6
-}
-u8 pa0itssit {
- sfmt decimal
- srom 1-3 0x71
- srom 8-10 0xC0
-}
-u8 pa0maxpwr {
- sfmt decimal
- srom 1-3 0x69
- srom 8-10 0xC1
-}
-u8 opo {
- srom 2-3 0x79
- srom 8-10 0x143
+u16 mcs2gpo0 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# 5G PA params
-u16 pa1b0 {
- sfmt decimal
- srom 1-3 0x6A
- srom 8-10 0xCC
-}
-u16 pa1b1 {
- sfmt decimal
- srom 1-3 0x6C
- srom 8-10 0xCE
-}
-u16 pa1b2 {
- sfmt decimal
- srom 1-3 0x6E
- srom 8-10 0xD0
-}
-u16 pa1lob0 {
- sfmt decimal
- srom 2-3 0x3C
- srom 8-10 0xD2
-}
-u16 pa1lob1 {
- sfmt decimal
- srom 2-3 0x3E
- srom 8-10 0xD4
-}
-u16 pa1lob2 {
- sfmt decimal
- srom 2-3 0x40
- srom 8-10 0xD6
-}
-u16 pa1hib0 {
- sfmt decimal
- srom 2-3 0x42
- srom 8-10 0xD8
-}
-u16 pa1hib1 {
- sfmt decimal
- srom 2-3 0x44
- srom 8-10 0xDA
-}
-u16 pa1hib2 {
- sfmt decimal
- srom 2-3 0x46
- srom 8-10 0xDC
-}
-u8 pa1itssit {
- sfmt decimal
- srom 1-3 0x70
- srom 8-10 0xC8
-}
-u8 pa1maxpwr {
- sfmt decimal
- srom 1-3 0x68
- srom 8-10 0xC9
-}
-u8 pa1lomaxpwr {
- sfmt decimal
- srom 2-3 0x3A
- srom 8-10 0xCA
-}
-u8 pa1himaxpwr {
- sfmt decimal
- srom 2-3 0x3B
- srom 8-10 0xCB
+u16 mcs2gpo1 {
+ #desc <variable description>
+ #help <variable help>
}
-
-u16 pdoffset40ma0 {
- srom >= 11 0xCA
+u16 mcs2gpo2 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 pdoffset40ma1 {
- srom >= 11 0xCC
+u16 mcs2gpo3 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 pdoffset40ma2 {
- srom >= 11 0xCE
+u16 mcs2gpo4 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 pdoffset80ma0 {
- srom >= 11 0xD0
+u16 mcs2gpo5 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 pdoffset80ma1 {
- srom >= 11 0xD2
+u16 mcs2gpo6 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 pdoffset80ma2 {
- srom >= 11 0xD4
+u16 mcs2gpo7 {
+ #desc <variable description>
+ #help <variable help>
}
-
-u8 pdoffset2g40ma0 {
- srom >= 11 0xC9 (&0xF)
+u16 mcs32po {
+ #desc <variable description>
+ #help <variable help>
}
-u8 pdoffset2g40ma1 {
- srom >= 11 0xC9 (&0xF0, >>4)
+u16 mcs5ghpo0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 pdoffset2g40ma2 {
- srom >= 11 0xC8 (&0xF)
+u16 mcs5ghpo1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 pdoffset2g40mvalid {
- srom >= 11 0xC8 (&0x80, >>7)
+u16 mcs5ghpo2 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# 40Mhz channel 2g/5g power offset
-u16 bw40po {
- srom 4-7 0x18E
- srom 8 0x196
+u16 mcs5ghpo3 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# 40Mhz channel dup 2g/5g power offset
-u16 bwduppo {
- srom 4-7 0x190
- srom 8 0x198
+u16 mcs5ghpo4 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# cck2g/ofdm2g/ofdm5g power offset
-u16 cck2gpo {
- srom 4-7 0x138
- srom 8 0x140
+u16 mcs5ghpo5 {
+ #desc <variable description>
+ #help <variable help>
}
-u32 ofdm2gpo {
- srom 4-7 0x13A
- srom 8 0x142
+u16 mcs5ghpo6 {
+ #desc <variable description>
+ #help <variable help>
}
-u32 ofdm5gpo {
- srom 4-7 0x13E
- srom 8 0x146
+u16 mcs5ghpo7 {
+ #desc <variable description>
+ #help <variable help>
}
-u32 ofdm5glpo {
- srom 4-7 0x142
- srom 8 0x14A
+u16 mcs5glpo0 {
+ #desc <variable description>
+ #help <variable help>
}
-u32 ofdm5ghpo {
- srom 4-7 0x146
- srom 8 0x14E
+u16 mcs5glpo1 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# cdd2g/5g power offset
-u16 cddpo {
- srom 4-7 0x18A
- srom 8 0x192
+u16 mcs5glpo2 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# mcs2g power offset
-u16 mcs2gpo0 {
- srom 4-7 0x14A
- srom 8 0x152
+u16 mcs5glpo3 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs2gpo1 {
- srom 4-7 0x14C
- srom 8 0x154
+u16 mcs5glpo4 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs2gpo2 {
- srom 4-7 0x14E
- srom 8 0x156
+u16 mcs5glpo5 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs2gpo3 {
- srom 4-7 0x150
- srom 8 0x158
+u16 mcs5glpo6 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs2gpo4 {
- srom 4-7 0x152
- srom 8 0x15A
+u16 mcs5glpo7 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs2gpo5 {
- srom 4-7 0x154
- srom 8 0x15C
+u16 mcs5gpo0 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs2gpo6 {
- srom 4-7 0x156
- srom 8 0x15E
+u16 mcs5gpo1 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs2gpo7 {
- srom 4-7 0x158
- srom 8 0x160
+u16 mcs5gpo2 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# mcs5g low-high band power offset
-u16 mcs5glpo0 {
- srom 4-7 0x16A
- srom 8 0x172
+u16 mcs5gpo3 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5glpo1 {
- srom 4-7 0x16C
- srom 8 0x174
+u16 mcs5gpo4 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5glpo2 {
- srom 4-7 0x16E
- srom 8 0x176
+u16 mcs5gpo5 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5glpo3 {
- srom 4-7 0x170
- srom 8 0x178
+u16 mcs5gpo6 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5glpo4 {
- srom 4-7 0x172
- srom 8 0x17A
+u16 mcs5gpo7 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5glpo5 {
- srom 4-7 0x174
- srom 8 0x17C
+u32 mcsbw202gpo {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5glpo6 {
- srom 4-7 0x176
- srom 8 0x17E
+u32 mcsbw205ghpo {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5glpo7 {
- srom 4-7 0x178
- srom 8 0x180
+u32 mcsbw205glpo {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5ghpo0 {
- srom 4-7 0x17A
- srom 8 0x182
+u32 mcsbw205gmpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 mcsbw20ul2gpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 mcsbw20ul5ghpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 mcsbw20ul5glpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 mcsbw20ul5gmpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 mcsbw402gpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 mcsbw405ghpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 mcsbw405glpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 mcsbw405gmpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 mcsbw805ghpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 mcsbw805glpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 mcsbw805gmpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 mcslr5ghpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 mcslr5glpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 mcslr5gmpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 measpower {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 measpower1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 measpower2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noisecaloffset {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noisecaloffset5g {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl2ga0 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl2ga1 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl2ga2 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5ga0[4] {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5ga1[4] {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5ga2[4] {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5gha0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5gha1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5gha2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5gla0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5gla1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5gla2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5gma0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5gma1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5gma2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5gua0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5gua1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 noiselvl5gua2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 ofdm2gpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 ofdm5ghpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 ofdm5glpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u32 ofdm5gpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 ofdmlrbw202gpo {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 opo {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa0b0 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa0b1 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa0b2 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pa0itssit {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pa0maxpwr {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa1b0 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa1b1 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa1b2 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa1hib0 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa1hib1 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa1hib2 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pa1himaxpwr {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pa1itssit {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa1lob0 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa1lob1 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa1lob2 {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pa1lomaxpwr {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pa1maxpwr {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa2ga0[3] {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa2ga1[3] {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa2ga2[3] {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa2ga3[3] {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gccka0[3] {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw0a0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw0a1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw0a2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw0a3 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw1a0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw1a1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw1a2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw1a3 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw2a0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw2a1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw2a2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw2a3 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw3a0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw3a1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw3a2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa2gw3a3 {
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa5ga0[12] {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa5ga1[12] {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa5ga2[12] {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+i16 pa5ga3[12] {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gbw4080a0[12] {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gbw4080a1[12] {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gbw40a0[12] {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5ghpo1 {
- srom 4-7 0x17C
- srom 8 0x184
+u16 pa5gbw80a0[12] {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5ghpo2 {
- srom 4-7 0x17E
- srom 8 0x186
+u16 pa5ghw0a0 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5ghpo3 {
- srom 4-7 0x180
- srom 8 0x188
+u16 pa5ghw0a1 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5ghpo4 {
- srom 4-7 0x182
- srom 8 0x18A
+u16 pa5ghw0a2 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5ghpo5 {
- srom 4-7 0x184
- srom 8 0x18C
+u16 pa5ghw0a3 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5ghpo6 {
- srom 4-7 0x186
- srom 8 0x18E
+u16 pa5ghw1a0 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5ghpo7 {
- srom 4-7 0x188
- srom 8 0x190
+u16 pa5ghw1a1 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# mcs5g mid band power offset
-u16 mcs5gpo0 {
- srom 4-7 0x15A
- srom 8 0x162
+u16 pa5ghw1a2 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5gpo1 {
- srom 4-7 0x15C
- srom 8 0x164
+u16 pa5ghw1a3 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5gpo2 {
- srom 4-7 0x15E
- srom 8 0x166
+u16 pa5ghw2a0 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5gpo3 {
- srom 4-7 0x160
- srom 8 0x168
+u16 pa5ghw2a1 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5gpo4 {
- srom 4-7 0x162
- srom 8 0x16A
+u16 pa5ghw2a2 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5gpo5 {
- srom 4-7 0x164
- srom 8 0x16C
+u16 pa5ghw2a3 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5gpo6 {
- srom 4-7 0x166
- srom 8 0x16E
+u16 pa5ghw3a0 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 mcs5gpo7 {
- srom 4-7 0x168
- srom 8 0x170
+u16 pa5ghw3a1 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# stbc2g/5g power offset
-u16 stbcpo {
- srom 4-7 0x18C
- srom 8 0x194
+u16 pa5ghw3a2 {
+ #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 pa5ghw3a3 {
+ #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 pa5glw0a0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rssismc2g {
- srom 3 0x51 (&0xF0, >>4)
- srom 8-10 0xA5 (&0xF0, >>4)
+u16 pa5glw0a1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rssisav2g {
- srom 3 0x50 (&0x7)
- srom 8-10 0xA4 (&0x7)
+u16 pa5glw0a2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 bxa2g {
- srom 3 0x50 (&0x18, >>3)
- srom 8-10 0xA4 (&0x18, >>3)
+u16 pa5glw0a3 {
+ #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 pa5glw1a0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rssismc5g {
- srom 3 0x53 (&0xF0, >>4)
- srom 8-10 0xA7 (&0xF0, >>4)
+u16 pa5glw1a1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rssisav5g {
- srom 3 0x52 (&0x7)
- srom 8-10 0xA6 (&0x7)
+u16 pa5glw1a2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 bxa5g {
- srom 3 0x52 (&0x18, >>3)
- srom 8-10 0xA6 (&0x18, >>3)
+u16 pa5glw1a3 {
+ #desc <variable description>
+ #help <variable help>
}
-
-u8 rxgainerr2ga0 {
- srom 8-10 0x19B (&0x3F)
- srom >= 11 0x1C7 (&0x3F)
+u16 pa5glw2a0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rxgainerr2ga1 {
- srom 8-10 u16 0x19A (&0x7C0, >>6)
- srom >= 11 u16 0x1C6 (&0x7C0, >>6)
+u16 pa5glw2a1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rxgainerr2ga2 {
- srom 8-10 0x19A (&0xF8, >>3)
- srom >= 11 0x1C6 (&0xF8, >>3)
+u16 pa5glw2a2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8[4] rxgainerr5ga0 {
- srom >= 11 u8 0x1C9 (&0x3F), u8 0x1CB (&0x3F), u8 0x1CD (&0x3F), u8 0x1CF (&0x3F)
+u16 pa5glw2a3 {
+ #desc <variable description>
+ #help <variable help>
}
-u8[4] rxgainerr5ga1 {
- srom >= 11 u16[4] 0x1C8 (&0x7C0, >>6)
+u16 pa5glw3a0 {
+ #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)
+u16 pa5glw3a1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rxgainerr5gha0 {
- srom 8-10 0x1A1 (&0x3F)
+u16 pa5glw3a2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rxgainerr5gha1 {
- srom 8-10 u16 0x1A0 (&0x7C0, >>6)
+u16 pa5glw3a3 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw0a0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw0a1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw0a2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw0a3 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw1a0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw1a1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw1a2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw1a3 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw2a0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw2a1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw2a2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw2a3 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw3a0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw3a1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw3a2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pa5gw3a3 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 paparambwver {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 papdcap2g {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 papdcap5g {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pcieingress_war {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pdetrange2g {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pdetrange5g {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pdgain2g {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pdgain5g {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pdoffset2g40ma0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pdoffset2g40ma1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pdoffset2g40ma2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pdoffset2g40mvalid {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pdoffset40ma0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pdoffset40ma1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pdoffset40ma2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pdoffset80ma0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pdoffset80ma1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u16 pdoffset80ma2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pdoffsetcckma0 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pdoffsetcckma1 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 pdoffsetcckma2 {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 phycal_tempdelta {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u16 rawtempsense {
+ #desc <variable description>
+ #help <variable help>
+}
+u8 regrev {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u32 rmax {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+u32 rmin {
+ fmt decimal
+ #desc <variable description>
+ #help <variable help>
+}
+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 rxgainerr5gha2 {
- srom 8-10 0x1A0 (&0xF8, >>3)
+u8 rssisav5g {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rxgainerr5gla0 {
- srom 8-10 0x19D (&0x3F)
+u8 rssismc2g {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rxgainerr5gla1 {
- srom 8-10 u16 0x19C (&0x7C0, >>6)
+u8 rssismc5g {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rxgainerr5gla2 {
- srom 8-10 0x19C (&0xF8, >>3)
+u8 rssismf2g {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rxgainerr5gma0 {
- srom 8-10 0x19F (&0x3F)
+u8 rssismf5g {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rxgainerr5gma1 {
- srom 8-10 u16 0x19E (&0x7C0, >>6)
+u8 rxgainerr2ga0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rxgainerr5gma2 {
- srom 8-10 0x19E (&0xF8, >>3)
+u8 rxgainerr2ga1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rxgainerr5gua0 {
- srom 8-10 0x1A3 (&0x3F)
+u8 rxgainerr2ga2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rxgainerr5gua1 {
- srom 8-10 u16 0x1A2 (&0x7C0, >>6)
+u8 rxgainerr5ga0[4] {
+ #desc <variable description>
+ #help <variable help>
}
-u8 rxgainerr5gua2 {
- srom 8-10 0x1A2 (&0xF8, >>3)
+u8 rxgainerr5ga1[4] {
+ #desc <variable description>
+ #help <variable help>
}
-
-# 4328 2G RX power offset
-i8 rxpo2g {
- sfmt decimal
- srom 3 0x5B
- srom 8-10 0xAD
+u8 rxgainerr5ga2[4] {
+ #desc <variable description>
+ #help <variable help>
}
-
-# 4328 5G RX power offset
-i8 rxpo5g {
- sfmt decimal
- srom 3 0x5A
- srom 8-10 0xAC
+u8 rxgainerr5gha0 {
+ #desc <variable description>
+ #help <variable help>
}
-
-u16 subband5gver {
- srom 8-10 u8 0x1A5 (&0x7)
- srom >= 11 0xD6
+u8 rxgainerr5gha1 {
+ #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
-}
-u8 temps_period {
- sfmt decimal
- srom 8-10 0xBC (&0xF)
- srom >= 11 0xB8 (&0xF)
-}
-u8 temps_hysteresis {
- sfmt decimal
- srom 8-10 0xBC (&0xF0, >>4)
- srom >= 11 0xB8 (&0xF0, >>4)
-}
-u8 tempoffset {
- sfmt decimal
- srom 8-10 0xB3
- srom >= 11 0xAF
-}
-u8 tempsense_slope {
- srom 8-10 0xB7
- srom >= 11 0xB3
-}
-u8 tempcorrx {
- srom 8-10 0xB6 (&0xFC, >>2)
- srom >= 11 0xB2 (&0xFC, >>2)
-}
-u8 tempsense_option {
- srom 8-10 0xB6 (&0x3)
- srom >= 11 0xB2 (&0x3)
-}
-u8 phycal_tempdelta {
- sfmt decimal
- srom 8-10 0xBD
- srom >= 11 0xB9
+u8 rxgainerr5gha2 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# 4328 2G TR isolation, 1 byte
-u8 tri2g {
- srom 3 0x55
- srom 8-10 0xA9
+u8 rxgainerr5gla0 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# 4328 5G TR isolation, 3 bytes
-u8 tri5gl {
- srom 3 0x57
- srom 8-10 0xAB
+u8 rxgainerr5gla1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 tri5g {
- srom 3 0x54
- srom 8-10 0xA8
+u8 rxgainerr5gla2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 tri5gh {
- srom 3 0x56
- srom 8-10 0xAA
+u8 rxgainerr5gma0 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# phy txbf rpcalvars
-u16 rpcal2g {
- srom >= 11 0x16C
+u8 rxgainerr5gma1 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 rpcal5gb0 {
- srom >= 11 0x16E
+u8 rxgainerr5gma2 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 rpcal5gb1 {
- srom >= 11 0x17C
+u8 rxgainerr5gua0 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 rpcal5gb2 {
- srom >= 11 0x17E
+u8 rxgainerr5gua1 {
+ #desc <variable description>
+ #help <variable help>
}
-u16 rpcal5gb3 {
- srom >= 11 0x18C
+u8 rxgainerr5gua2 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# Crystal frequency in kilohertz
-u32 xtalfreq {
- sfmt decimal
- srom >= 11 u16 0xB4
+u8 rxgains2gelnagaina0 {
+ #desc <variable description>
+ #help <variable help>
}
-
-# N-PHY tx power workaround
-u8 txpid2ga0 {
- srom 4-7 0x63
+u8 rxgains2gelnagaina1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid2ga1 {
- srom 4-7 0x62
+u8 rxgains2gelnagaina2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid2ga2 {
- srom 4-7 0x65
+u8 rxgains2gelnagaina3 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid2ga3 {
- srom 4-7 0x64
+u8 rxgains2gtrelnabypa0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid5ga0 {
- srom 4-7 0x67
+u8 rxgains2gtrelnabypa1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid5ga1 {
- srom 4-7 0x66
+u8 rxgains2gtrelnabypa2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid5ga2 {
- srom 4-7 0x69
+u8 rxgains2gtrelnabypa3 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid5ga3 {
- srom 4-7 0x68
+u8 rxgains2gtrisoa0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid5gha0 {
- srom 4-7 0x6F
+u8 rxgains2gtrisoa1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid5gha1 {
- srom 4-7 0x6E
+u8 rxgains2gtrisoa2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid5gha2 {
- srom 4-7 0x71
+u8 rxgains2gtrisoa3 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid5gha3 {
- srom 4-7 0x70
+u8 rxgains5gelnagaina0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid5gla0 {
- srom 4-7 0x6B
+u8 rxgains5gelnagaina1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid5gla1 {
- srom 4-7 0x6A
+u8 rxgains5gelnagaina2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid5gla2 {
- srom 4-7 0x6D
+u8 rxgains5gelnagaina3 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txpid5gla3 {
- srom 4-7 0x6C
+u8 rxgains5ghelnagaina0 {
+ #desc <variable description>
+ #help <variable help>
}
-
-u16 cckPwrOffset {
- srom 10 0x1B4
+u8 rxgains5ghelnagaina1 {
+ #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
+u8 rxgains5ghelnagaina2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 eu_edthresh2g {
- srom 8 0x1A9
- srom 9 0x199
- srom 10 0x199
- srom 11 0x1D1
+u8 rxgains5ghelnagaina3 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 eu_edthresh5g {
- srom 8 0x1A8
- srom 9 0x198
- srom 10 0x198
- srom 11 0x1D0
+u8 rxgains5ghtrelnabypa0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 freqoffset_corr {
- srom 8-10 0xB9 (&0xF)
+u8 rxgains5ghtrelnabypa1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 hw_iqcal_en {
- srom 8-10 0xB9 (&0x20, >>5)
+u8 rxgains5ghtrelnabypa2 {
+ #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 rxgains5ghtrelnabypa3 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 iqcal_swp_dis {
- srom 8-10 0xB9 (&0x10, >>4)
+u8 rxgains5ghtrisoa0 {
+ #desc <variable description>
+ #help <variable help>
}
-
-u8 noisecaloffset {
- srom 8-9 0x1B5
+u8 rxgains5ghtrisoa1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noisecaloffset5g {
- srom 8-9 0x1B4
+u8 rxgains5ghtrisoa2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl5gha0 {
- srom 8-10 0x1B1 (&0x1F)
+u8 rxgains5ghtrisoa3 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl5gha1 {
- srom 8-10 u16 0x1B0 (&0x3E0, >>5)
+u8 rxgains5gmelnagaina0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl5gha2 {
- srom 8-10 0x1B0 (&0x7C, >>2)
+u8 rxgains5gmelnagaina1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl5gla0 {
- srom 8-10 0x1AD (&0x1F)
+u8 rxgains5gmelnagaina2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl5gla1 {
- srom 8-10 u16 0x1AC (&0x3E0, >>5)
+u8 rxgains5gmelnagaina3 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl5gla2 {
- srom 8-10 0x1AC (&0x7C, >>2)
+u8 rxgains5gmtrelnabypa0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl5gma0 {
- srom 8-10 0x1AF (&0x1F)
+u8 rxgains5gmtrelnabypa1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl5gma1 {
- srom 8-10 u16 0x1AE (&0x3E0, >>5)
+u8 rxgains5gmtrelnabypa2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl5gma2 {
- srom 8-10 0x1AE (&0x7C, >>2)
+u8 rxgains5gmtrelnabypa3 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl5gua0 {
- srom 8-10 0x1B3 (&0x1F)
+u8 rxgains5gmtrisoa0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl5gua1 {
- srom 8-10 u16 0x1B2 (&0x3E0, >>5)
+u8 rxgains5gmtrisoa1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 noiselvl5gua2 {
- srom 8-10 0x1B2 (&0x7C, >>2)
+u8 rxgains5gmtrisoa2 {
+ #desc <variable description>
+ #help <variable help>
}
-
-u8 pcieingress_war {
- srom 8-10 0x1A7 (&0xF)
+u8 rxgains5gmtrisoa3 {
+ #desc <variable description>
+ #help <variable help>
}
-
-u8 pdoffsetcckma0 {
- srom >= 11 0x18F (&0xF)
+u8 rxgains5gtrelnabypa0 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 pdoffsetcckma1 {
- srom >= 11 0x18F (&0xF0, >>4)
+u8 rxgains5gtrelnabypa1 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 pdoffsetcckma2 {
- srom >= 11 0x18E (&0xF)
+u8 rxgains5gtrelnabypa2 {
+ #desc <variable description>
+ #help <variable help>
}
-
-u8 sar2g {
- srom 9-10 0x1A9
- srom >= 11 0x1BB
+u8 rxgains5gtrelnabypa3 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 sar5g {
- srom 9-10 0x1A8
- srom >= 11 0x1BA
+u8 rxgains5gtrisoa0 {
+ #desc <variable description>
+ #help <variable help>
}
-
-u32[5] swctrlmap_2g {
- srom 10 u32[4] 0x1B8, u16 0x1C8
+u8 rxgains5gtrisoa1 {
+ #desc <variable description>
+ #help <variable help>
}
-
-u16 tssifloor2g {
- srom >= 11 0xBE (&0x3FF)
-}
-u16[4] tssifloor5g {
- srom >= 11 0xC0 (&0x3FF)
-}
-
-u8 txidxcap2g {
- srom >= 11 u16 0x1A8 (&0xFF0, >>4)
+u8 rxgains5gtrisoa2 {
+ #desc <variable description>
+ #help <variable help>
}
-u8 txidxcap5g {
- srom >= 11 u16 0x1AC (&0xFF0, >>4)
+u8 rxgains5gtrisoa3 {
+ #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
- }
-
- # AC-PHY rxgains
- u8 rxgains5ghtrelnabypa {
- srom >= 11 0x8 (&0x80, >>7)
- }
- u8 rxgains5ghelnagaina {
- srom >= 11 0x8 (&0x7)
- }
- u8 rxgains5gelnagaina {
- srom >= 11 0xA (&0x7)
- }
- u8 rxgains5gmtrelnabypa {
- srom >= 11 0x9 (&0x80, >>7)
+i8 rxpo2g {
+ fmt decimal
+ #desc <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>
+}
+
+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>
+}
+
+srom 2-3 {
+ 0x004: u16 boardtype
+ 0x006: u16 subvid
+ 0x03A: u8 pa1himaxpwr
+ 0x03B: u8 pa1lomaxpwr
+ 0x03C: i16 pa1lob0
+ 0x03E: i16 pa1lob1
+ 0x040: i16 pa1lob2
+ 0x042: i16 pa1hib0
+ 0x044: i16 pa1hib1
+ 0x046: i16 pa1hib2
+ srom 2 {
+ 0x048: u8 il0macaddr[6] {
+ +0x1, +0x0, +0x3, +0x2, +0x5, +0x4
+ }
+ 0x04C: u16 boardnum
+ 0x054: u8 et1macaddr[6] {
+ +0x1, +0x0, +0x3, +0x2, +0x5, +0x4
+ }
}
- u8 rxgains2gtrelnabypa {
- srom >= 11 0xB (&0x80, >>7)
+ srom 3 {
+ 0x04A: u8 macaddr[6] {
+ +0x1, +0x0, +0x3, +0x2, +0x5, +0x4
+ }
+ 0x04E: u16 boardnum
+ 0x050: u8 rssismf2g (&0xF)
+ 0x050: u8 rssismc2g (&0xF0, >>4)
+ 0x051: u8 bxa2g (&0x18, >>3)
+ 0x051: u8 rssisav2g (&0x7)
+ 0x052: u8 rssismc5g (&0xF0, >>4)
+ 0x052: u8 rssismf5g (&0xF)
+ 0x053: u8 bxa5g (&0x18, >>3)
+ 0x053: u8 rssisav5g (&0x7)
+ 0x054: u8 tri2g
+ 0x055: u8 tri5g
+ 0x056: u8 tri5gl
+ 0x057: u8 tri5gh
+ 0x05A: i8 rxpo2g
+ 0x05B: i8 rxpo5g
}
- u8 rxgains5gmtrisoa {
- srom >= 11 0x9 (&0x78, >>3)
+ 0x05C: u8 boardrev
+ 0x05D: u8 aa5g (&0xC0, >>6)
+ 0x05D: u8 aa2g (&0x30, >>4)
+ 0x05E: i16 pa0b0
+ 0x060: i16 pa0b1
+ 0x062: i16 pa0b2
+ 0x064: u8 ledbh0
+ 0x065: u8 ledbh1
+ 0x066: u8 ledbh2
+ 0x067: u8 ledbh3
+ 0x068: u8 pa0maxpwr
+ 0x069: u8 pa1maxpwr
+ 0x06A: i16 pa1b0
+ 0x06C: i16 pa1b1
+ 0x06E: i16 pa1b2
+ 0x070: u8 pa0itssit
+ 0x071: u8 pa1itssit
+ srom 2 {
+ 0x072: u32 boardflags {
+ +0x0: u16 |
+ 0x038: u16 (<<16)
+ }
}
- u8 rxgains5gmelnagaina {
- srom >= 11 0x9 (&0x7)
+ srom 3 {
+ 0x072: u32 boardflags {
+ +0x0: u16 |
+ +0x8: u16 (<<16)
+ }
}
- u8 rxgains2gelnagaina {
- srom >= 11 0xB (&0x7)
+ 0x074: u8 ag0
+ 0x075: u8 ag1
+ 0x076: char ccode[2] { +0x1, +0x0 }
+ 0x078: u8 opo
+ srom 3 {
+ 0x079: u8 regrev
+ 0x07C: u16 leddc
}
- u8 rxgains5gtrisoa {
- srom >= 11 0xA (&0x78, >>3)
+ 0x07E: u8 sromrev
+ 0x07F: u8 <sromcrc>
+}
+
+
+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 rxgains5gtrelnabypa {
- srom >= 11 0xA (&0x80, >>7)
+ 0x082: u16 boardrev
+ 0x084: u32 boardflags
+ 0x088: u32 boardflags2
+ 0x08C: u8 macaddr[6] { +0x1, +0x0, +0x3, +0x2, +0x5, +0x4 }
+ 0x090: u16 boardnum
+ 0x092: char ccode[2] { +0x1, +0x0 }
+ 0x094: u8 regrev
+ 0x096: u8 ledbh0
+ 0x097: u8 ledbh1
+ 0x098: u8 ledbh2
+ 0x099: u8 ledbh3
+ 0x09A: u16 leddc
+ 0x09C: u8 aa2g
+ 0x09D: u8 aa5g
+ 0x09E: u8 ag0
+ 0x09F: u8 ag1
+ 0x0A0: u8 ag2
+ 0x0A1: u8 ag3
+ 0x0A2: u8 txchain (&0xF)
+ 0x0A2: u8 rxchain (&0xF0, >>4)
+ 0x0A3: u8 antswitch
+ 0x0A4: u8 rssismf2g (&0xF)
+ 0x0A4: u8 rssismc2g (&0xF0, >>4)
+ 0x0A5: u8 rssisav2g (&0x7)
+ 0x0A5: u8 bxa2g (&0x18, >>3)
+ 0x0A6: u8 rssismf5g (&0xF)
+ 0x0A6: u8 rssismc5g (&0xF0, >>4)
+ 0x0A7: u8 bxa5g (&0x18, >>3)
+ 0x0A7: u8 rssisav5g (&0x7)
+ 0x0A8: u8 tri2g
+ 0x0A9: u8 tri5g
+ 0x0AA: u8 tri5gl
+ 0x0AB: u8 tri5gh
+ 0x0AC: i8 rxpo2g
+ 0x0AD: i8 rxpo5g
+ 0x0AE: u8 extpagain2g (&0x6, >>1)
+ 0x0AE: u8 tssipos2g (&0x1)
+ 0x0AE: u8 pdetrange2g (&0xF8, >>3)
+ 0x0AF: u8 antswctl2g (&0xF8, >>3)
+ 0x0AF: u8 triso2g (&0x7)
+ 0x0B0: u8 extpagain5g (&0x6, >>1)
+ 0x0B0: u8 pdetrange5g (&0xF8, >>3)
+ 0x0B0: u8 tssipos5g (&0x1)
+ 0x0B1: u8 triso5g (&0x7)
+ 0x0B1: u8 antswctl5g (&0xF8, >>3)
+ 0x0B2: u8 tempoffset
+ 0x0B3: u8 tempthresh
+ 0x0B4: u16 rawtempsense (&0x1FF)
+ 0x0B5: u8 measpower (&0xFE, >>1)
+ 0x0B6: u8 tempsense_slope
+ 0x0B7: u8 tempsense_option (&0x3)
+ 0x0B7: u8 tempcorrx (&0xFC, >>2)
+ 0x0B8: u8 iqcal_swp_dis (&0x10, >>4)
+ 0x0B8: u8 freqoffset_corr (&0xF)
+ 0x0B8: u8 hw_iqcal_en (&0x20, >>5)
+ 0x0BA: u8 elna2g
+ 0x0BB: u8 elna5g
+ 0x0BC: u8 phycal_tempdelta
+ 0x0BD: u8 temps_hysteresis (&0xF0, >>4)
+ 0x0BD: u8 temps_period (&0xF)
+ 0x0BE: u8 measpower2 {
+ +0x0: u16 (&0x3F80, >>7)
+ }
+ 0x0BE: u8 measpower1 (&0x7F)
+ 0x0C0: u8 pa0maxpwr
+ 0x0C0: u8 maxp2ga0
+ 0x0C1: u8 itt2ga0
+ 0x0C1: u8 pa0itssit
+ 0x0C2: u16 pa2gw0a0
+ 0x0C2: i16 pa0b0
+ 0x0C4: i16 pa0b1
+ 0x0C4: u16 pa2gw1a0
+ 0x0C6: u16 pa2gw2a0
+ 0x0C6: i16 pa0b2
+ 0x0C8: u8 pa1maxpwr
+ 0x0C8: u8 maxp5ga0[1]
+ 0x0C9: u8 itt5ga0
+ 0x0C9: u8 pa1itssit
+ 0x0CA: u8 pa1himaxpwr
+ 0x0CA: u8 maxp5gha0
+ 0x0CB: u8 maxp5gla0
+ 0x0CB: u8 pa1lomaxpwr
+ 0x0CC: i16 pa1b0
+ 0x0CC: u16 pa5gw0a0
+ 0x0CE: u16 pa5gw1a0
+ 0x0CE: i16 pa1b1
+ 0x0D0: i16 pa1b2
+ 0x0D0: u16 pa5gw2a0
+ 0x0D2: u16 pa5glw0a0
+ 0x0D2: i16 pa1lob0
+ 0x0D4: u16 pa5glw1a0
+ 0x0D4: i16 pa1lob1
+ 0x0D6: u16 pa5glw2a0
+ 0x0D6: i16 pa1lob2
+ 0x0D8: i16 pa1hib0
+ 0x0D8: u16 pa5ghw0a0
+ 0x0DA: u16 pa5ghw1a0
+ 0x0DA: i16 pa1hib1
+ 0x0DC: u16 pa5ghw2a0
+ 0x0DC: i16 pa1hib2
+ 0x0E0: u8 maxp2ga1
+ 0x0E1: u8 itt2ga1
+ 0x0E2: u16 pa2gw0a1
+ 0x0E4: u16 pa2gw1a1
+ 0x0E6: u16 pa2gw2a1
+ 0x0E8: u8 maxp5ga1[1]
+ 0x0E9: u8 itt5ga1
+ 0x0EA: u8 maxp5gha1
+ 0x0EB: u8 maxp5gla1
+ 0x0EC: u16 pa5gw0a1
+ 0x0EE: u16 pa5gw1a1
+ 0x0F0: u16 pa5gw2a1
+ 0x0F2: u16 pa5glw0a1
+ 0x0F4: u16 pa5glw1a1
+ 0x0F6: u16 pa5glw2a1
+ 0x0F8: u16 pa5ghw0a1
+ 0x0FA: u16 pa5ghw1a1
+ 0x0FC: u16 pa5ghw2a1
+ 0x100: u8 maxp2ga2
+ 0x101: u8 itt2ga2
+ 0x102: u16 pa2gw0a2
+ 0x104: u16 pa2gw1a2
+ 0x106: u16 pa2gw2a2
+ 0x108: u8 maxp5ga2[1]
+ 0x109: u8 itt5ga2
+ 0x10A: u8 maxp5gha2
+ 0x10B: u8 maxp5gla2
+ 0x10C: u16 pa5gw0a2
+ 0x10E: u16 pa5gw1a2
+ 0x110: u16 pa5gw2a2
+ 0x112: u16 pa5glw0a2
+ 0x114: u16 pa5glw1a2
+ 0x116: u16 pa5glw2a2
+ 0x118: u16 pa5ghw0a2
+ 0x11A: u16 pa5ghw1a2
+ 0x11C: u16 pa5ghw2a2
+ 0x120: u8 maxp2ga3
+ 0x121: u8 itt2ga3
+ 0x122: u16 pa2gw0a3
+ 0x124: u16 pa2gw1a3
+ 0x126: u16 pa2gw2a3
+ 0x128: u8 maxp5ga3[1]
+ 0x129: u8 itt5ga3
+ 0x12A: u8 maxp5gha3
+ 0x12B: u8 maxp5gla3
+ 0x12C: u16 pa5gw0a3
+ 0x12E: u16 pa5gw1a3
+ 0x130: u16 pa5gw2a3
+ 0x132: u16 pa5glw0a3
+ 0x134: u16 pa5glw1a3
+ 0x136: u16 pa5glw2a3
+ 0x138: u16 pa5ghw0a3
+ 0x13A: u16 pa5ghw1a3
+ 0x13C: u16 pa5ghw2a3
+ 0x140: u16 cckbw202gpo
+ 0x142: u8 opo
+ 0x142: u16 cckbw20ul2gpo
+ 0x144: u32 legofdmbw202gpo
+ 0x148: u32 legofdmbw20ul2gpo
+ 0x14C: u32 legofdmbw205glpo
+ 0x150: u32 legofdmbw20ul5glpo
+ 0x154: u32 legofdmbw205gmpo
+ 0x158: u32 legofdmbw20ul5gmpo
+ 0x15C: u32 legofdmbw205ghpo
+ 0x160: u32 legofdmbw20ul5ghpo
+ 0x164: u32 mcsbw202gpo
+ 0x168: u32 mcsbw20ul2gpo
+ 0x16C: u32 mcsbw402gpo
+ 0x170: u32 mcsbw205glpo
+ 0x174: u32 mcsbw20ul5glpo
+ 0x178: u32 mcsbw405glpo
+ 0x17C: u32 mcsbw205gmpo
+ 0x180: u32 mcsbw20ul5gmpo
+ 0x184: u32 mcsbw405gmpo
+ 0x188: u32 mcsbw205ghpo
+ 0x18C: u32 mcsbw20ul5ghpo
+ 0x190: u32 mcsbw405ghpo
+ 0x194: u16 mcs32po
+ 0x196: u16 legofdm40duppo
+ 0x198: u8 eu_edthresh2g
+ 0x199: u8 eu_edthresh5g
+ 0x19A: u8 rxgainerr2ga0 (&0x3F)
+ 0x19A: u8 rxgainerr2ga1 {
+ +0x0: u16 (&0x7C0, >>6)
+ }
+ 0x19B: u8 rxgainerr2ga2 (&0xF8, >>3)
+ 0x19C: u8 rxgainerr5gla0 (&0x3F)
+ 0x19C: u8 rxgainerr5gla1 {
+ +0x0: u16 (&0x7C0, >>6)
+ }
+ 0x19D: u8 rxgainerr5gla2 (&0xF8, >>3)
+ 0x19E: u8 rxgainerr5gma0 (&0x3F)
+ 0x19E: u8 rxgainerr5gma1 {
+ +0x0: u16 (&0x7C0, >>6)
+ }
+ 0x19F: u8 rxgainerr5gma2 (&0xF8, >>3)
+ 0x1A0: u8 rxgainerr5gha0 (&0x3F)
+ 0x1A0: u8 rxgainerr5gha1 {
+ +0x0: u16 (&0x7C0, >>6)
+ }
+ 0x1A1: u8 rxgainerr5gha2 (&0xF8, >>3)
+ 0x1A2: u8 rxgainerr5gua0 (&0x3F)
+ 0x1A2: u8 rxgainerr5gua1 {
+ +0x0: u16 (&0x7C0, >>6)
+ }
+ 0x1A3: u8 rxgainerr5gua2 (&0xF8, >>3)
+ 0x1A4: u8 subband5gver (&0x7)
+ 0x1A6: u8 pcieingress_war (&0xF)
+ 0x1A8: u8 sar2g
+ 0x1A9: u8 sar5g
+ 0x1AA: u8 noiselvl2ga1 {
+ +0x0: u16 (&0x3E0, >>5)
+ }
+ 0x1AA: u8 noiselvl2ga0 (&0x1F)
+ 0x1AB: u8 noiselvl2ga2 (&0x7C, >>2)
+ 0x1AC: u8 noiselvl5gla0 (&0x1F)
+ 0x1AC: u8 noiselvl5gla1 {
+ +0x0: u16 (&0x3E0, >>5)
+ }
+ 0x1AD: u8 noiselvl5gla2 (&0x7C, >>2)
+ 0x1AE: u8 noiselvl5gma1 {
+ +0x0: u16 (&0x3E0, >>5)
+ }
+ 0x1AE: u8 noiselvl5gma0 (&0x1F)
+ 0x1AF: u8 noiselvl5gma2 (&0x7C, >>2)
+ 0x1B0: u8 noiselvl5gha0 (&0x1F)
+ 0x1B0: u8 noiselvl5gha1 {
+ +0x0: u16 (&0x3E0, >>5)
+ }
+ 0x1B1: u8 noiselvl5gha2 (&0x7C, >>2)
+ 0x1B2: u8 noiselvl5gua0 (&0x1F)
+ 0x1B2: u8 noiselvl5gua1 {
+ +0x0: u16 (&0x3E0, >>5)
+ }
+ 0x1B3: u8 noiselvl5gua2 (&0x7C, >>2)
+ srom 9 {
+ 0x1B4: u8 noisecaloffset
+ 0x1B5: u8 noisecaloffset5g
+ 0x1B6: u8 sromrev
+ 0x1B7: u8 <sromcrc>
}
- u8 rxgains2gtrisoa {
- srom >= 11 0xB (&0x78, >>3)
- }
- u8 rxgains5ghtrisoa {
- srom >= 11 0x8 (&0x78, >>3)
+ 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>
}
+}
- # 11n PA parameters
- u16 pa5gw2a {
- srom 4-7 0x12
- srom 8-10 0x10
- }
- u16 pa5ghw1a {
- srom 4-7 0x20
- srom 8-10 0x1A
- }
- u16 pa5glw3a {
- srom 4-7 0x1C
- }
- u16 pa5glw1a {
- srom 4-7 0x18
- srom 8-10 0x14
- }
- u16 pa5gw1a {
- srom 4-7 0x10
- srom 8-10 0xE
- }
- u16 pa5glw0a {
- srom 4-7 0x16
- srom 8-10 0x12
- }
- u16 pa5gw3a {
- srom 4-7 0x14
- }
- u16 pa5glw2a {
- srom 4-7 0x1A
- srom 8-10 0x16
- }
- u16 pa5ghw3a {
- srom 4-7 0x24
- }
- u16 pa5gw0a {
- srom 4-7 0xE
- srom 8-10 0xC
- }
- u8 maxp5gha {
- srom 4-7 0xD
- srom 8-10 0xB
- }
- u16 pa5ghw2a {
- srom 4-7 0x22
- srom 8-10 0x1C
- }
- u16 pa5ghw0a {
- srom 4-7 0x1E
- srom 8-10 0x18
- }
- u16 pa2gw3a {
- srom 4-7 0x8
- }
- u16 pa2gw2a {
- srom 4-7 0x6
- srom 8-10 0x6
- }
- u16 pa2gw1a {
- srom 4-7 0x4
- srom 8-10 0x4
- }
- u16 pa2gw0a {
- srom 4-7 0x2
- srom 8-10 0x2
- }
- u8 maxp5gla {
- srom 4-7 0xC
- srom 8-10 0xA
- }
- u8 itt5ga {
- srom 4-7 0xA
- srom 8-10 0x8
- }
- u8 itt2ga {
- srom 4-7 0x0
- srom 8-10 0x0
- }
+
+srom 11 {
+ 0x004: u16 boardtype
+ 0x006: u16 subvid
+ 0x060: u16 devid
+ 0x080: u16 <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>
}
Index: sys/dev/bhnd/tools/nvram_map_gen.awk
===================================================================
--- sys/dev/bhnd/tools/nvram_map_gen.awk
+++ sys/dev/bhnd/tools/nvram_map_gen.awk
@@ -30,14 +30,28 @@
#
# $FreeBSD$
-BEGIN {
+BEGIN { main() }
+END { at_exit() }
+
+#
+# Print usage
+#
+function usage() {
+ print "usage: bhnd_nvram_map.awk <input map> [-hd] [-o output file]"
+ _EARLY_EXIT = 1
+ exit 1
+}
+
+function main(_i) {
RS="\n"
- depth = 0
- symbols[depth,"_file"] = FILENAME
- num_output_vars = 0
OUTPUT_FILE = null
+ # Probe awk implementation's hex digit handling
+ if ("0xA" + 0 != 10) {
+ AWK_REQ_HEX_PARSING=1
+ }
+
# Seed rand()
srand()
@@ -46,36 +60,39 @@
OUT_T_HEADER = "HEADER"
OUT_T_DATA = "DATA"
+ # Tab width to use when calculating output alignment
+ TAB_WIDTH = 8
+
# Enable debug output
DEBUG = 0
# Maximum revision
- REV_MAX = 255
+ REV_MAX = 256
# Parse arguments
if (ARGC < 2)
usage()
- for (i = 1; i < ARGC; i++) {
- if (ARGV[i] == "--debug") {
+ for (_i = 1; _i < ARGC; _i++) {
+ if (ARGV[_i] == "--debug") {
DEBUG = 1
- } else if (ARGV[i] == "-d" && OUT_T == null) {
+ } else if (ARGV[_i] == "-d" && OUT_T == null) {
OUT_T = OUT_T_DATA
- } else if (ARGV[i] == "-h" && OUT_T == null) {
+ } else if (ARGV[_i] == "-h" && OUT_T == null) {
OUT_T = OUT_T_HEADER
- } else if (ARGV[i] == "-o") {
- i++
- if (i >= ARGC)
+ } else if (ARGV[_i] == "-o") {
+ _i++
+ if (_i >= ARGC)
usage()
- OUTPUT_FILE = ARGV[i]
- } else if (ARGV[i] == "--") {
- i++
+ OUTPUT_FILE = ARGV[_i]
+ } else if (ARGV[_i] == "--") {
+ _i++
break
- } else if (ARGV[i] !~ /^-/) {
- FILENAME = ARGV[i]
+ } else if (ARGV[_i] !~ /^-/) {
+ FILENAME = ARGV[_i]
} else {
- print "unknown option " ARGV[i]
+ print "unknown option " ARGV[_i]
usage()
}
}
@@ -95,8 +112,8 @@
if (OUTPUT_FILE == "-") {
OUTPUT_FILE = "/dev/stdout"
} else if (OUTPUT_FILE == null) {
- _bi = split(FILENAME, _paths, "/")
- OUTPUT_FILE = _paths[_bi]
+ OUTPUT_FILE_IDX = split(FILENAME, _g_output_path, "/")
+ OUTPUT_FILE = _g_output_path[OUTPUT_FILE_IDX]
if (OUTPUT_FILE !~ /^bhnd_/)
OUTPUT_FILE = "bhnd_" OUTPUT_FILE
@@ -107,441 +124,3330 @@
OUTPUT_FILE = OUTPUT_FILE "_data.h"
}
- # Format Constants
- FMT["hex"] = "BHND_NVRAM_SFMT_HEX"
- FMT["decimal"] = "BHND_NVRAM_SFMT_DEC"
- FMT["ccode"] = "BHND_NVRAM_SFMT_CCODE"
- FMT["macaddr"] = "BHND_NVRAM_SFMT_MACADDR"
- FMT["led_dc"] = "BHND_NVRAM_SFMT_LEDDC"
-
- # Data Type Constants
- DTYPE["u8"] = "BHND_NVRAM_TYPE_UINT8"
- DTYPE["u16"] = "BHND_NVRAM_TYPE_UINT16"
- DTYPE["u32"] = "BHND_NVRAM_TYPE_UINT32"
- DTYPE["i8"] = "BHND_NVRAM_TYPE_INT8"
- DTYPE["i16"] = "BHND_NVRAM_TYPE_INT16"
- DTYPE["i32"] = "BHND_NVRAM_TYPE_INT32"
- DTYPE["char"] = "BHND_NVRAM_TYPE_CHAR"
-
- # Default masking for standard types
- TMASK["u8"] = "0x000000FF"
- TMASK["u16"] = "0x0000FFFF"
- TMASK["u32"] = "0xFFFFFFFF"
- TMASK["i8"] = TMASK["u8"]
- TMASK["i16"] = TMASK["u16"]
- TMASK["i32"] = TMASK["u32"]
- TMASK["char"] = TMASK["u8"]
-
- # Byte sizes for standard types
- TSIZE["u8"] = "1"
- TSIZE["u16"] = "2"
- TSIZE["u32"] = "4"
- TSIZE["i8"] = TSIZE["u8"]
- TSIZE["i16"] = TSIZE["u8"]
- TSIZE["i32"] = TSIZE["u8"]
- TSIZE["char"] = "1"
-
# Common Regexs
- INT_REGEX = "^(0|[1-9][0-9]*),?$"
- HEX_REGEX = "^0x[A-Fa-f0-9]+,?$"
+ UINT_REGEX = "^(0|[1-9][0-9]*)$"
+ HEX_REGEX = "^(0x[A-Fa-f0-9]+)$"
+ OFF_REGEX = "^(0|[1-9][0-9]*)|^(0x[A-Fa-f0-9]+)"
+ REL_OFF_REGEX = "^\\+(0|[1-9][0-9]*)|^\\+(0x[A-Fa-f0-9]+)"
ARRAY_REGEX = "\\[(0|[1-9][0-9]*)\\]"
- TYPES_REGEX = "^(((u|i)(8|16|32))|char)("ARRAY_REGEX")?,?$"
-
- IDENT_REGEX = "^[A-Za-z_][A-Za-z0-9_]*,?$"
- SROM_OFF_REGEX = "("TYPES_REGEX"|"HEX_REGEX")"
-
- # Parser states types
- ST_STRUCT_BLOCK = "struct" # struct block
- ST_VAR_BLOCK = "var" # variable block
- ST_SROM_DEFN = "srom" # srom offset defn
- ST_NONE = "NONE" # default state
-
- # Property types
- PROP_T_SFMT = "sfmt"
- PROP_T_ALL1 = "all1"
-
- # Internal variables used for parser state
- # tracking
- STATE_TYPE = "_state_type"
- STATE_IDENT = "_state_block_name"
- STATE_LINENO = "_state_first_line"
- STATE_ISBLOCK = "_state_is_block"
-
- # Common array keys
- DEF_LINE = "def_line"
- NUM_REVS = "num_revs"
- REV = "rev"
-
- # Revision array keys
- REV_START = "rev_start"
- REV_END = "rev_end"
- REV_DESC = "rev_decl"
- REV_NUM_OFFS = "num_offs"
-
- # Offset array keys
- OFF = "off"
- OFF_NUM_SEGS = "off_num_segs"
- OFF_SEG = "off_seg"
-
- # Segment array keys
- SEG_ADDR = "seg_addr"
- SEG_COUNT = "seg_count"
- SEG_TYPE = "seg_type"
- SEG_MASK = "seg_mask"
- SEG_SHIFT = "seg_shift"
-
- # Variable array keys
- VAR_NAME = "v_name"
- VAR_TYPE = "v_type"
- VAR_BASE_TYPE = "v_base_type"
- VAR_FMT = "v_fmt"
- VAR_STRUCT = "v_parent_struct"
- VAR_PRIVATE = "v_private"
- VAR_ARRAY = "v_array"
- VAR_IGNALL1 = "v_ignall1"
+ TYPES_REGEX = "^(((u|i)(8|16|32))|char)("ARRAY_REGEX")?$"
+
+ IDENT_REGEX = "[A-Za-z_][A-Za-z0-9_]*"
+ SVAR_IDENT_REGEX = "^<"IDENT_REGEX">{?$" # <var> identifiers
+ VAR_IDENT_REGEX = "^"IDENT_REGEX"{?$" # var identifiers
+
+ VACCESS_REGEX = "^(private|internal)$"
+
+ # Property array keys
+ PROP_ID = "p_id"
+ PROP_NAME = "p_name"
+
+ # Prop path array keys
+ PPATH_HEAD = "ppath_head"
+ PPATH_TAIL = "ppath_tail"
+
+ # Object array keys
+ OBJ_IS_CLS = "o_is_cls"
+ OBJ_SUPER = "o_super"
+ OBJ_PROP = "o_prop"
+
+ # Class array keys
+ CLS_NAME = "cls_name"
+ CLS_PROP = "cls_prop"
+
+ # C SPROM binding opcodes/opcode flags
+ SPROM_OPCODE_EOF = "SPROM_OPCODE_EOF"
+ SPROM_OPCODE_NELEM = "SPROM_OPCODE_NELEM"
+ SPROM_OPCODE_VAR_END = "SPROM_OPCODE_VAR_END"
+ SPROM_OPCODE_VAR_IMM = "SPROM_OPCODE_VAR_IMM"
+ SPROM_OPCODE_VAR_REL_IMM = "SPROM_OPCODE_VAR_REL_IMM"
+ SPROM_OPCODE_VAR = "SPROM_OPCODE_VAR"
+ SPROM_OPCODE_REV_IMM = "SPROM_OPCODE_REV_IMM"
+ SPROM_OPCODE_REV_RANGE = "SPROM_OPCODE_REV_RANGE"
+ SPROM_OP_REV_START_MASK = "SPROM_OP_REV_START_MASK"
+ SPROM_OP_REV_START_SHIFT = "SPROM_OP_REV_START_SHIFT"
+ SPROM_OP_REV_END_MASK = "SPROM_OP_REV_END_MASK"
+ SPROM_OP_REV_END_SHIFT = "SPROM_OP_REV_END_SHIFT"
+ SPROM_OPCODE_MASK_IMM = "SPROM_OPCODE_MASK_IMM"
+ SPROM_OPCODE_MASK = "SPROM_OPCODE_MASK"
+ SPROM_OPCODE_SHIFT_IMM = "SPROM_OPCODE_SHIFT_IMM"
+ SPROM_OPCODE_SHIFT = "SPROM_OPCODE_SHIFT"
+ SPROM_OPCODE_OFFSET_REL_IMM = "SPROM_OPCODE_OFFSET_REL_IMM"
+ SPROM_OPCODE_OFFSET = "SPROM_OPCODE_OFFSET"
+ SPROM_OPCODE_TYPE = "SPROM_OPCODE_TYPE"
+ SPROM_OPCODE_TYPE_IMM = "SPROM_OPCODE_TYPE_IMM"
+ SPROM_OPCODE_DO_BINDN_IMM = "SPROM_OPCODE_DO_BINDN_IMM"
+ SPROM_OPCODE_DO_BIND = "SPROM_OPCODE_DO_BIND"
+ SPROM_OPCODE_DO_BINDN = "SPROM_OPCODE_DO_BINDN"
+ SPROM_OP_BIND_SKIP_IN_MASK = "SPROM_OP_BIND_SKIP_IN_MASK"
+ SPROM_OP_BIND_SKIP_IN_SHIFT = "SPROM_OP_BIND_SKIP_IN_SHIFT"
+ SPROM_OP_BIND_SKIP_IN_SIGN = "SPROM_OP_BIND_SKIP_IN_SIGN"
+ SPROM_OP_BIND_SKIP_OUT_MASK = "SPROM_OP_BIND_SKIP_OUT_MASK"
+ SPROM_OP_BIND_SKIP_OUT_SHIFT = "SPROM_OP_BIND_SKIP_OUT_SHIFT"
+
+ SPROM_OP_DATA_U8 = "SPROM_OP_DATA_U8"
+ SPROM_OP_DATA_U8_SCALED = "SPROM_OP_DATA_U8_SCALED"
+ SPROM_OP_DATA_U16 = "SPROM_OP_DATA_U16"
+ SPROM_OP_DATA_U32 = "SPROM_OP_DATA_U32"
+ SPROM_OP_DATA_I8 = "SPROM_OP_DATA_I8"
+
+ SPROM_OP_BIND_SKIP_IN_MAX = 3 # maximum SKIP_IN value
+ SPROM_OP_BIND_SKIP_IN_MIN = -3 # minimum SKIP_IN value
+ SPROM_OP_BIND_SKIP_OUT_MAX = 1 # maximum SKIP_OUT value
+ SPROM_OP_BIND_SKIP_OUT_MIN = 0 # minimum SKIP_OUT value
+ SPROM_OP_IMM_MAX = 15 # maximum immediate value
+ SPROM_OP_REV_RANGE_MAX = 15 # maximum SROM rev range value
+
+ # SPROM opcode encoding state
+ SromOpStream = class_new("SromOpStream")
+ class_add_prop(SromOpStream, p_layout, "layout")
+ class_add_prop(SromOpStream, p_revisions, "revisions")
+ class_add_prop(SromOpStream, p_vid, "vid")
+ class_add_prop(SromOpStream, p_offset, "offset")
+ class_add_prop(SromOpStream, p_type, "type")
+ class_add_prop(SromOpStream, p_nelem, "nelem")
+ class_add_prop(SromOpStream, p_mask, "mask")
+ class_add_prop(SromOpStream, p_shift, "shift")
+ class_add_prop(SromOpStream, p_bind_total, "bind_total")
+ class_add_prop(SromOpStream, p_pending_bind, "pending_bind")
+
+ # SROM pending bind operation
+ SromOpBind = class_new("SromOpBind")
+ class_add_prop(SromOpBind, p_segment, "segment")
+ class_add_prop(SromOpBind, p_count, "count")
+ class_add_prop(SromOpBind, p_offset, "offset")
+ class_add_prop(SromOpBind, p_width, "width")
+ class_add_prop(SromOpBind, p_skip_in, "skip_in")
+ class_add_prop(SromOpBind, p_skip_out, "skip_out")
+ class_add_prop(SromOpBind, p_buffer, "buffer")
+
+ # Map class definition
+ Map = class_new("Map")
+
+ # Array class definition
+ Array = class_new("Array")
+ class_add_prop(Array, p_count, "count")
+
+ # MacroType class definition
+ # Used to define a set of known macro types that may be generated
+ MacroType = class_new("MacroType")
+ class_add_prop(MacroType, p_name, "name")
+ class_add_prop(MacroType, p_const_suffix, "const_suffix")
+
+ MTypeVarName = macro_type_new("name", "") # var name
+ MTypeVarID = macro_type_new("id", "_ID") # var unique ID
+ MTypeVarMaxLen = macro_type_new("len", "_MAXLEN") # var max array length
+
+ # Preprocessor Constant
+ MacroDefine = class_new("MacroDefine")
+ class_add_prop(MacroDefine, p_name, "name")
+ class_add_prop(MacroDefine, p_value, "value")
+
+ # ParseState definition
+ ParseState = class_new("ParseState")
+ class_add_prop(ParseState, p_ctx, "ctx")
+ class_add_prop(ParseState, p_is_block, "is_block")
+ class_add_prop(ParseState, p_line, "line")
+
+ # Value Formats
+ Fmt = class_new("Fmt")
+ class_add_prop(Fmt, p_name, "name")
+ class_add_prop(Fmt, p_symbol, "const")
+
+ FmtHex = fmt_new("hex", "bhnd_nvram_val_bcm_hex_fmt")
+ FmtDec = fmt_new("decimal", "bhnd_nvram_val_bcm_decimal_fmt")
+ FmtMAC = fmt_new("macaddr", "bhnd_nvram_val_bcm_macaddr_fmt")
+ FmtLEDDC = fmt_new("leddc", "bhnd_nvram_val_bcm_leddc_fmt")
+ FmtStr = fmt_new("string", "bhnd_nvram_val_bcm_string_fmt")
+
+ ValueFormats = map_new()
+ map_set(ValueFormats, get(FmtHex, p_name), FmtHex)
+ map_set(ValueFormats, get(FmtDec, p_name), FmtDec)
+ map_set(ValueFormats, get(FmtMAC, p_name), FmtMAC)
+ map_set(ValueFormats, get(FmtLEDDC, p_name), FmtLEDDC)
+ map_set(ValueFormats, get(FmtStr, p_name), FmtStr)
+
+ # Data Types
+ Type = class_new("Type")
+ class_add_prop(Type, p_name, "name")
+ class_add_prop(Type, p_width, "width")
+ class_add_prop(Type, p_signed, "signed")
+ class_add_prop(Type, p_const, "const")
+ class_add_prop(Type, p_const_val, "const_val")
+ class_add_prop(Type, p_array_const, "array_const")
+ class_add_prop(Type, p_array_const_val, "array_const_val")
+ class_add_prop(Type, p_default_fmt, "default_fmt")
+ class_add_prop(Type, p_mask, "mask")
+
+ ArrayType = class_new("ArrayType", AST)
+ class_add_prop(ArrayType, p_type, "type")
+ class_add_prop(ArrayType, p_count, "count")
+
+ UInt8Max = 255
+ UInt16Max = 65535
+ UInt32Max = 4294967295
+ Int8Min = -128
+ Int8Max = 127
+ Int16Min = -32768
+ Int16Max = 32767
+ Int32Min = -2147483648
+ Int32Max = 2147483648
+ CharMin = Int8Min
+ CharMax = Int8Max
+
+ UInt8 = type_new("u8", 1, 0, "BHND_NVRAM_TYPE_UINT8",
+ "BHND_NVRAM_TYPE_UINT8_ARRAY", FmtHex, UInt8Max, 0, 16)
+
+ UInt16 = type_new("u16", 2, 0, "BHND_NVRAM_TYPE_UINT16",
+ "BHND_NVRAM_TYPE_UINT16_ARRAY", FmtHex, UInt16Max, 1, 17)
+
+ UInt32 = type_new("u32", 4, 0, "BHND_NVRAM_TYPE_UINT32",
+ "BHND_NVRAM_TYPE_UINT32_ARRAY", FmtHex, UInt32Max, 2, 18)
+
+ Int8 = type_new("i8", 1, 1, "BHND_NVRAM_TYPE_INT8",
+ "BHND_NVRAM_TYPE_INT8_ARRAY", FmtDec, UInt8Max, 4, 20)
+
+ Int16 = type_new("i16", 2, 1, "BHND_NVRAM_TYPE_INT16",
+ "BHND_NVRAM_TYPE_INT16_ARRAY", FmtDec, UInt16Max, 5, 21)
+
+ Int32 = type_new("i32", 4, 1, "BHND_NVRAM_TYPE_INT32",
+ "BHND_NVRAM_TYPE_INT32_ARRAY", FmtDec, UInt32Max, 6, 22)
+
+ Char = type_new("char", 1, 1, "BHND_NVRAM_TYPE_CHAR",
+ "BHND_NVRAM_TYPE_CHAR_ARRAY", FmtStr, UInt8Max, 8, 24)
+
+ BaseTypes = map_new()
+ map_set(BaseTypes, get(UInt8, p_name), UInt8)
+ map_set(BaseTypes, get(UInt16, p_name), UInt16)
+ map_set(BaseTypes, get(UInt32, p_name), UInt32)
+ map_set(BaseTypes, get(Int8, p_name), Int8)
+ map_set(BaseTypes, get(Int16, p_name), Int16)
+ map_set(BaseTypes, get(Int32, p_name), Int32)
+ map_set(BaseTypes, get(Char, p_name), Char)
+
+ BaseTypesArray = map_to_array(BaseTypes)
+ BaseTypesCount = array_size(BaseTypesArray)
+
+ # Variable Flags
+ VFlag = class_new("VFlag")
+ class_add_prop(VFlag, p_name, "name")
+ class_add_prop(VFlag, p_const, "const")
+
+ VFlagPrivate = vflag_new("private", "BHND_NVRAM_VF_MFGINT")
+ VFlagIgnoreAll1 = vflag_new("ignall1", "BHND_NVRAM_VF_IGNALL1")
+
+ # Variable Access Type Constants
+ VAccess = class_new("VAccess")
+ VAccessPublic = obj_new(VAccess) # Public
+ VAccessPrivate = obj_new(VAccess) # MFG Private
+ VAccessInternal = obj_new(VAccess) # Implementation-Internal
+
+ #
+ # AST node classes
+ #
+ AST = class_new("AST")
+ class_add_prop(AST, p_line, "line")
+
+ SymbolContext = class_new("SymbolContext", AST)
+ class_add_prop(SymbolContext, p_vars, "vars")
+
+ # NVRAM root parser context
+ NVRAM = class_new("NVRAM", SymbolContext)
+ class_add_prop(NVRAM, p_var_groups, "var_groups")
+ class_add_prop(NVRAM, p_srom_layouts, "srom_layouts")
+ class_add_prop(NVRAM, p_srom_table, "srom_table")
+
+ # Variable Group
+ VarGroup = class_new("VarGroup", SymbolContext)
+ class_add_prop(VarGroup, p_name, "name")
+
+ # Revision Range
+ RevRange = class_new("RevRange", AST)
+ class_add_prop(RevRange, p_start, "start")
+ class_add_prop(RevRange, p_end, "end")
+
+ # String Constant
+ StringConstant = class_new("StringConstant", AST)
+ class_add_prop(StringConstant, p_value, "value") # string
+ class_add_prop(StringConstant, p_continued, "continued") # bool
+
+ # Variable Declaration
+ Var = class_new("Var", AST)
+ class_add_prop(Var, p_access, "access") # VAccess
+ class_add_prop(Var, p_name, "name") # string
+ class_add_prop(Var, p_desc, "desc") # StringConstant
+ class_add_prop(Var, p_help, "help") # StringConstant
+ class_add_prop(Var, p_type, "type") # AbstractType
+ class_add_prop(Var, p_fmt, "fmt") # Fmt
+ class_add_prop(Var, p_ignall1, "ignall1") # bool
+ # ID is assigned once all variables are sorted
+ class_add_prop(Var, p_vid, "vid") # int
+
+ # Common interface inherited by parser contexts that support
+ # registration of SROM variable entries
+ SromContext = class_new("SromContext", AST)
+ class_add_prop(SromContext, p_revisions, "revisions")
+
+ # SROM Layout Node
+ SromLayout = class_new("SromLayout", SromContext)
+ class_add_prop(SromLayout, p_entries, "entries") # Array<SromEntry>
+ class_add_prop(SromLayout, p_revmap, "revmap") # Map<(string,int), SromEntry>
+ class_add_prop(SromLayout, p_output_var_counts, # Map<int, int> (rev->count)
+ "output_var_counts")
+
+ # SROM Layout Filter Node
+ # Represents a filter over a parent SromLayout's revisions
+ SromLayoutFilter = class_new("SromLayoutFilter", SromContext)
+ class_add_prop(SromLayoutFilter, p_parent, "parent")
+
+ # SROM variable entry
+ SromEntry = class_new("SromEntry", AST)
+ class_add_prop(SromEntry, p_var, "var")
+ class_add_prop(SromEntry, p_revisions, "revisions")
+ class_add_prop(SromEntry, p_base_offset, "base_offset")
+ class_add_prop(SromEntry, p_type, "type")
+ class_add_prop(SromEntry, p_offsets, "offsets")
+
+ # SROM variable offset
+ SromOffset = class_new("SromOffset", AST)
+ class_add_prop(SromOffset, p_segments, "segments")
+
+ # SROM variable offset segment
+ SromSegment = class_new("SromSegment", AST)
+ class_add_prop(SromSegment, p_offset, "offset")
+ class_add_prop(SromSegment, p_type, "type")
+ class_add_prop(SromSegment, p_mask, "mask")
+ class_add_prop(SromSegment, p_shift, "shift")
+ class_add_prop(SromSegment, p_value, "value")
+
+ # Create the parse state stack
+ _g_parse_stack_depth = 0
+ _g_parse_stack[0] = null
+
+ # Push the root parse state
+ parser_state_push(nvram_new(), 0)
}
-# return the flag definition for variable `v`
-function gen_var_flags (v)
+function at_exit(_block_start, _state, _output_vars, _noutput_vars, _name, _var,
+ _i)
{
- _num_flags = 0;
- if (vars[v,VAR_ARRAY])
- _flags[_num_flags++] = "BHND_NVRAM_VF_ARRAY"
+ # Skip completion handling if exiting from an error
+ if (_EARLY_EXIT)
+ exit 1
+
+ # Check for complete block closure
+ if (!in_parser_context(NVRAM)) {
+ _state = parser_state_get()
+ _block_start = get(_state, p_line)
+ errorx("missing '}' for block opened on line " _block_start "")
+ }
+
+ # Apply lexicographical sorting to our variable names. To support more
+ # effecient table searching, we guarantee a stable sort order (using C
+ # collation).
+ #
+ # This also has a side-effect of generating a unique monotonic ID
+ # for all variables, which we will emit as a #define and can use as a
+ # direct index into the C variable table
+ _output_vars = array_new()
+ for (_name in _g_var_names) {
+ _var = _g_var_names[_name]
+
+ # Don't include internal variables in the output
+ if (var_is_internal(_var))
+ continue
+
+ array_append(_output_vars, _var)
+ }
- if (vars[v,VAR_PRIVATE])
- _flags[_num_flags++] = "BHND_NVRAM_VF_MFGINT"
+ # Sort by variable name
+ array_sort(_output_vars, prop_to_path(p_name))
- if (vars[v,VAR_IGNALL1])
- _flags[_num_flags++] = "BHND_NVRAM_VF_IGNALL1"
-
- if (_num_flags == 0)
- _flags[_num_flags++] = "0"
+ # Set all variable ID properties to their newly assigned ID value
+ _noutput_vars = array_size(_output_vars)
+ for (_i = 0; _i < _noutput_vars; _i++) {
+ _var = array_get(_output_vars, _i)
+ set(_var, p_vid, _i)
+ }
+
+ # Truncate output file and write common header
+ printf("") > OUTPUT_FILE
+ emit("/*\n")
+ emit(" * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n")
+ emit(" *\n")
+ emit(" * generated from nvram map: " FILENAME "\n")
+ emit(" */\n")
+ emit("\n")
+
+ # Emit all variable definitions
+ if (OUT_T == OUT_T_DATA) {
+ write_data(_output_vars)
+ } else if (OUT_T == OUT_T_HEADER) {
+ write_header(_output_vars)
+ }
- return (join(_flags, "|", _num_flags))
+ printf("%u variable records written to %s\n", array_size(_output_vars),
+ OUTPUT_FILE) >> "/dev/stderr"
}
-# emit the bhnd_sprom_offsets for a given variable revision key
-function emit_var_sprom_offsets (v, revk)
+# Write the public header (output type HEADER)
+function write_header(output_vars, _noutput_vars, _var,
+ _tab_align, _macro, _macros, _num_macros, _i)
{
- emit(sprintf("{{%u, %u}, (struct bhnd_sprom_offset[]) {\n",
- vars[revk,REV_START],
- vars[revk,REV_END]))
- output_depth++
+ # Produce our array of #defines
+ _num_macros = 0
+ _noutput_vars = array_size(output_vars)
+ for (_i = 0; _i < _noutput_vars; _i++) {
+ _var = array_get(output_vars, _i)
+
+ # Variable name
+ _macro = var_get_macro(_var, MTypeVarName, \
+ "\"" get(_var, p_name) "\"")
+ _macros[_num_macros++] = _macro
+
+ # Variable array length
+ if (var_has_array_type(_var)) {
+ _macro = var_get_macro(_var, MTypeVarMaxLen,
+ var_get_array_len(_var))
+ _macros[_num_macros++] = _macro
+ }
+ }
+
+ # Calculate value tab alignment position for our macros
+ _tab_align = macros_get_tab_alignment(_macros, _num_macros)
- num_offs = vars[revk,REV_NUM_OFFS]
- num_offs_written = 0
- elem_count = 0
- for (offset = 0; offset < num_offs; offset++) {
- offk = subkey(revk, OFF, offset"")
- num_segs = vars[offk,OFF_NUM_SEGS]
+ # Write the macros
+ for (_i = 0; _i < _num_macros; _i++)
+ write_macro_define(_macros[_i], _tab_align)
+}
- for (seg = 0; seg < num_segs; seg++) {
- segk = subkey(offk, OFF_SEG, seg"")
+# Write the private data header (output type DATA)
+function write_data(output_vars, _noutput_vars, _var, _nvram, _layouts,
+ _nlayouts, _layout, _revs, _rev, _rev_start, _rev_end, _base_type,
+ _srom_table, _nsrom_table, _i, _j)
+{
+ _nvram = parser_state_get_context(NVRAM)
+ _layouts = get(_nvram, p_srom_layouts)
+ _nlayouts = array_size(_layouts)
- for (seg_n = 0; seg_n < vars[segk,SEG_COUNT]; seg_n++) {
- seg_addr = vars[segk,SEG_ADDR]
- seg_addr += TSIZE[vars[segk,SEG_TYPE]] * seg_n
+ _noutput_vars = array_size(output_vars)
- emit(sprintf("{%s, %s, %s, %s, %s},\n",
- seg_addr,
- (seg > 0) ? "true" : "false",
- DTYPE[vars[segk,SEG_TYPE]],
- vars[segk,SEG_SHIFT],
- vars[segk,SEG_MASK]))
+ # Write all our private NVAR_ID defines
+ write_data_defines(output_vars)
- num_offs_written++
- }
- }
+ # Write all layout binding opcodes, and build an array
+ # mapping SROM revision to corresponding SROM layout
+ _srom_table = array_new()
+ for (_i = 0; _i < _nlayouts; _i++) {
+ _layout = array_get(_layouts, _i)
+
+ # Write binding opcode table to our output file
+ write_srom_bindings(_layout)
+
+ # Add entries to _srom_table for all covered revisions
+ _revs = get(_layout, p_revisions)
+ _rev_start = get(_revs, p_start)
+ _rev_end = get(_revs, p_end)
+
+ for (_j = _rev_start; _j <= _rev_end; _j++)
+ array_append(_srom_table, _j)
+ }
+
+ # Sort in ascending order, by SROM revision
+ array_sort(_srom_table)
+ _nsrom_table = array_size(_srom_table)
+
+ # Write the variable definitions
+ emit("/* Variable definitions */\n")
+ emit("const struct bhnd_nvram_vardefn " \
+ "bhnd_nvram_vardefns[] = {\n")
+ output_depth++
+ for (_i = 0; _i < _noutput_vars; _i++) {
+ write_data_nvram_vardefn(array_get(output_vars, _i))
+ }
+ output_depth--
+ emit("};\n")
+ emit("const size_t bhnd_nvram_num_vardefns = " _noutput_vars ";\n")
+
+ # Write static asserts for raw type constant values that must be kept
+ # synchronized with the code
+ for (_i = 0; _i < BaseTypesCount; _i++) {
+ _base_type = array_get(BaseTypesArray, _i)
+
+ emit(sprintf("_Static_assert(%s == %u, \"%s\");\n",
+ type_get_const(_base_type), type_get_const_val(_base_type),
+ "type constant out of sync"))
+
+ emit(sprintf("_Static_assert(%s == %u, \"%s\");\n",
+ get(_base_type, p_array_const),
+ get(_base_type, p_array_const_val),
+ "array type constant out of sync"))
+ }
+
+ # Write all top-level bhnd_sprom_layout entries
+ emit("/* SPROM layouts */\n")
+ emit("const struct bhnd_sprom_layout bhnd_sprom_layouts[] = {\n")
+ output_depth++
+ for (_i = 0; _i < _nsrom_table; _i++) {
+ _rev = array_get(_srom_table, _i)
+ _layout = nvram_get_srom_layout(_nvram, _rev)
+ write_data_srom_layout(_layout, _rev)
}
+ output_depth--
+ emit("};\n")
+ emit("const size_t bhnd_sprom_num_layouts = " _nsrom_table ";\n")
+}
+
+# Write a bhnd_nvram_vardef entry for the given variable
+function write_data_nvram_vardefn(v, _desc, _help, _type, _fmt) {
+ obj_assert_class(v, Var)
+
+ _desc = get(v, p_desc)
+ _help = get(v, p_help)
+ _type = get(v, p_type)
+ _fmt = var_get_fmt(v)
+
+ emit("{\n")
+ output_depth++
+ emit(sprintf(".name = \"%s\",\n", get(v, p_name)))
+
+ if (_desc != null)
+ emit(sprintf(".desc = \"%s\",\n", get(_desc, p_value)))
+ else
+ emit(".desc = NULL,\n")
+
+ if (_help != null)
+ emit(sprintf(".help = \"%s\",\n", get(_help, p_value)))
+ else
+ emit(".help = NULL,\n")
+
+ emit(".type = " type_get_const(_type) ",\n")
+ emit(".nelem = " var_get_array_len(v) ",\n")
+ emit(".fmt = &" get(_fmt, p_symbol) ",\n")
+ emit(".flags = " gen_var_flags(v) ",\n")
output_depth--
- emit("}, " num_offs_written "},\n")
+ emit("},\n")
}
-# emit a bhnd_nvram_vardef for variable name `v`
-function emit_nvram_vardef (v)
+# Write a top-level bhnd_sprom_layout entry for the given revision
+# and layout definition
+function write_data_srom_layout(layout, revision, _flags, _size,
+ _sromcrc, _crc_seg,
+ _sromsig, _sig_seg, _sig_offset, _sig_value,
+ _sromrev, _rev_seg, _rev_off,
+ _num_vars)
{
- emit(sprintf("{\"%s\", %s, %s, %s, (struct bhnd_sprom_vardefn[]) {\n",
- v suffix,
- DTYPE[vars[v,VAR_BASE_TYPE]],
- FMT[vars[v,VAR_FMT]],
- gen_var_flags(v)))
+ _flags = array_new()
+
+ # Calculate the size; it always follows the internal CRC variable
+ _sromcrc = srom_layout_find_entry(layout, "<sromcrc>", revision)
+ if (_sromcrc == null) {
+ errorx("missing '<sromcrc>' entry for '"revision"' layout, " \
+ "cannot compute total size")
+ } else {
+ _crc_seg = srom_entry_get_single_segment(_sromcrc)
+ _size = get(_crc_seg, p_offset)
+ _size += get(get(_crc_seg, p_type), p_width)
+ }
+
+ # Fetch signature definition
+ _sromsig = srom_layout_find_entry(layout, "<sromsig>", revision)
+ if (_sromsig == null) {
+ array_append(_flags, "SPROM_LAYOUT_MAGIC_NONE")
+ } else {
+ _sig_seg = srom_entry_get_single_segment(_sromsig)
+
+ _sig_offset = get(_sig_seg, p_offset)
+ _sig_value = get(_sig_seg, p_value)
+ if (_sig_value == "")
+ errorc(get(_sromsig, p_line), "missing signature value")
+ }
+
+ # Fetch sromrev definition
+ _sromrev = srom_layout_find_entry(layout, "sromrev", revision)
+ if (_sromrev == null) {
+ errorx("missing 'sromrev' entry for '"revision"' layout, " \
+ "cannot determine offset")
+ } else {
+ # Must be a u8 value
+ if (!type_equal(get(_sromrev, p_type), UInt8)) {
+ errorx("'sromrev' entry has non-u8 type '" \
+ type_to_string(get(_sromrev, p_type)))
+ }
+
+ _rev_seg = srom_entry_get_single_segment(_sromrev)
+ _rev_off = get(_rev_seg, p_offset)
+ }
+
+ # Write layout entry
+ emit("{\n")
output_depth++
+ emit(".size = "_size",\n")
+ emit(".rev = "revision",\n")
+
+ if (array_size(_flags) > 0) {
+ emit(".flags = " array_join(_flags, "|") ",\n")
+ } else {
+ emit(".flags = 0,\n")
+ }
+
+ emit(".srev_offset = " _rev_off ",\n")
- for (rev = 0; rev < vars[v,NUM_REVS]; rev++) {
- revk = subkey(v, REV, rev"")
- emit_var_sprom_offsets(v, revk)
+ if (_sromsig != null) {
+ emit(".magic_offset = " _sig_offset ",\n")
+ emit(".magic_value = " _sig_value ",\n")
+ } else {
+ emit(".magic_offset = 0,\n")
+ emit(".magic_value = 0,\n")
}
+ emit(".bindings = " srom_layout_get_variable_name(layout) ",\n")
+ emit(".bindings_size = nitems(" \
+ srom_layout_get_variable_name(layout) "),\n")
+
+ emit(".num_vars = " srom_layout_num_output_vars(layout, revision) ",\n")
+
+ obj_delete(_flags)
+
output_depth--
- emit("}, " vars[v,NUM_REVS] "},\n")
+ emit("},\n");
+}
+
+# Create a new opstream encoding state instance for the given layout
+function srom_ops_new(layout, _obj) {
+ obj_assert_class(layout, SromLayout)
+
+ _obj = obj_new(SromOpStream)
+ set(_obj, p_layout, layout)
+ set(_obj, p_revisions, get(layout, p_revisions))
+ set(_obj, p_vid, 0)
+ set(_obj, p_offset, 0)
+ set(_obj, p_type, null)
+ set(_obj, p_mask, null)
+ set(_obj, p_shift, null)
+
+ return (_obj)
}
-# emit a header name #define for variable `v`
-function emit_var_namedef (v)
+# Return the current type width, or throw an error if no type is currently
+# specified.
+function srom_ops_get_type_width(opstream, _type)
{
- emit("#define\tBHND_NVAR_" toupper(v) "\t\"" v "\"\n")
+ obj_assert_class(opstream, SromOpStream)
+
+ _type = get(opstream, p_type)
+ if (_type == null)
+ errorx("no type value set")
+
+ return (get(type_get_base(_type), p_width))
+}
+
+# Write a string to the SROM opcode stream, either buffering the write,
+# or emitting it directly.
+function srom_ops_emit(opstream, string, _pending_bind, _buffer) {
+ obj_assert_class(opstream, SromOpStream)
+
+ # Buffered?
+ if ((_pending_bind = get(opstream, p_pending_bind)) != null) {
+ _buffer = get(_pending_bind, p_buffer)
+ array_append(_buffer, string)
+ return
+ }
+
+ # Emit directly
+ emit(string)
}
-# generate a set of var offset definitions for struct variable `st_vid`
-function gen_struct_var_offsets (vid, revk, st_vid, st_revk, base_addr)
+# Emit a SROM opcode followed by up to four optional bytes
+function srom_ops_emit_opcode(opstream, opcode, arg0, arg1, arg2, arg3) {
+ obj_assert_class(opstream, SromOpStream)
+
+ srom_ops_emit(opstream, opcode",\n")
+ if (arg0 != "") srom_ops_emit(opstream, arg0",\n")
+ if (arg1 != "") srom_ops_emit(opstream, arg1",\n")
+ if (arg2 != "") srom_ops_emit(opstream, arg2",\n")
+ if (arg3 != "") srom_ops_emit(opstream, arg3",\n")
+}
+
+# Emit a SROM opcode and associated integer value, choosing the best
+# SROM_OP_DATA variant for encoding the value.
+#
+# opc: The standard opcode for non-IMM encoded data, or null if none
+# opc_imm: The IMM opcode, or null if none
+# value: The value to encode
+# svalue: Symbolic representation of value to include in output, or null
+function srom_ops_emit_int_opcode(opstream, opc, opc_imm, value, svalue,
+ _width, _offset, _delta)
{
- # Copy all offsets to the new variable
- for (offset = 0; offset < vars[v,REV_NUM_OFFS]; offset++) {
- st_offk = subkey(st_revk, OFF, offset"")
- offk = subkey(revk, OFF, offset"")
-
- # Copy all segments to the new variable, applying base
- # address adjustment
- num_segs = vars[st_offk,OFF_NUM_SEGS]
- vars[offk,OFF_NUM_SEGS] = num_segs
-
- for (seg = 0; seg < num_segs; seg++) {
- st_segk = subkey(st_offk, OFF_SEG, seg"")
- segk = subkey(offk, OFF_SEG, seg"")
-
- vars[segk,SEG_ADDR] = vars[st_segk,SEG_ADDR] + \
- base_addr""
- vars[segk,SEG_COUNT] = vars[st_segk,SEG_COUNT]
- vars[segk,SEG_TYPE] = vars[st_segk,SEG_TYPE]
- vars[segk,SEG_MASK] = vars[st_segk,SEG_MASK]
- vars[segk,SEG_SHIFT] = vars[st_segk,SEG_SHIFT]
+ obj_assert_class(opstream, SromOpStream)
+
+ # Fetch current type width
+ _width = srom_ops_get_type_width(opstream)
+
+ # Special cases:
+ if (opc_imm == SPROM_OPCODE_SHIFT_IMM) {
+ # SHIFT_IMM -- the imm value must be positive and divisible by
+ # two (shift/2) to use the IMM form.
+ if (value >= 0 && value % 2 == 0) {
+ value = (value/2)
+ opc = null
+ } else {
+ opc_imm = null
+ }
+ } else if (opc_imm == SPROM_OPCODE_OFFSET_REL_IMM) {
+ # OFFSET_REL_IMM -- the imm value must be positive, divisible
+ # by the type width, and relative to the last offset to use
+ # the IMM form.
+
+ # Assert that callers correctly flushed any pending bind before
+ # attempting to set a relative offset
+ if (get(opstream, p_pending_bind) != null)
+ errorx("can't set relative offset with a pending bind")
+
+ # Fetch current offset, calculate relative value and determine
+ # whether we can issue an IMM opcode
+ _offset = get(opstream, p_offset)
+ _delta = value - _offset
+ if (_delta >= 0 &&
+ _delta % _width == 0 &&
+ (_delta/_width) <= SPROM_OP_IMM_MAX)
+ {
+ srom_ops_emit(opstream,
+ sprintf("/* %#x + %#x -> %#x */\n", _offset,
+ _delta, value))
+ value = (_delta / _width)
+ opc = null
+ } else {
+ opc_imm = null
}
}
+
+ # If no symbolic representation provided, write the raw value
+ if (svalue == null)
+ svalue = value
+
+ # Try to encode as IMM value?
+ if (opc_imm != null && value >= 0 && value <= SPROM_OP_IMM_MAX) {
+ srom_ops_emit_opcode(opstream, "("opc_imm"|"svalue")")
+ return
+ }
+
+ # Can't encode as immediate; do we have a non-immediate form?
+ if (opc == null)
+ errorx("can't encode '" value "' as immediate, and no " \
+ "non-immediate form was provided")
+
+ # Determine and emit minimal encoding
+ # We let the C compiler perform the bit operations, rather than
+ # trying to wrestle awk's floating point arithmetic
+ if (value < 0) {
+ # Only Int8 is used
+ if (value < Int8Min)
+ errorx("cannot int8 encode '" value "'")
+
+ srom_ops_emit_opcode(opstream,
+ "("opc"|"SPROM_OP_DATA_I8")", svalue)
+
+ } else if (value <= UInt8Max) {
+
+ srom_ops_emit_opcode(opstream,
+ "("opc"|"SPROM_OP_DATA_U8")", svalue)
+
+ } else if (value % _width == 0 && (value / _width) <= UInt8Max) {
+
+ srom_ops_emit_opcode(opstream,
+ "("opc"|"SPROM_OP_DATA_U8_SCALED")", svalue / _width)
+
+ } else if (value <= UInt16Max) {
+
+ srom_ops_emit_opcode(opstream,
+ "("opc"|"SPROM_OP_DATA_U16")",
+ "("svalue" & 0xFF)",
+ "("svalue" >> 8)")
+
+ } else if (value <= UInt32Max) {
+
+ srom_ops_emit_opcode(opstream,
+ "("opc"|"SPROM_OP_DATA_U32")",
+ "("svalue" & 0xFF)",
+ "(("svalue" >> 8) & 0xFF)",
+ "(("svalue" >> 16) & 0xFF)",
+ "(("svalue" >> 24) & 0xFF)")
+
+ } else {
+ errorx("can't encode '" value "' (too large)")
+ }
}
-# generate a complete set of variable definitions for struct variable `st_vid`.
-function gen_struct_vars (st_vid)
+# Emit initial OPCODE_VAR opcode and update opstream state
+function srom_ops_reset_var(opstream, var, _vid_prev, _vid, _vid_name,
+ _type, _base_type)
{
- st = vars[st_vid,VAR_STRUCT]
- st_max_off = 0
-
- # determine the total number of variables to generate
- for (st_rev = 0; st_rev < structs[st,NUM_REVS]; st_rev++) {
- srevk = subkey(st, REV, st_rev"")
- for (off = 0; off < structs[srevk,REV_NUM_OFFS]; off++) {
- if (off > st_max_off)
- st_max_off = off
- }
+ obj_assert_class(opstream, SromOpStream)
+ obj_assert_class(var, Var)
+
+ # Flush any pending bind for the previous variable
+ srom_ops_flush_bind(opstream, 1)
+
+ # Fetch current state
+ _vid_prev = get(opstream, p_vid)
+
+ _vid = get(var, p_vid)
+ _vid_name = var_get_macro_name(var, MTypeVarID)
+
+ # Update state
+ _type = get(var, p_type)
+ set(opstream, p_vid, _vid)
+ set(opstream, p_type, type_get_base(_type))
+ set(opstream, p_nelem, var_get_array_len(var))
+ set(opstream, p_mask, type_get_default_mask(_type))
+ set(opstream, p_shift, 0)
+ set(opstream, p_bind_total, 0)
+
+ # Always provide a human readable comment
+ srom_ops_emit(opstream, sprintf("/* %s (%#x) */\n", get(var, p_name),
+ get(opstream, p_offset)))
+
+ # Prefer a single VAR_IMM byte
+ if (_vid_prev == 0 || _vid <= SPROM_OP_IMM_MAX) {
+ srom_ops_emit_int_opcode(opstream,
+ null, SPROM_OPCODE_VAR_IMM,
+ _vid, _vid_name)
+ return
}
- # generate variable records for each defined struct offset
- for (off = 0; off < st_max_off; off++) {
- # Construct basic variable definition
- v = st_vid off""
- vars[v,VAR_TYPE] = vars[st_vid,VAR_TYPE]
- vars[v,VAR_BASE_TYPE] = vars[st_vid,VAR_BASE_TYPE]
- vars[v,VAR_FMT] = vars[st_vid,VAR_FMT]
- vars[v,VAR_PRIVATE] = vars[st_vid,VAR_PRIVATE]
- vars[v,VAR_ARRAY] = vars[st_vid,VAR_ARRAY]
- vars[v,VAR_IGNALL1] = vars[st_vid,VAR_IGNALL1]
- vars[v,NUM_REVS] = 0
-
- # Add to output variable list
- output_vars[num_output_vars++] = v
-
- # Construct revision / offset entries
- for (srev = 0; srev < structs[st,NUM_REVS]; srev++) {
- # Struct revision key
- st_revk = subkey(st, REV, srev"")
-
- # Skip offsets not defined for this revision
- if (off > structs[st_revk,REV_NUM_OFFS])
- continue
-
- # Strut offset key and associated base address */
- offk = subkey(st_revk, OFF, off"")
- base_addr = structs[offk,SEG_ADDR]
-
- for (vrev = 0; vrev < vars[st_vid,NUM_REVS]; vrev++) {
- st_var_revk = subkey(st_vid, REV, vrev"")
- v_start = vars[st_var_revk,REV_START]
- v_end = vars[st_var_revk,REV_END]
- s_start = structs[st_revk,REV_START]
- s_end = structs[st_revk,REV_END]
-
- # We don't support computing the union
- # of partially overlapping ranges
- if ((v_start < s_start && v_end >= s_start) ||
- (v_start <= s_end && v_end > s_end))
- {
- errorx("partially overlapping " \
- "revision ranges are not supported")
- }
-
- # skip variables revs that are not within
- # the struct offset's compatibility range
- if (v_start < s_start || v_start > s_end ||
- v_end < s_start || v_end > s_end)
- continue
-
- # Generate the new revision record
- rev = vars[v,NUM_REVS] ""
- revk = subkey(v, REV, rev)
- vars[v,NUM_REVS]++
-
- vars[revk,DEF_LINE] = vars[st_revk,DEF_LINE]
- vars[revk,REV_START] = v_start
- vars[revk,REV_END] = v_end
- vars[revk,REV_NUM_OFFS] = \
- vars[st_var_revk,REV_NUM_OFFS]
-
- gen_struct_var_offsets(v, revk, st_vid, st_revk,
- base_addr)
- }
- }
+ # Try encoding as a single VAR_REL_IMM byte
+ if (_vid_prev <= _vid && (_vid - _vid_prev) <= SPROM_OP_IMM_MAX) {
+ srom_ops_emit_int_opcode(opstream,
+ null, SPROM_OPCODE_VAR_REL_IMM,
+ _vid - _vid_prev, null)
+ return
}
+
+ # Fall back on a multibyte encoding
+ srom_ops_emit_int_opcode(opstream, SPROM_OPCODE_VAR, null, _vid,
+ _vid_name)
}
+# Emit OPCODE_REV/OPCODE_REV_RANGE (if necessary) for a new revision range
+function srom_ops_emit_revisions(opstream, revisions, _prev_revs,
+ _start, _end)
+{
+ obj_assert_class(opstream, SromOpStream)
+ _prev_revs = get(opstream, p_revisions)
+
+ if (revrange_equal(_prev_revs, revisions))
+ return;
-END {
- # Skip completion handling if exiting from an error
- if (_EARLY_EXIT)
- exit 1
+ # Update stream state
+ set(opstream, p_revisions, revisions)
- # Check for complete block closure
- if (depth > 0) {
- block_start = g(STATE_LINENO)
- errorx("missing '}' for block opened on line " block_start "")
+ _start = get(revisions, p_start)
+ _end = get(revisions, p_end)
+
+ # Sanity-check range values
+ if (_start < 0 || _end < 0)
+ errorx("invalid range: " revrange_to_string(revisions))
+
+ # If range covers a single revision, and can be encoded within
+ # SROM_OP_IMM_MAX, we can use the single byte encoding
+ if (_start == _end && _start <= SPROM_OP_IMM_MAX) {
+ srom_ops_emit_int_opcode(opstream,
+ null, SPROM_OPCODE_REV_IMM, _start)
+ return
}
- # Generate concrete variable definitions for all struct variables
- for (v in var_names) {
- if (vars[v,VAR_STRUCT] != null) {
- gen_struct_vars(v)
- } else {
- output_vars[num_output_vars++] = v
- }
+ # Otherwise, we need to use the two byte range encoding
+ if (_start > SPROM_OP_REV_RANGE_MAX || _end > SPROM_OP_REV_RANGE_MAX) {
+ errorx(sprintf("cannot encode range values %s (>= %u)",
+ revrange_to_string(revisions), SPROM_OP_REV_RANGE_MAX))
}
- # Apply lexicographical sorting. To support more effecient table
- # searching, we guarantee a stable sort order (using C collation).
- sort(output_vars)
+ srom_ops_emit_opcode(opstream,
+ SPROM_OPCODE_REV_RANGE,
+ sprintf("(%u << %s) | (%u << %s)",
+ _start, SPROM_OP_REV_START_SHIFT,
+ _end, SPROM_OP_REV_END_SHIFT))
+}
- # Truncate output file and write common header
- printf("") > OUTPUT_FILE
- emit("/*\n")
- emit(" * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n")
- emit(" *\n")
- emit(" * generated from nvram map: " FILENAME "\n")
- emit(" */\n")
- emit("\n")
+# Emit OPCODE_OFFSET (if necessary) for a new offset
+function srom_ops_emit_offset(opstream, offset, _prev_offset, _rel_offset,
+ _bind)
+{
+ obj_assert_class(opstream, SromOpStream)
- # Emit all variable definitions
- if (OUT_T == OUT_T_DATA) {
- emit("#include <dev/bhnd/nvram/bhnd_nvram_common.h>\n")
- emit("static const struct bhnd_nvram_vardefn "\
- "bhnd_nvram_vardefs[] = {\n")
- output_depth++
- for (i = 0; i < num_output_vars; i++)
- emit_nvram_vardef(output_vars[i])
- output_depth--
- emit("};\n")
- } else if (OUT_T == OUT_T_HEADER) {
- for (i = 0; i < num_output_vars; i++)
- emit_var_namedef(output_vars[i])
+ # Flush any pending bind before adjusting the offset
+ srom_ops_flush_bind(opstream, 0)
+
+ # Fetch current offset
+ _prev_offset = get(opstream, p_offset)
+ if (_prev_offset == offset)
+ return
+
+ # Encode (possibly a relative, 1-byte form) of the offset opcode
+ srom_ops_emit_int_opcode(opstream, SPROM_OPCODE_OFFSET,
+ SPROM_OPCODE_OFFSET_REL_IMM, offset, null)
+
+ # Update state
+ set(opstream, p_offset, offset)
+}
+
+# Emit OPCODE_TYPE (if necessary) for a new type value; this also
+# resets the mask to the type default.
+function srom_ops_emit_type(opstream, type, _base_type, _prev_type, _prev_mask,
+ _default_mask)
+{
+ obj_assert_class(opstream, SromOpStream)
+ if (!obj_is_instanceof(type, ArrayType))
+ obj_assert_class(type, Type)
+
+ _default_mask = type_get_default_mask(type)
+ _base_type = type_get_base(type)
+
+ # If state already matches the requested type, nothing to be
+ # done
+ _prev_type = get(opstream, p_type)
+ _prev_mask = get(opstream, p_mask)
+ if (type_equal(_prev_type, _base_type) && _prev_mask == _default_mask)
+ return
+
+ # Update state
+ set(opstream, p_type, _base_type)
+ set(opstream, p_mask, _default_mask)
+
+ # Emit opcode.
+ if (type_get_const_val(_base_type) <= SPROM_OP_IMM_MAX) {
+ # Single byte IMM encoding
+ srom_ops_emit_opcode(opstream,
+ SPROM_OPCODE_TYPE_IMM "|" type_get_const(_base_type))
+ } else {
+ # Two byte encoding
+ srom_ops_emit_opcode(opstream, SPROM_OPCODE_TYPE,
+ type_get_const(_base_type))
}
+}
- printf("%u variable records written to %s\n", num_output_vars,
- OUTPUT_FILE) >> "/dev/stderr"
+# Emit OPCODE_MASK (if necessary) for a new mask value
+function srom_ops_emit_mask(opstream, mask, _prev_mask) {
+ obj_assert_class(opstream, SromOpStream)
+ _prev_mask = get(opstream, p_mask)
+
+ if (_prev_mask == mask)
+ return
+
+ set(opstream, p_mask, mask)
+ srom_ops_emit_int_opcode(opstream,
+ SPROM_OPCODE_MASK, SPROM_OPCODE_MASK_IMM,
+ mask, sprintf("0x%x", mask))
}
+# Emit OPCODE_SHIFT (if necessary) for a new shift value
+function srom_ops_emit_shift(opstream, shift, _prev_shift) {
+ obj_assert_class(opstream, SromOpStream)
+ _prev_shift = get(opstream, p_shift)
-#
-# Print usage
-#
-function usage ()
+ if (_prev_shift == shift)
+ return
+
+ set(opstream, p_shift, shift)
+ srom_ops_emit_int_opcode(opstream,
+ SPROM_OPCODE_SHIFT, SPROM_OPCODE_SHIFT_IMM,
+ shift, null)
+}
+
+# Return true if a valid BIND/BINDN encoding exists for the given SKIP_IN
+# value, false if the skip values exceed the limits of the bind opcode
+# family.
+function srom_ops_can_encode_skip_in(skip_in) {
+ return (skip_in >= SPROM_OP_BIND_SKIP_IN_MIN &&
+ skip_in <= SPROM_OP_BIND_SKIP_IN_MAX)
+}
+
+# Return true if a valid BIND/BINDN encoding exists for the given SKIP_OUT
+# value, false if the skip values exceed the limits of the bind opcode
+# family.
+function srom_ops_can_encode_skip_out(skip_out) {
+ return (skip_in >= SPROM_OP_BIND_SKIP_IN_MIN &&
+ skip_in <= SPROM_OP_BIND_SKIP_IN_MAX)
+}
+
+# Return true if a valid BIND/BINDN encoding exists for the given skip
+# values, false if the skip values exceed the limits of the bind opcode
+# family.
+function srom_ops_can_encode_skip(skip_in, skip_out) {
+ return (srom_ops_can_encode_skip_in(skip_in) &&
+ srom_ops_can_encode_skip_out(skip_out))
+}
+
+# Create a new SromOpBind instance for the given segment
+function srom_opbind_new(segment, skip_in, skip_out, _obj, _type, _width,
+ _offset)
{
- print "usage: bhnd_nvram_map.awk <input map> [-hd] [-o output file]"
- _EARLY_EXIT = 1
- exit 1
+ obj_assert_class(segment, SromSegment)
+
+ # Verify that an encoding exists for the skip values
+ if (!srom_ops_can_encode_skip_in(skip_in)) {
+ errorx(sprintf("cannot encode SKIP_IN=%d; maximum supported " \
+ "range %d-%d", skip_in,
+ SPROM_OP_BIND_SKIP_IN_MIN, SPROM_OP_BIND_SKIP_IN_MAX))
+ }
+
+ if (!srom_ops_can_encode_skip_out(skip_out)) {
+ errorx(sprintf("cannot encode SKIP_OUT=%d; maximum supported " \
+ "range %d-%d", skip_out,
+ SPROM_OP_BIND_SKIP_OUT_MIN, SPROM_OP_BIND_SKIP_OUT_MAX))
+ }
+
+ # Fetch basic segment info
+ _offset = get(segment, p_offset)
+ _type = srom_segment_get_base_type(segment)
+ _width = get(_type, p_width)
+
+ # Construct new instance
+ _obj = obj_new(SromOpBind)
+
+ set(_obj, p_segment, segment)
+ set(_obj, p_count, 1)
+ set(_obj, p_offset, _offset)
+ set(_obj, p_width, _width)
+ set(_obj, p_skip_in, skip_in)
+ set(_obj, p_skip_out, skip_out)
+ set(_obj, p_buffer, array_new())
+
+ return (_obj)
+}
+
+# Try to coalesce a BIND for the given segment with an existing bind request,
+# returning true on success, or false if the two segments cannot be coalesced
+# into the existing request
+function srom_opbind_append(bind, segment, skip_out, _bind_seg, _bind_off,
+ _width, _count, _skip_in, _seg_offset, _delta)
+{
+ obj_assert_class(bind, SromOpBind)
+ obj_assert_class(segment, SromSegment)
+
+ # Are the segments compatible?
+ _bind_seg = get(bind, p_segment)
+ if (!srom_segment_attributes_equal(_bind_seg, segment))
+ return (0)
+
+ # Are the output skip values compatible?
+ if (get(bind, p_skip_out) != skip_out)
+ return (0)
+
+ # Find bind offset/count/width/skip
+ _bind_off = get(bind, p_offset)
+ _count = get(bind, p_count)
+ _skip_in = get(bind, p_skip_in)
+ _width = get(bind, p_width)
+
+ # Fetch new segment's offset
+ _seg_offset = get(segment, p_offset)
+
+ # If there's only one segment in the bind op, we ned to compute the
+ # skip value to be used for all later segments (including the
+ # segment we're attempting to append)
+ #
+ # If there's already multiple segments, we just need to verify that
+ # the bind_offset + (count * width * skip_in) produces the new
+ # segment's offset
+ if (_count == 1) {
+ # Determine the delta between the two segment offsets. This
+ # must be a multiple of the type width to be encoded
+ # as a BINDN entry
+ _delta = _seg_offset - _bind_off
+ if ((_delta % _width) != 0)
+ return (0)
+
+ # The skip byte count is calculated as (type width * skip)
+ _skip_in = _delta / _width
+
+ # Is the skip encodable?
+ if (!srom_ops_can_encode_skip_in(_skip_in))
+ return (0)
+
+ # Save required skip
+ set(bind, p_skip_in, _skip_in)
+ } else if (_count > 1) {
+ # Get the final offset of the binding if we were to add
+ # one additional segment
+ _bind_off = _bind_off + (_width * _skip_in * (_count + 1))
+
+ # If it doesn't match our segment's offset, we can't
+ # append this segment
+ if (_bind_off != _seg_offset)
+ return (0)
+ }
+
+ # Success! Increment the bind count in the existing bind
+ set(bind, p_count, _count + 1)
+ return (1)
}
+# Return true if the given binding operation can be omitted from the output
+# if it would be immediately followed by a VAR, VAR_REL_IMM, or EOF opcode.
#
-# Join all array elements with the given separator
+# The bind operatin must be configured with default count, skip_in, and
+# skip_out values of 1, and must contain no buffered post-BIND opcodes
+function srom_opbind_is_implicit_encodable(bind) {
+ obj_assert_class(bind, SromOpBind)
+
+ if (get(bind, p_count) != 1)
+ return (0)
+
+ if (get(bind, p_skip_in) != 1)
+ return (0)
+
+ if (get(bind, p_skip_out) != 1)
+ return (0)
+
+ if (array_size(get(bind, p_buffer)) != 0)
+ return (0)
+
+ return (1)
+}
+
+
+# Encode all segment settings for a single offset segment, followed by a bind
+# request.
#
-function join (array, sep, count)
+# opstream: Opcode stream
+# segment: Segment to be written
+# continued: If this segment's value should be OR'd with the value of a
+# following segment
+function srom_ops_emit_segment(opstream, segment, continued, _value,
+ _bind, _skip_in, _skip_out)
{
- if (count == 0)
- return ("")
+ obj_assert_class(opstream, SromOpStream)
+ obj_assert_class(segment, SromSegment)
- _result = array[0]
- for (_ji = 1; _ji < count; _ji++)
- _result = _result sep array[_ji]
+ # Determine basic bind parameters
+ _count = 1
+ _skip_in = 1
+ _skip_out = continued ? 0 : 1
- return (_result)
+ # Try to coalesce with a pending binding
+ if ((_bind = get(opstream, p_pending_bind)) != null) {
+ if (srom_opbind_append(_bind, segment, _skip_out))
+ return
+ }
+
+ # Otherwise, flush any pending bind and enqueue our own
+ srom_ops_flush_bind(opstream, 0)
+ if (get(opstream, p_pending_bind))
+ errorx("bind not flushed!")
+
+ # Encode type
+ _value = get(segment, p_type)
+ srom_ops_emit_type(opstream, _value)
+
+ # Encode offset
+ _value = get(segment, p_offset)
+ srom_ops_emit_offset(opstream, _value)
+
+ # Encode mask
+ _value = get(segment, p_mask)
+ srom_ops_emit_mask(opstream, _value)
+
+ # Encode shift
+ _value = get(segment, p_shift)
+ srom_ops_emit_shift(opstream, _value)
+
+ # Enqueue binding with opstream
+ _bind = srom_opbind_new(segment, _skip_in, _skip_out)
+ set(opstream, p_pending_bind, _bind)
}
-#
-# Sort a contiguous integer-indexed array, using standard awk comparison
-# operators over its values.
-#
-function sort (array) {
- # determine array size
- _sort_alen = 0
+# (private) Adjust the stream's input offset by applying the given bind
+# operation's skip_in * width * count.
+function _srom_ops_apply_bind_offset(opstream, bind, _count, _offset, _width,
+ _skip_in, _opstream_offset)
+{
+ obj_assert_class(opstream, SromOpStream)
+ obj_assert_class(bind, SromOpBind)
- for (_ssort_key in array)
- _sort_alen++
+ _opstream_offset = get(opstream, p_offset)
+ _offset = get(bind, p_offset)
+ if (_opstream_offset != _offset)
+ errorx("stream/bind offset state mismatch")
- if (_sort_alen <= 1)
- return
+ _count = get(bind, p_count)
+ _width = get(bind, p_width)
+ _skip_in = get(bind, p_skip_in)
- # perform sort
- _qsort(array, 0, _sort_alen-1)
+ set(opstream, p_offset,
+ _opstream_offset + ((_width * _skip_in) * _count))
}
-function _qsort (array, first, last)
+# (private) Write a bind instance and all buffered opcodes
+function _srom_ops_emit_bind(opstream, bind, _count, _skip_in, _skip_out,
+ _off_start, _width, _si_signbit, _written, _nbuffer, _buffer)
{
- if (first >= last)
+ obj_assert_class(opstream, SromOpStream)
+ obj_assert_class(bind, SromOpBind)
+
+ # Assert that any pending bind state has already been cleared
+ if (get(opstream, p_pending_bind) != null)
+ errorx("cannot flush bind with an existing pending_bind active")
+
+ # Fetch (and assert valid) our skip values
+ _skip_in = get(bind, p_skip_in)
+ _skip_out = get(bind, p_skip_out)
+
+ if (!srom_ops_can_encode_skip(_skip_in, _skip_out))
+ errorx("invalid skip values in buffered bind")
+
+ # Determine SKIP_IN sign bit
+ _si_signbit = "0"
+ if (_skip_in < 0)
+ _si_signbit = SPROM_OP_BIND_SKIP_IN_SIGN
+
+ # Emit BIND/BINDN opcodes until the full count is encoded
+ _count = get(bind, p_count)
+ while (_count > 0) {
+ if (_count > 1 && _count <= SPROM_OP_IMM_MAX &&
+ _skip_in == 1 && _skip_out == 1)
+ {
+ # The one-byte BINDN form requires encoding the count
+ # as a IMM, and has an implicit in/out skip of 1.
+ srom_ops_emit_opcode(opstream,
+ "("SPROM_OPCODE_DO_BINDN_IMM"|"_count")")
+ _count -= _count
+
+ } else if (_count > 1) {
+ # The two byte BINDN form can encode skip values and a
+ # larger U8 count
+ _written = min(_count, UInt8Max)
+
+ srom_ops_emit_opcode(opstream,
+ sprintf("(%s|%s|(%u<<%s)|(%u<<%s))",
+ SPROM_OPCODE_DO_BINDN,
+ _si_signbit,
+ abs(_skip_in), SPROM_OP_BIND_SKIP_IN_SHIFT,
+ _skip_out, SPROM_OP_BIND_SKIP_OUT_SHIFT),
+ _written)
+ _count -= _written
+
+ } else {
+ # The 1-byte BIND form can encode the same SKIP values
+ # as the 2-byte BINDN, with a implicit count of 1
+ srom_ops_emit_opcode(opstream,
+ sprintf("(%s|%s|(%u<<%s)|(%u<<%s))",
+ SPROM_OPCODE_DO_BIND,
+ _si_signbit,
+ abs(_skip_in), SPROM_OP_BIND_SKIP_IN_SHIFT,
+ _skip_out, SPROM_OP_BIND_SKIP_OUT_SHIFT))
+ _count--
+ }
+ }
+
+ # Update the stream's input offset
+ _srom_ops_apply_bind_offset(opstream, bind)
+
+ # Write any buffered post-BIND opcodes
+ _buffer = get(bind, p_buffer)
+ _nbuffer = array_size(_buffer)
+ for (_i = 0; _i < _nbuffer; _i++)
+ srom_ops_emit(opstream, array_get(_buffer, _i))
+}
+
+# Flush any buffered binding
+function srom_ops_flush_bind(opstream, allow_implicit, _bind, _bind_total)
+{
+ obj_assert_class(opstream, SromOpStream)
+
+ # If no pending bind, nothing to flush
+ if ((_bind = get(opstream, p_pending_bind)) == null)
return
- # select pivot element
- _qpivot = int(first + int((last-first+1) * rand()))
- _qleft = first
- _qright = last
+ # Check the per-variable bind count to determine whether
+ # we can encode an implicit bind.
+ #
+ # If there have been any explicit bind statements, implicit binding
+ # cannot be used.
+ _bind_total = get(opstream, p_bind_total)
+ if (allow_implicit && _bind_total > 0) {
+ # Disable implicit encoding; explicit bind statements have
+ # been issued for this variable previously.
+ allow_implicit = 0
+ }
- _qpivot_val = array[_qpivot]
+ # Increment bind count
+ set(opstream, p_bind_total, _bind_total + 1)
- # partition
- while (_qleft <= _qright) {
- while (array[_qleft] < _qpivot_val)
- _qleft++
+ # Clear the property value
+ set(opstream, p_pending_bind, null)
+
+ # If a pending bind operation can be encoded as an implicit bind,
+ # emit a descriptive comment and update the stream state.
+ #
+ # Otherwise, emit the full set of bind opcode(s)
+ _base_off = get(opstream, p_offset)
+ if (allow_implicit && srom_opbind_is_implicit_encodable(_bind)) {
+ # Update stream's input offset
+ _srom_ops_apply_bind_offset(opstream, _bind)
+ } else {
+ _srom_ops_emit_bind(opstream, _bind)
+ }
+
+ # Provide bind information as a comment
+ srom_ops_emit(opstream,
+ sprintf("/* bind (%s @ %#x -> %#x) */\n",
+ type_to_string(get(opstream, p_type)),
+ _base_off, get(opstream, p_offset)))
+
+ # Clean up
+ obj_delete(_bind)
+}
+
+# Write OPCODE_EOF after flushing any buffered writes
+function srom_ops_emit_eof(opstream) {
+ obj_assert_class(opstream, SromOpStream)
+
+ # Flush any buffered writes
+ srom_ops_flush_bind(opstream, 1)
+
+ # Emit an explicit VAR_END opcode for the last entry
+ srom_ops_emit_opcode(opstream, SPROM_OPCODE_VAR_END)
+
+ # Emit EOF
+ srom_ops_emit_opcode(opstream, SPROM_OPCODE_EOF)
+}
+
+# Write the SROM offset segment bindings to the opstream
+function write_srom_offset_bindings(opstream, offsets,
+ _noffsets, _offset, _segs, _nsegs, _segment, _cont,
+ _i, _j)
+{
+ _noffsets = array_size(offsets)
+ for (_i = 0; _i < _noffsets; _i++) {
+ # Encode each segment in this offset
+ _offset = array_get(offsets, _i)
+ _segs = get(_offset, p_segments)
+ _nsegs = array_size(_segs)
+
+ for (_j = 0; _j < _nsegs; _j++) {
+ _segment = array_get(_segs, _j)
+ _cont = 0
+
+ # Should this value be OR'd with the next segment?
+ if (_j+1 < _nsegs)
+ _cont = 1
+
+ # Encode segment
+ srom_ops_emit_segment(opstream, _segment, _cont)
+ }
+ }
+}
+
+# Write the SROM entry stream for a SROM entry to the output file
+function write_srom_entry_bindings(entry, opstream, _var, _vid,
+ _var_type, _entry_type, _offsets, _noffsets)
+{
+ _var = get(entry, p_var)
+ _vid = get(_var, p_vid)
+
+ # Encode revision switch. This resets variable state, so must
+ # occur before any variable definitions to which it applies
+ srom_ops_emit_revisions(opstream, get(entry, p_revisions))
+
+ # Encode variable ID
+ srom_ops_reset_var(opstream, _var, _vid)
+ output_depth++
+
+ # Write entry-specific array length (SROM layouts may define array
+ # mappings with fewer elements than in the variable definition)
+ if (srom_entry_has_array_type(entry)) {
+ _var_type = get(_var, p_type)
+ _entry_type = get(entry, p_type)
+
+ # If the array length differs from the variable default,
+ # write an OPCODE_EXT_NELEM entry
+ if (type_get_nelem(_var_type) != type_get_nelem(_entry_type)) {
+ srom_ops_emit_opcode(opstream, SPROM_OPCODE_NELEM,
+ srom_entry_get_array_len(entry))
+ }
+ }
+
+ # Write offset segment bindings
+ _offsets = get(entry, p_offsets)
+ write_srom_offset_bindings(opstream, _offsets)
+ output_depth--
+}
+
+# Write a SROM layout binding opcode table to the output file
+function write_srom_bindings(layout, _varname, _var, _all_entries,
+ _nall_entries, _entries, _nentries, _entry, _opstream, _i)
+{
+ _varname = srom_layout_get_variable_name(layout)
+ _all_entries = get(layout, p_entries)
+ _opstream = srom_ops_new(layout)
+
+ #
+ # Collect all entries to be included in the output, and then
+ # sort by their variable's assigned ID (ascending).
+ #
+ # The variable IDs were previously assigned in lexigraphical sort
+ # order; since the variable *offsets* tend to match this order, this
+ # works out well for our compact encoding, allowing us to make use of
+ # compact relative encoding of both variable IDs and variable offsets.
+ #
+ _entries = array_new()
+ _nall_entries = array_size(_all_entries)
+ for (_i = 0; _i < _nall_entries; _i++) {
+ _entry = array_get(_all_entries, _i)
+ _var = get(_entry, p_var)
+
+ # Skip internal variables
+ if (var_is_internal(_var))
+ continue
+
+ # Sanity check variable ID assignment
+ if (get(_var, p_vid) == "")
+ errorx("missing variable ID for " obj_to_string(_var))
+
+ array_append(_entries, _entry)
+ }
+
+ # Sort entries by variable ID, ascending
+ array_sort(_entries, prop_path_create(p_var, p_vid))
+
+ # Emit all entry binding opcodes
+ emit("static const uint8_t " _varname "[] = {\n")
+ output_depth++
+
+ _nentries = array_size(_entries)
+ for (_i = 0; _i < _nentries; _i++) {
+ _entry = array_get(_entries, _i)
+ write_srom_entry_bindings(_entry, _opstream)
+ }
+
+ # Flush and write EOF
+ srom_ops_emit_eof(_opstream)
+
+ output_depth--
+ emit("};\n")
+
+ obj_delete(_opstream)
+ obj_delete(_entries)
+}
+
+# Write the BHND_NVAR_<NAME>_ID #defines to the output file
+function write_data_defines(output_vars, _noutput_vars, _tab_align, _var,
+ _macro, _macros, _num_macros, _i)
+{
+ # Produce our array of #defines
+ _num_macros = 0
+ _noutput_vars = array_size(output_vars)
+ for (_i = 0; _i < _noutput_vars; _i++) {
+ _var = array_get(output_vars, _i)
+
+ # Variable ID
+ _macro = var_get_macro(_var, MTypeVarID, get(_var, p_vid))
+ _macros[_num_macros++] = _macro
+ }
+
+ # Calculate value tab alignment position for our macros
+ _tab_align = macros_get_tab_alignment(_macros, _num_macros)
+
+ # Write the #defines
+ emit("/* ID constants provide an index into the variable array */\n")
+ for (_i = 0; _i < _num_macros; _i++)
+ write_macro_define(_macros[_i], _tab_align)
+ emit("\n\n");
+}
+
+# Calculate the common tab alignment to be used with a set of prefix strings
+# with the given maximum length
+function tab_alignment(max_len, _tab_align) {
+ _tab_align = max_len
+ _tab_align += (TAB_WIDTH - (_tab_align % TAB_WIDTH)) % TAB_WIDTH
+ _tab_align /= TAB_WIDTH
+
+ return (_tab_align)
+}
+
+# Generate and return a tab string that can be appended to a string of
+# `strlen` to pad the column out to `align_to`
+#
+# Note: If the string from which strlen was derived contains tabs, the result
+# is undefined
+function tab_str(strlen, align_to, _lead, _pad, _result, _i) {
+ _lead = strlen
+ _lead -= (_lead % TAB_WIDTH);
+ _lead /= TAB_WIDTH;
+
+ # Determine required padding to reach the desired alignment
+ if (align_to >= _lead)
+ _pad = align_to - _lead;
+ else
+ _pad = 1;
+
+ for (_i = 0; _i < _pad; _i++)
+ _result = _result "\t"
+
+ return (_result)
+}
+
+
+# Write a MacroDefine constant, padding the constant out to `align_to`
+function write_macro_define(macro, align_to, _tabstr, _i) {
+ # Determine required padding to reach the desired alignment
+ _tabstr = tab_str(length(get(macro, p_name)), align_to)
+
+ emit("#define\t" get(macro, p_name) _tabstr get(macro, p_value) "\n")
+}
+
+# Calculate the tab alignment to be used with a given integer-indexed array
+# of Macro instances.
+function macros_get_tab_alignment(macros, macros_len, _macro, _max_len, _i) {
+ _max_len = 0
+ for (_i = 0; _i < macros_len; _i++) {
+ _macro = macros[_i]
+ _max_len = max(_max_len, length(get(_macro, p_name)))
+ }
+
+ return (tab_alignment(_max_len))
+}
+
+# Variable group block
+$1 == "group" && in_parser_context(NVRAM) {
+ parse_variable_group()
+}
+
+# Variable definition
+(($1 ~ VACCESS_REGEX && $2 ~ TYPES_REGEX) || $1 ~ TYPES_REGEX) &&
+ in_parser_context(SymbolContext) \
+{
+ parse_variable_defn()
+}
+
+# Variable "fmt" parameter
+$1 == "fmt" && in_parser_context(Var) {
+ parse_variable_param($1)
+ next
+}
+
+# Variable "all1" parameter
+$1 == "all1" && in_parser_context(Var) {
+ parse_variable_param($1)
+ next
+}
+
+# Variable desc/help parameters
+($1 == "desc" || $1 == "help") && in_parser_context(Var) {
+ parse_variable_param($1)
+ next
+}
+
+# SROM layout block
+$1 == "srom" && in_parser_context(NVRAM) {
+ parse_srom_layout()
+}
+
+
+# SROM layout revision filter block
+$1 == "srom" && in_parser_context(SromLayout) {
+ parse_srom_layout_filter()
+}
+
+# SROM layout variable entry
+$1 ~ "("OFF_REGEX"):$" && \
+ (in_parser_context(SromLayout) || in_parser_context(SromLayoutFilter)) \
+{
+ parse_srom_variable_entry()
+}
+
+
+# SROM entry segment
+$1 ~ "("REL_OFF_REGEX"|"OFF_REGEX")[:,|]?" && in_parser_context(SromEntry) {
+ parse_srom_entry_segments()
+}
+
+# Skip comments and blank lines
+/^[ \t]*#/ || /^$/ {
+ next
+}
+
+# Close blocks
+/}/ && !in_parser_context(NVRAM) {
+ while (!in_parser_context(NVRAM) && $0 ~ "}") {
+ parser_state_close_block();
+ }
+ next
+}
+
+# Report unbalanced '}'
+/}/ && in_parser_context(NVRAM) {
+ error("extra '}'")
+}
+
+# Invalid variable type
+$1 && in_parser_context(SymbolContext) {
+ error("unknown type '" $1 "'")
+}
+
+# Generic parse failure
+{
+ error("unrecognized statement")
+}
+
+# Create a class instance with the given name
+function class_new(name, superclass, _class) {
+ if (_class != null)
+ errorx("class_get() must be called with one or two arguments")
+
+ # Look for an existing class instance
+ if (name in _g_class_names)
+ errorx("redefining class: " name)
+
+ # Create and register the class object
+ _class = obj_new(superclass)
+ _g_class_names[name] = _class
+ _g_obj[_class,OBJ_IS_CLS] = 1
+ _g_obj[_class,CLS_NAME] = name
+
+ return (_class)
+}
+
+# Return the class instance with the given name
+function class_get(name) {
+ if (name in _g_class_names)
+ return (_g_class_names[name])
+
+ errorx("no such class " name)
+}
+
+# Return the name of cls
+function class_get_name(cls) {
+ if (cls == null) {
+ warnx("class_get_name() called with null class")
+ return "<null>"
+ }
+
+ if (!obj_is_class(cls))
+ errorx(cls " is not a class object")
+
+ return (_g_obj[cls,CLS_NAME])
+}
+
+# Return true if the given property property ID is defined on class
+function class_has_prop_id(class, prop_id, _super) {
+ if (_super != null)
+ errorx("class_has_prop_id() must be called with two arguments")
+
+ if (class == null)
+ return (0)
+
+ # Check class<->prop cache
+ if ((class, prop_id) in _g_class_prop_cache)
+ return (1)
+
+ # Otherwise, slow path
+ if (!obj_is_class(class))
+ errorx(class " is not a class object")
+
+ if (_super != null)
+ errorx("class_has_prop_id() must be called with two arguments")
+
+ for (_super = class; _super != null; _super = obj_get_class(_super)) {
+ if (!((_super,CLS_PROP,prop_id) in _g_obj))
+ continue
+
+ # Found; add to class<->prop cache
+ _g_class_prop_cache[class,prop_id] = 1
+ return (1)
+ }
+
+ return (0)
+}
+
+# Return true if the given property prop is defined on class
+function class_has_property(class, prop) {
+ if (!(PROP_ID in prop))
+ return (0)
+
+ return (class_has_prop_id(class, prop[PROP_ID]))
+}
+
+# Define a `prop` on `class` with the given `name` string
+function class_add_prop(class, prop, name, _prop_id) {
+ if (_prop_id != null)
+ errorx("class_add_prop() must be called with three arguments")
+
+ # Check for duplicate property definition
+ if (class_has_property(class, prop))
+ errorx("property " prop[PROP_NAME] " already defined on " \
+ class_get_name(class))
+
+ # Init property IDs
+ if (_g_prop_ids == null)
+ _g_prop_ids = 1
+
+ # Get (or create) new property entry
+ if (name in _g_prop_names) {
+ _prop_id = _g_prop_names[name]
+ } else {
+ _prop_id = _g_prop_ids++
+ _g_prop_names[name] = _prop_id
+ _g_props[_prop_id] = name
+
+ prop[PROP_NAME] = name
+ prop[PROP_ID] = _prop_id
+ }
+
+ # Add to class definition
+ _g_obj[class,CLS_PROP,prop[PROP_ID]] = name
+ return (name)
+}
+
+# Return the property ID for a given class-defined property
+function class_get_prop_id(class, prop) {
+ if (class == null)
+ errorx("class_get_prop_id() on null class")
+
+ if (!class_has_property(class, prop)) {
+ errorx("requested undefined property '" prop[PROP_NAME] "on " \
+ class_get_name(class))
+ }
+
+ return (prop[PROP_ID])
+}
+
+# Return the property ID for a given class-defined property name
+function class_get_named_prop_id(class, name, _prop_id) {
+ if (class == null)
+ errorx("class_get_prop_id() on null class")
+
+ if (!(name in _g_prop_names))
+ errorx("requested undefined property '" name "'")
+
+ _prop_id = _g_prop_names[name]
+
+ if (!class_has_prop_id(class, _prop_id)) {
+ errorx("requested undefined property '" _g_props[_prop_id] \
+ "' on " class_get_name(class))
+ }
+
+ return (_prop_id)
+}
+
+# Create a new instance of the given class
+function obj_new(class, _obj) {
+ if (_obj != null)
+ errorx("obj_new() must be called with one argument")
+
+ if (_g_obj_ids == null)
+ _g_obj_ids = 1
+
+ # Assign ID and set superclass
+ _obj = _g_obj_ids++
+ _g_obj[_obj,OBJ_SUPER] = class
+
+ return (_obj)
+}
+
+# obj_delete() support for Map instances
+function _obj_delete_map(obj, _prefix, _key) {
+ obj_assert_class(obj, Map)
+ _prefix = "^" obj SUBSEP
+ for (_key in _g_maps) {
+ if (!match(_key, _prefix) && _key != obj)
+ continue
+ delete _g_maps[_key]
+ }
+}
+
+# obj_delete() support for Array instances
+function _obj_delete_array(obj, _size, _i) {
+ obj_assert_class(obj, Array)
+ _size = array_size(obj)
+
+ for (_i = 0; _i < _size; _i++)
+ delete _g_arrays[obj,OBJ_PROP,_i]
+}
+
+# Destroy all metadata associated with the given object
+function obj_delete(obj, _prop_id, _prop_name, _prefix, _key, _size, _i) {
+ if (obj_is_class(obj))
+ errorx("cannot delete class objects")
+
+ # Handle classes that use external global array storage
+ # for effeciency
+ if (obj_is_instanceof(obj, Map)) {
+ _obj_delete_map(obj)
+ } else if (obj_is_instanceof(obj, Array)) {
+ _obj_delete_array(obj)
+ }
+
+ # Delete all object properties
+ for (_prop_name in _g_prop_names) {
+ if (!obj_has_prop_id(obj, _prop_id))
+ continue
+
+ _prop_id = _g_prop_names[_prop_name]
+ delete _g_obj[obj,OBJ_PROP,_prop_id]
+ delete _g_obj_nr[obj,OBJ_PROP,_prop_id]
+ }
+
+ # Delete instance state
+ delete _g_obj[obj,OBJ_IS_CLS]
+ delete _g_obj[obj,OBJ_SUPER]
+}
+
+# Print an object's unique ID, class, and properties to
+# stdout
+function obj_dump(obj, _pname, _prop_id, _prop_val) {
+ print(class_get_name(obj_get_class(obj)) "<" obj ">:")
+
+ # Dump all properties
+ for (_pname in _g_prop_names) {
+ _prop_id = _g_prop_names[_pname]
+
+ if (!obj_has_prop_id(obj, _prop_id))
+ continue
+
+ _prop_val = prop_get(obj, _prop_id)
+ printf("\t%s: %s\n", _pname, _prop_val)
+ }
+}
+
+# Return true if obj is a class object
+function obj_is_class(obj) {
+ return (_g_obj[obj,OBJ_IS_CLS] == 1)
+}
+
+# Return the class of obj, if any.
+function obj_get_class(obj) {
+ if (obj == null)
+ errorx("obj_get_class() on null object")
+ return (_g_obj[obj,OBJ_SUPER])
+}
+
+# Return true if obj is an instance of the given class
+function obj_is_instanceof(obj, class, _super) {
+ if (_super != null)
+ errorx("obj_is_instanceof() must be called with two arguments")
+
+ if (!obj_is_class(class))
+ errorx(class " is not a class object")
+
+ if (obj == null) {
+ errorx("obj_is_instanceof() called with null obj (class " \
+ class_get_name(class) ")")
+ }
+
+ for (_super = obj_get_class(obj); _super != null;
+ _super = obj_get_class(_super))
+ {
+ if (_super == class)
+ return (1)
+ }
+
+ return (0)
+}
+
+# Default object shallow equality implementation. Returns true if the two
+# objects share a common superclass and have identity equality across all defined
+# properties.
+function obj_trivially_equal(lhs, rhs, _class, _pname, _prop_id) {
+ # Simple case
+ if (lhs == rhs)
+ return (1)
+
+ # Must share a common superclass
+ _class = obj_get_class(lhs)
+ if (_class != obj_get_class(rhs))
+ return (0)
+
+ # Compare all properties
+ _prop_count = 0
+ for (_pname in _g_prop_names) {
+ _prop_id = _g_prop_names[_pname]
+
+ if (!class_has_prop_id(_class, _prop_id))
+ continue
+
+ if (prop_get(lhs, _prop_id) != prop_get(rhs, _prop_id))
+ return (0)
+ }
+
+ # All properties are trivially equal
+ return (1)
+}
+
+
+# Return a debug string representation of an object's unique ID, class, and
+# properties
+function obj_to_string(obj, _pname, _prop_id, _prop_val, _prop_count, _result) {
+ _result = class_get_name(obj_get_class(obj)) "<" obj ">: { "
+
+ # Fetch all properties
+ _prop_count = 0
+ for (_pname in _g_prop_names) {
+ _prop_id = _g_prop_names[_pname]
+
+ if (!obj_has_prop_id(obj, _prop_id))
+ continue
+
+ if (_prop_count >= 0)
+ _result = _result ", "
+
+ _result = _result sprintf("\t%s: %s\n", _pname, _prop_val)
+ _prop_count++
+ }
+
+ return (_result " }")
+}
+
+# Assert that obj is an instance of the given class
+function obj_assert_class(obj, class) {
+ if (!obj_is_instanceof(obj, class)) {
+ errorx(class_get_name(obj_get_class(obj)) "<" obj "> is not " \
+ "an instance of " class_get_name(class))
+ }
+}
+
+# Return true if the given property prop is defined by the object's superclass
+function obj_has_property(obj, prop, _class) {
+ if (obj == null)
+ errorx("obj_has_property() on null object")
+
+ _class = obj_get_class(obj)
+ return (class_has_property(_class, prop))
+}
+
+# Return true if the given property ID is defined by the object's superclass
+function obj_has_prop_id(obj, prop_id, _class) {
+ if (obj == null)
+ errorx("obj_has_prop_id() on null object")
+
+ _class = obj_get_class(obj)
+ return (class_has_prop_id(_class, prop_id))
+}
+
+# Return the line (NR) at which a given property ID was set on the object
+# Will throw an error if the property has not been set on obj
+function obj_get_prop_id_nr(obj, prop_id) {
+ if (obj == null)
+ errorx("obj_get_prop_id_nr() on null object")
+
+ if (!obj_has_prop_id(obj, prop_id)) {
+ errorx("requested undefined property '" _g_props[prop_id] \
+ "' (" prop_id ") on " obj_to_string(obj))
+ }
+
+ # Fetch NR
+ if ((obj,OBJ_PROP,prop_id) in _g_obj_nr)
+ return (_g_obj_nr[obj,OBJ_PROP,prop_id])
+
+ errorx("property '" _g_props[prop_id] "' (" prop_id ") not " \
+ "previously set on " obj_to_string(obj))
+}
+
+# Return the line (NR) at which a given property was set on the object
+# Will throw an error if the property has not been set on obj
+function obj_get_prop_nr(obj, prop) {
+ return (obj_get_prop_id_nr(obj, prop[PROP_ID]))
+}
+
+# Return an abstract property ID for a given property
+function obj_get_prop_id(obj, prop) {
+ if (obj == null)
+ errorx("obj_get_prop_id() on null object")
+
+ return (class_get_prop_id(obj_get_class(obj), prop))
+}
+
+
+# Return the property ID for a given property name
+function obj_get_named_prop_id(obj, name) {
+ if (obj == null)
+ errorx("obj_get_named_prop_id() on null object")
+
+ return (class_get_named_prop_id(obj_get_class(obj), name))
+}
+
+# Set a property on obj
+function set(obj, prop, value, _class) {
+ return (prop_set(obj, prop[PROP_ID], value))
+}
+
+# Get a property value defined on obj
+function get(obj, prop, _class) {
+ return (prop_get(obj, prop[PROP_ID]))
+}
+
+# Set a property on obj, using a property ID returned by obj_get_prop_id() or
+# class_get_prop_id()
+function prop_set(obj, prop_id, value, _class) {
+ if (obj == null) {
+ errorx("setting property '" _g_props[prop_id] \
+ "' on null object")
+ }
+
+ _class = obj_get_class(obj)
+ if (_class == null)
+ errorx(obj " has no superclass")
+
+ if (!class_has_prop_id(_class, prop_id)) {
+ errorx("requested undefined property '" _g_props[prop_id] \
+ "' (" prop_id ") on " class_get_name(_class))
+ }
+
+ # Track the line on which the property was set
+ _g_obj_nr[obj,OBJ_PROP,prop_id] = NR
+ _g_obj[obj,OBJ_PROP,prop_id] = value
+}
+
+# Convert a property ID to a property path.
+function prop_id_to_path(prop_id) {
+ if (!(prop_id in _g_props))
+ errorx("'" prop_id "' is not a property ID")
+
+ # Convert to path string representation
+ return (""prop_id)
+}
+
+# Convert a property to a property path.
+function prop_to_path(prop) {
+ if (!(PROP_ID in prop))
+ errorx("prop_to_path() called with non-property head")
+
+ return (prop_id_to_path(prop[PROP_ID]))
+}
+
+# Create a property path from head and tail properties
+# Additional properties may be appended via prop_path_append() or
+# prop_path_append_id()
+function prop_path_create(head, tail) {
+ if (!(PROP_ID in head))
+ errorx("prop_path() called with non-property head")
+
+ if (!(PROP_ID in tail))
+ errorx("prop_path() called with non-property tail")
+
+ return (head[PROP_ID] SUBSEP tail[PROP_ID])
+}
+
+# Append a property to the given property path
+function prop_path_append(path, tail) {
+ if (!(PROP_ID in tail))
+ errorx("prop_path_append() called with non-property tail")
+
+ return (prop_path_append_id(path, tail[PROP_ID]))
+}
+
+# Append a property ID to the given property path
+function prop_path_append_id(path, tail_id) {
+ if (!(tail_id in _g_props))
+ errorx("'" tail_id "' is not a property ID")
+
+ return (path SUBSEP tail_id)
+}
+
+# Fetch a value from obj using a property path previously returned by
+# prop_path_create(), prop_to_path(), etc.
+function prop_get_path(obj, prop_path, _class, _prop_ids, _nprop_ids, _next,
+ _prop_head, _prop_len, _prop_tail)
+{
+ if (obj == null) {
+ errorx("requested property path '" \
+ gsub(SUBSEP, ".", prop_path) "' on null object")
+ }
+
+ # Try the cache first
+ _class = obj_get_class(obj)
+ if ((_class,prop_path,PPATH_HEAD) in _g_ppath_cache) {
+ _prop_head = _g_ppath_cache[_class,prop_path,PPATH_HEAD]
+ _next = prop_get(obj, _prop_head)
+
+ if ((_class,prop_path,PPATH_TAIL) in _g_ppath_cache) {
+ _prop_tail = _g_ppath_cache[_class,prop_path,PPATH_TAIL]
+ return (prop_get_path(_next, _prop_tail))
+ }
+
+ return (_next)
+ }
+
+ # Parse the head/tail of the property path and add to cache
+ _nprop_ids = split(prop_path, _prop_ids, SUBSEP)
+ if (_nprop_ids == 0)
+ errorx("empty property path")
+ _prop_head = _prop_ids[1]
+ _g_ppath_cache[_class,prop_path,PPATH_HEAD] = _prop_head
+
+ if (_nprop_ids > 1) {
+ _prop_len = length(_prop_head)
+ _prop_tail = substr(prop_path, _prop_len+2)
+
+ # Add to cache
+ _g_ppath_cache[_class,prop_path,PPATH_TAIL] = _prop_tail
+ }
+
+ # Recursively call out implementation, this time fetching from
+ # cache
+ return (prop_get_path(obj, prop_path))
+}
+
+# Fetch a value property value from obj, using a property ID returned by
+# obj_get_prop_id() or class_get_prop_id()
+function prop_get(obj, prop_id, _class) {
+ if (obj == null) {
+ errorx("requested property '" _g_props[prop_id] \
+ "' on null object")
+ }
+
+ _class = obj_get_class(obj)
+ if (_class == null)
+ errorx(obj " has no superclass")
+
+ if (!class_has_prop_id(_class, prop_id)) {
+ errorx("requested undefined property '" _g_props[prop_id] \
+ "' (" prop_id ") on " class_get_name(_class))
+ }
+
+ return (_g_obj[obj,OBJ_PROP,prop_id])
+}
+
+# Create a new MacroType instance
+function macro_type_new(name, const_suffix, _obj) {
+ _obj = obj_new(MacroType)
+
+ set(_obj, p_name, name)
+ set(_obj, p_const_suffix, const_suffix)
+
+ return (_obj)
+}
+
+# Create a new MacroDefine instance
+function macro_new(name, value, _obj) {
+ _obj = obj_new(MacroDefine)
+ set(_obj, p_name, name)
+ set(_obj, p_value, value)
+
+ return (_obj)
+}
+
+# Create an empty array; this uses _g_arrays to store integer
+# keys/values under the object's property prefix.
+function array_new(_obj) {
+ _obj = obj_new(Array)
+ set(_obj, p_count, 0)
+
+ return (_obj)
+}
+
+# Return the number of elements in the array
+function array_size(array) {
+ obj_assert_class(array, Array)
+ return (get(array, p_count))
+}
+
+# Return true if the array is empty
+function array_empty(array) {
+ return (array_size(array) == 0)
+}
+
+# Append a value to the array
+function array_append(array, value, _i) {
+ obj_assert_class(array, Array)
+
+ _i = get(array, p_count)
+ _g_arrays[array,OBJ_PROP,_i] = value
+ set(array, p_count, _i+1)
+}
+
+# Set an array value
+# An error will be thrown if the idx is outside array bounds
+function array_set(array, idx, value) {
+ obj_assert_class(array, Array)
+
+ if (!((array,OBJ_PROP,idx) in _g_arrays))
+ errorx(idx " out of range of array " obj_to_string(array))
+
+ _g_arrays[array,OBJ_PROP,idx] = value
+}
+
+# Return value at the given index from the array
+# An error will be thrown if 'idx' is outside the array bounds
+function array_get(array, idx) {
+ obj_assert_class(array, Array)
+
+ if (!((array,OBJ_PROP,idx) in _g_arrays))
+ errorx(idx " out of range of array " obj_to_string(array))
+
+ return (_g_arrays[array,OBJ_PROP,idx])
+}
+
+
+#
+# Sort an array, using standard awk comparison operators over its values.
+#
+# If `prop_path` is non-NULL, the corresponding property path (or property ID)
+# will be fetched from each array element and used as the sorting value.
+#
+function array_sort(array, prop_path, _size) {
+ obj_assert_class(array, Array)
+
+ _size = array_size(array)
+ if (_size <= 1)
+ return
+
+ _qsort(array, prop_path, 0, _size-1)
+}
+
+function _qsort_get_key(array, idx, prop_path, _v) {
+ _v = array_get(array, idx)
+
+ if (prop_path == null)
+ return (_v)
+
+ return (prop_get_path(_v, prop_path))
+}
+
+function _qsort(array, prop_path, first, last, _qpivot, _qpivot_val, _qleft,
+ _qleft_val, _qright, _qright_val)
+{
+ if (first >= last)
+ return
+
+ # select pivot element
+ _qpivot = int(first + int((last-first+1) * rand()))
+ _qleft = first
+ _qright = last
+
+ _qpivot_val = _qsort_get_key(array, _qpivot, prop_path)
+
+ # partition
+ while (_qleft <= _qright) {
+ while (_qsort_get_key(array, _qleft, prop_path) < _qpivot_val)
+ _qleft++
+
+ while (_qsort_get_key(array, _qright, prop_path) > _qpivot_val)
+ _qright--
+
+ # swap
+ if (_qleft <= _qright) {
+ _qleft_val = array_get(array, _qleft)
+ _qright_val = array_get(array, _qright)
+
+ array_set(array, _qleft, _qright_val)
+ array_set(array, _qright, _qleft_val)
+
+ _qleft++
+ _qright--
+ }
+ }
+
+ # sort the partitions
+ _qsort(array, prop_path, first, _qright)
+ _qsort(array, prop_path, _qleft, last)
+}
+
+
+#
+# Join all array values with the given separator
+#
+# If `prop_path` is non-NULL, the corresponding property path (or property ID)
+# will be fetched from each array value and included in the result, rather than
+# immediate array value
+#
+function array_join(array, sep, prop_path, _i, _size, _value, _result) {
+ obj_assert_class(array, Array)
+
+ _result = ""
+ _size = array_size(array)
+ for (_i = 0; _i < _size; _i++) {
+ # Fetch the value (and optionally, a target property)
+ _value = array_get(array, _i)
+ if (prop_path != null)
+ _value = prop_get_path(_value, prop_path)
+
+ if (_i+1 < _size)
+ _result = _result _value sep
+ else
+ _result = _result _value
+ }
+
+ return (_result)
+}
+
+# Return the first value in the array, or null if empty
+function array_first(array) {
+ obj_assert_class(array, Array)
+
+ if (array_size(array) == 0)
+ return (null)
+ else
+ return (array_get(array, 0))
+}
+
+# Return the last value in the array, or null if empty
+function array_tail(list, _size) {
+ obj_assert_class(array, Array)
+
+ _size = array_size(array)
+ if (_size == 0)
+ return (null)
+ else
+ return (array_get(array, _size-1))
+}
+
+# Create an empty hash table; this uses the _g_maps array to store arbitrary
+# keys/values under the object's property prefix.
+function map_new(_obj) {
+ _obj = obj_new(Map)
+ return (_obj)
+}
+
+# Add `key` with `value` to `map`
+function map_set(map, key, value) {
+ obj_assert_class(map, Map)
+ _g_maps[map,OBJ_PROP,key] = value
+}
+
+# Remove `key` from the map
+function map_remove(map, key) {
+ obj_assert_class(map, Map)
+ delete _g_maps[map,OBJ_PROP,key]
+}
+
+# Return true if `key` is found in `map`, false otherwise
+function map_contains(map, key) {
+ obj_assert_class(map, Map)
+ return ((map,OBJ_PROP,key) in _g_maps)
+}
+
+# Fetch the value of `key` from the map. Will throw an error if the
+# key does not exist
+function map_get(map, key) {
+ obj_assert_class(map, Map)
+ return _g_maps[map,OBJ_PROP,key]
+}
+
+# Create and return a new list containing all defined values in `map`
+function map_to_array(map, _key, _prefix, _values) {
+ obj_assert_class(map, Map)
+
+ _values = array_new()
+ _prefix = "^" map SUBSEP OBJ_PROP SUBSEP
+ for (_key in _g_maps) {
+ if (!match(_key, _prefix))
+ continue
+
+ array_append(_values, _g_maps[_key])
+ }
+
+ return (_values)
+}
+
+# Create a new Type instance
+function type_new(name, width, signed, constant, array_constant, fmt, mask,
+ constant_value, array_constant_value, _obj)
+{
+ obj_assert_class(fmt, Fmt)
+
+ _obj = obj_new(Type)
+ set(_obj, p_name, name)
+ set(_obj, p_width, width)
+ set(_obj, p_signed, signed)
+ set(_obj, p_const, constant)
+ set(_obj, p_const_val, constant_value)
+ set(_obj, p_array_const, array_constant)
+ set(_obj, p_array_const_val, array_constant_value)
+ set(_obj, p_default_fmt, fmt)
+ set(_obj, p_mask, mask)
+
+ return (_obj)
+}
+
+# Return true if two types are equal
+function type_equal(lhs, rhs) {
+ # Simple case
+ if (lhs == rhs)
+ return (1)
+
+ # Must share a common class
+ if (obj_get_class(lhs) != obj_get_class(rhs))
+ return (0)
+
+ # Handle ArrayType equality
+ if (obj_is_instanceof(lhs, ArrayType)) {
+ # Size must be equal
+ if (get(lhs, p_count) != get(rhs, p_count))
+ return (0)
+
+ # The base types must be equal
+ return (type_equal(type_get_base(lhs), type_get_base(rhs)))
+ }
+
+ # Handle Type equality -- we just check for trivial identity
+ # equality of all members
+ obj_assert_class(lhs, Type)
+ return (obj_trivially_equal(lhs, rhs))
+}
+
+# Return the type's default value mask. If the type is an array type,
+# the default mask of the base type will be returned.
+function type_get_default_mask(type) {
+ if (obj_is_instanceof(type, ArrayType))
+ return (type_get_default_mask(type_get_base(type)))
+
+ obj_assert_class(type, Type)
+ return (get(type, p_mask))
+}
+
+# Return the type's C constant representation
+function type_get_const(type) {
+ if (obj_is_instanceof(type, ArrayType))
+ return (get(type_get_base(type), p_array_const))
+
+ obj_assert_class(type, Type)
+ return (get(type, p_const))
+}
+
+# Return the type's C constant integer value
+function type_get_const_val(type) {
+ if (obj_is_instanceof(type, ArrayType))
+ return (get(type_get_base(type), p_array_const_val))
+
+ obj_assert_class(type, Type)
+ return (get(type, p_const_val))
+}
+
+# Return an array type's element count, or 1 if the type is not
+# an array type
+function type_get_nelem(type) {
+ if (obj_is_instanceof(type, ArrayType))
+ return (get(type, p_count))
+
+ obj_assert_class(type, Type)
+ return (1)
+}
+
+# Return the base type for a given type instance.
+function type_get_base(type) {
+ if (obj_is_instanceof(type, ArrayType))
+ return (type_get_base(get(type, p_type)))
+
+ obj_assert_class(type, Type)
+ return (type)
+}
+
+# Return the default fmt for a given type instance
+function type_get_default_fmt(type, _base) {
+ _base = type_get_base(type)
+ return (get(_base, p_default_fmt))
+}
+
+# Return a string representation of the given type
+function type_to_string(type, _base_type) {
+ if (obj_is_instanceof(type, ArrayType)) {
+ _base_type = type_get_base(type)
+ return (type_to_string(_base_type) "[" get(type, p_count) "]")
+ }
+ return get(type, p_name)
+}
+
+# Return true if type `rhs` is can be coerced to type `lhs` without data
+# loss
+function type_can_represent(lhs, rhs) {
+ # Must be of the same class (Type or ArrayType)
+ if (obj_get_class(lhs) != obj_get_class(rhs))
+ return (0)
+
+ if (obj_is_instanceof(lhs, ArrayType)) {
+ # The base types must have a representable relationship
+ if (!type_can_represent(type_get_base(lhs), type_get_base(rhs)))
+ return (0)
+
+ # The lhs type must be able to represent -at least- as
+ # many elements as the RHS type
+ if (get(lhs, p_count) < get(rhs, p_count))
+ return (0)
+
+ return (1)
+ }
+
+ # A signed type could represent the full range of a smaller unsigned
+ # type, but we don't bother; the two should agree when used in a SROM
+ # layout. Instead simply assert that both are signed or unsigned.
+ if (get(lhs, p_signed) != get(rhs, p_signed))
+ return (0)
+
+ # The `rhs` type must be equal or smaller in width to the `lhs` type
+ if (get(lhs, p_width) < get(rhs, p_width))
+ return (0)
+
+ return (1)
+}
+
+# Create a new ArrayType instance
+function array_type_new(type, count, _obj) {
+ _obj = obj_new(ArrayType)
+ set(_obj, p_type, type)
+ set(_obj, p_count, count)
+
+ return (_obj)
+}
+
+#
+# Parse a type string to either the Type, ArrayType, or null if
+# the type is not recognized.
+#
+function parse_type_string(str, _base, _count) {
+ if (match(str, ARRAY_REGEX"$") > 0) {
+ # Extract count and base type
+ _count = substr(str, RSTART+1, RLENGTH-2)
+ sub(ARRAY_REGEX"$", "", str)
+
+ # Look for base type
+ if ((_base = type_named(str)) == null)
+ return (null)
+
+ return (array_type_new(_base, int(_count)))
+ } else {
+ return (type_named(str))
+ }
+}
+
+#
+# Parse a variable name in the form of 'name' or 'name[len]', returning
+# either the provided base_type if no array specifiers are found, or
+# the fully parsed ArrayType.
+#
+function parse_array_type_specifier(str, base_type, _count) {
+ if (match(str, ARRAY_REGEX"$") > 0) {
+ # Extract count
+ _count = substr(str, RSTART+1, RLENGTH-2)
+ return (array_type_new(base_type, int(_count)))
+ } else {
+ return (base_type)
+ }
+}
+
+# Return the type constant for `name`, if any
+function type_named(name, _n, _type) {
+ if (name == null)
+ errorx("called type_named() with null name")
+
+ if (map_contains(BaseTypes, name))
+ return (map_get(BaseTypes, name))
+
+ return (null)
+}
+
+# Create a new Fmt instance
+function fmt_new(name, symbol, _obj) {
+ _obj = obj_new(Fmt)
+ set(_obj, p_name, name)
+ set(_obj, p_symbol, symbol)
+
+ return (_obj)
+}
+
+
+# Return the Fmt constant for `name`, if any
+function fmt_named(name, _n, _fmt) {
+ if (map_contains(ValueFormats, name))
+ return (map_get(ValueFormats, name))
+
+ return (null)
+}
+
+# Create a new VFlag instance
+function vflag_new(name, constant, _obj) {
+ _obj = obj_new(VFlag)
+ set(_obj, p_name, name)
+ set(_obj, p_const, constant)
+
+ return (_obj)
+}
+
+# Create a new StringConstant AST node
+function stringconstant_new(value, continued, _obj) {
+ _obj = obj_new(StringConstant)
+ set(_obj, p_value, value)
+ set(_obj, p_continued, continued)
+ set(_obj, p_line, NR)
+
+ return (_obj)
+}
+
+# Create an empty StringConstant AST node to which additional lines
+# may be appended
+function stringconstant_empty(_obj) {
+ return (stringconstant_new("", 1))
+}
+
+# Parse an input string and return a new string constant
+# instance
+function stringconstant_parse_line(line, _obj) {
+ _obj = stringconstant_empty()
+ stringconstant_append_line(_obj, line)
+ return (_obj)
+}
+
+# Parse and apend an additional line to this string constant
+function stringconstant_append_line(str, line, _cont, _strbuf, _regex, _eol) {
+ obj_assert_class(str, StringConstant)
+
+ # Must be marked for continuation
+ if (!get(str, p_continued)) {
+ errorx("can't append to non-continuation string '" \
+ get(str, p_value) "'")
+ }
+
+ _strbuf = get(str, p_value)
+
+ # If start of string, look for (and remove) initial double quote
+ if (_strbuf == null) {
+ _regex = "^[ \t]*\""
+ if (!sub(_regex, "", line)) {
+ error("expected quoted string")
+ }
+ }
+
+ # Look for a terminating double quote
+ _regex = "([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\""
+
+ _eol = match(line, _regex)
+ if (_eol > 0) {
+ # Drop everything following the terminating quote
+ line = substr(line, 1, RLENGTH-1)
+ _cont = 0
+ } else {
+ # No terminating quote found, continues on next line
+ _cont = 1
+ }
+
+ # Trim leading and trailing whitespace
+ sub(/(^[ \t]+|[ \t]+$)/, "", line)
+
+ # Append to existing buffer
+ if ((_strbuf = get(str, p_value)) == NULL)
+ set(str, p_value, line)
+ else
+ set(str, p_value, _strbuf " " line)
+
+ # Update line continuation setting
+ set(str, p_continued, _cont)
+}
+
+# Create a new RevRange instance
+function revrange_new(start, end, _obj) {
+ _obj = obj_new(RevRange)
+ set(_obj, p_start, start)
+ set(_obj, p_end, end)
+ set(_obj, p_line, NR)
+
+ return (_obj)
+}
+
+# Return true if the two revision ranges are equal
+function revrange_equal(lhs, rhs) {
+ if (get(lhs, p_start) != get(rhs, p_start))
+ return (0)
+
+ if (get(lhs, p_end) != get(rhs, p_end))
+ return (0)
+
+ return (1)
+}
+
+# Return true if the requested rev is covered by revrange, false otherwise
+function revrange_contains(range, rev) {
+ obj_assert_class(range, RevRange)
+
+ if (rev < get(range, p_start))
+ return (0)
+ else if (rev > get(range, p_end)) {
+ return (0)
+ } else {
+ return (1)
+ }
+}
+
+#
+# Return a string representation of the given revision range
+#
+function revrange_to_string(revs, _start, _end) {
+ obj_assert_class(revs, RevRange)
+
+ _start = get(revs, p_start)
+ _end = get(revs, p_end)
+
+ if (_start == 0)
+ return ("<= " _end)
+ else if (_end == REV_MAX)
+ return (">= " _start)
+ else
+ return (_start "-" _end)
+}
+
+# Create a new VarGroup instance
+function var_group_new(name, _obj) {
+ _obj = obj_new(VarGroup)
+ set(_obj, p_name, name)
+ set(_obj, p_vars, array_new())
+ set(_obj, p_line, NR)
+
+ return (_obj)
+}
+
+# Create a new NVRAM instance
+function nvram_new(_obj, _vars, _v) {
+ _obj = obj_new(NVRAM)
+ _vars = array_new()
+ set(_obj, p_vars, _vars)
+ set(_obj, p_var_groups, array_new())
+ set(_obj, p_srom_layouts, array_new())
+ set(_obj, p_srom_table, map_new())
+
+ #
+ # Register our implicit variable definitions
+ #
+
+ # SROM signature offset
+ _v = var_new(VAccessInternal, "<sromsig>", UInt16)
+ array_append(_vars, _v)
+ _g_var_names[get(_v, p_name)] = _v
+
+ # SROM CRC8 offset
+ _v = var_new(VAccessInternal, "<sromcrc>", UInt8)
+ array_append(_vars, _v)
+ _g_var_names[get(_v, p_name)] = _v
+
+ return (_obj)
+}
+
+# Register a new SROM layout instance
+# An error will be thrown if the layout overlaps any revisions covered
+# by an existing instance.
+function nvram_add_srom_layout(nvram, layout, _table, _revs, _start, _end, _i) {
+ obj_assert_class(nvram, NVRAM)
+ obj_assert_class(layout, SromLayout)
+
+ # revision:layout hash table
+ _table = get(nvram, p_srom_table)
+
+ # register the layout's revisions
+ _revs = get(layout, p_revisions)
+ _start = get(_revs, p_start)
+ _end = get(_revs, p_end)
+
+ for (_i = _start; _i <= _end; _i++) {
+ if (map_contains(_table, _i)) {
+ error("SROM layout redeclares layout for revision '" \
+ _i "' (originally declared on line " \
+ get(map_get(_table, _i), p_line) ")")
+ }
+
+ map_set(_table, _i, layout)
+ }
+
+ # append to srom_layouts
+ array_append(get(nvram, p_srom_layouts), layout)
+}
+
+# Return the first SROM layout registered for a given SROM revision,
+# or null if no matching layout is found
+function nvram_get_srom_layout(nvram, revision, _layouts, _nlayouts, _layout,
+ _i)
+{
+ obj_assert_class(nvram, NVRAM)
+
+ _layouts = get(nvram, p_srom_layouts)
+ _nlayouts = array_size(_layouts)
+ for (_i = 0; _i < _nlayouts; _i++) {
+ _layout = array_get(_layouts, _i)
+
+ if (srom_layout_has_rev(_layout, revision))
+ return (_layout)
+ }
+
+ # Not found
+ return (null)
+}
+
+# Create a new Var instance
+function var_new(access, name, type, _obj) {
+ obj_assert_class(access, VAccess)
+
+ # Validate the variable identifier
+ #
+ # The access modifier dictates the permitted identifier format.
+ # VAccessInternal: <ident>
+ # VAccess(Public|Private): ident
+ if (access != VAccessInternal && name ~ SVAR_IDENT_REGEX) {
+ error("invalid identifier '"name"'; did you mean to " \
+ "mark this variable as internal?")
+ } else if (access == VAccessInternal) {
+ if (name !~ SVAR_IDENT_REGEX)
+ error("invalid identifier '"name"' for internal " \
+ "variable; did you mean '<" name ">'?")
+ } else if (name !~ VAR_IDENT_REGEX) {
+ error("invalid identifier '"name"'")
+ }
+
+ _obj = obj_new(Var)
+ set(_obj, p_access, access)
+ set(_obj, p_name, name)
+ set(_obj, p_type, type)
+ set(_obj, p_line, NR)
+
+ return (_obj)
+}
+
+# Return true if var is internal-only, and should not be included
+# in any output (e.g. has an access specifier of VAccessInternal).
+function var_is_internal(var) {
+ return (get(var, p_access) == VAccessInternal)
+}
+
+# Return true if `var` has an array type
+function var_has_array_type(var, _vtype) {
+ obj_assert_class(var, Var)
+ _vtype = get(var, p_type)
+ return (obj_is_instanceof(_vtype, ArrayType))
+}
+
+# Return the number of array elements defined by this variable's type,
+# or 1 if the variable does not have an array type.
+function var_get_array_len(var) {
+ obj_assert_class(var, Var)
+ return (type_get_nelem(get(var, p_type)))
+}
+
+# Return the fmt for var. If not explicitly set on var, will return then
+# return of calling type_get_default_fmt() with the variable's type
+function var_get_fmt(var, _fmt) {
+ obj_assert_class(var, Var)
+
+ # If defined on var, return it
+ if ((_fmt = get(var, p_fmt)) != null)
+ return (_fmt)
+
+ # Fall back on the type's default
+ return (type_get_default_fmt(get(var, p_type)))
+}
+
+# Return a new MacroDefine instance for the given variable, macro type,
+# and value
+function var_get_macro(var, macro_type, value, _macro) {
+ obj_assert_class(var, Var)
+ obj_assert_class(macro_type, MacroType)
+
+ return (macro_new(var_get_macro_name(var, macro_type), value))
+}
+
+# Return the preprocessor constant name to be used with `var` for the given
+# macro_type
+function var_get_macro_name(var, macro_type, _var_name, _suffix) {
+ obj_assert_class(var, Var)
+ obj_assert_class(macro_type, MacroType)
+
+ _var_name = get(var, p_name)
+ _suffix = get(macro_type, p_const_suffix)
+
+ return("BHND_NVAR_" toupper(_var_name) _suffix)
+}
+
+# Create a new SromLayout instance
+function srom_layout_new(rev_desc, _obj)
+{
+ _obj = obj_new(SromLayout)
+ set(_obj, p_revisions, rev_desc)
+ set(_obj, p_entries, array_new())
+ set(_obj, p_revmap, map_new())
+ set(_obj, p_output_var_counts, map_new())
+ set(_obj, p_line, NR)
+
+ return (_obj)
+}
+
+# Register a new entry with the srom layout
+function srom_layout_add_entry(layout, entry, _revmap, _name, _rev_start,
+ _rev_end, _var, _prev_entry, _count, _i)
+{
+ obj_assert_class(layout, SromLayout)
+ obj_assert_class(entry, SromEntry)
+
+ _layout_revmap = get(layout, p_revmap)
+ _layout_var_count = get(layout, p_output_var_counts)
+
+ _var = get(entry, p_var)
+ _name = get(_var, p_name)
+
+ # Add to revision array
+ array_append(get(layout, p_entries), entry)
+
+ # Add to the revision map tables
+ _rev_start = get(get(entry, p_revisions), p_start)
+ _rev_end = get(get(entry, p_revisions), p_end)
+
+ for (_i = _rev_start; _i <= _rev_end; _i++) {
+ # Check for existing entry
+ _prev_entry = srom_layout_find_entry(layout, _name, _i)
+ if (_prev_entry != null) {
+ error("redefinition of variable '" _name "' for SROM " \
+ "revision " _i " (previously defined on line " \
+ get(_prev_entry, p_line) ")")
+ }
+
+ # Add to the (varname,revision) map
+ map_set(_layout_revmap, (_name SUBSEP _i), entry)
+
+ # If an output variable, set or increment the output variable
+ # count
+ if (!srom_entry_should_output(entry, _i))
+ continue
+
+ if (!map_contains(_layout_var_count, _i)) {
+ map_set(_layout_var_count, _i, 1)
+ } else {
+ _count = map_get(_layout_var_count, _i)
+ map_set(_layout_var_count, _i, _count + 1)
+ }
+ }
+}
+
+
+# Return the variable name to be used when emitting C declarations
+# for this SROM layout
+#
+# The name is gauranteed to be unique across SROM layouts with non-overlapping
+# revision ranges
+function srom_layout_get_variable_name(layout, _revs) {
+ obj_assert_class(layout, SromLayout)
+
+ _revs = get(layout, p_revisions)
+
+ return ("bhnd_sprom_layout_r" get(_revs, p_start) \
+ "_r" get(_revs, p_end))
+}
+
+# Return true if the given SROM revision is defined by the layout, false
+# otherwise
+function srom_layout_has_rev(layout, rev) {
+ obj_assert_class(layout, SromLayout)
+ return (revrange_contains(get(layout, p_revisions), rev))
+}
+
+
+# Return the total number of output variables (variables to be included
+# in the SROM layout bindings) for the given SROM revision
+function srom_layout_num_output_vars(layout, rev, _counts)
+{
+ obj_assert_class(layout, SromLayout)
+
+ _counts = get(layout, p_output_var_counts)
+ if (!map_contains(_counts, rev))
+ return (0)
+
+ return (map_get(_counts, rev))
+}
+
+# Return the SromEntry defined for the given variable name and SROM revision,
+# or null if none
+function srom_layout_find_entry(layout, vname, revision, _key, _srom_revmap) {
+ obj_assert_class(layout, SromLayout)
+
+ _srom_revmap = get(layout, p_revmap)
+
+ # SromEntry are mapped by name,revision composite keys
+ _key = vname SUBSEP revision
+ if (!map_contains(_srom_revmap, _key))
+ return (null)
+
+ return (map_get(_srom_revmap, _key))
+
+}
+
+# Create a new SromLayoutFilter instance, checking that `revs`
+# falls within the parent's revision range
+function srom_layout_filter_new(parent, revs, _obj, _start, _end, _parent_revs) {
+ obj_assert_class(parent, SromLayout)
+ obj_assert_class(revs, RevRange)
+
+ # Fetch our parent's revision range, confirm that we're
+ # a strict subset
+ _start = get(revs, p_start)
+ _end = get(revs, p_end)
+ _parent_revs = get(parent, p_revisions)
+
+ if (!revrange_contains(_parent_revs, _start))
+ error("'" _start "' is outside of parent range")
+
+ if (!revrange_contains(_parent_revs, _end))
+ error("'" _end "' is outside of parent range")
+
+ if (revrange_equal(revs, _parent_revs)) {
+ error("srom range '" revrange_to_string(revs) "' is " \
+ "identical to parent range of '" \
+ revrange_to_string(_parent_revs) "'")
+ }
+
+ # Construct and return new filter instance
+ _obj = obj_new(SromLayoutFilter)
+ set(_obj, p_parent, parent)
+ set(_obj, p_revisions, revs)
+ set(_obj, p_line, NR)
+
+ return (_obj)
+}
+
+#
+# Create a new SromEntry instance
+#
+# var: The variable referenced by this entry
+# revisions: The SROM revisions to which this entry applies
+# base_offset: The SROM entry offset; any relative segment offsets will be
+# calculated relative to the base offset
+# type: The SROM's value type; this may be a subtype of the variable
+# type, and defines the data (width, sign, etc) to be read from
+# SROM.
+#
+function srom_entry_new(var, revisions, base_offset, type, _obj) {
+ obj_assert_class(var, Var)
+ if (revisions != null)
+ obj_assert_class(revisions, RevRange)
+
+ _obj = obj_new(SromEntry)
+ set(_obj, p_var, var)
+ set(_obj, p_revisions, revisions)
+ set(_obj, p_base_offset, base_offset)
+ set(_obj, p_type, type)
+ set(_obj, p_offsets, array_new())
+ set(_obj, p_line, NR)
+
+ return (_obj)
+}
+
+# Return true if the SromEntry has an array type
+function srom_entry_has_array_type(entry) {
+ obj_assert_class(entry, SromEntry)
+
+ return (obj_is_instanceof(get(entry, p_type), ArrayType))
+}
+
+# Return the number of array elements defined by this SromEntry's type,
+# or 1 if the entry does not have an array type.
+function srom_entry_get_array_len(entry, _type) {
+ obj_assert_class(entry, SromEntry)
+
+ return (type_get_nelem(get(entry, p_type)))
+}
+
+#
+# Return true if the given entry should be included in the output bindings
+# generated for the given revision, false otherwise.
+#
+function srom_entry_should_output(entry, rev, _var, _revs)
+{
+ obj_assert_class(entry, SromEntry)
+
+ _var = get(entry, p_var)
+ _revs = get(entry, p_revisions)
+
+ # Exclude internal variables
+ if (var_is_internal(_var))
+ return (0)
+
+ # Exclude inapplicable entry revisions
+ if (!revrange_contains(_revs, rev))
+ return (0)
+
+ return (1)
+}
+
+#
+# Return the single, non-shifted, non-masked offset/segment for the given
+# SromEntry, or throw an error if the entry contains multiple offsets/segments.
+#
+# This is used to fetch special-cased variable definitions that are required
+# to present a single simple offset.
+#
+function srom_entry_get_single_segment(entry, _offsets, _segments, _seg,
+ _base_type, _default_mask)
+{
+ obj_assert_class(entry, SromEntry)
+
+ # Fetch the single offset's segment list
+ _offsets = get(entry, p_offsets)
+ if (array_size(_offsets) != 1)
+ errorc(get(entry, p_line), "unsupported offset count")
+
+ _segments = get(array_first(_offsets), p_segments)
+ if (array_size(_segments) != 1)
+ errorc(get(entry, p_line), "unsupported segment count")
+
+ # Fetch the single segment
+ _seg = array_first(_segments)
+ _base_type = srom_segment_get_base_type(_seg)
+ _default_mask = get(_base_type, p_mask)
+
+ # Must not be shifted/masked
+ if (get(_seg, p_shift) != 0)
+ errorc(obj_get_prop_nr(_seg, p_mask), "shift unsupported")
+
+ if (get(_seg, p_mask) != _default_mask)
+ errorc(obj_get_prop_nr(_seg, p_mask), "mask unsupported")
+
+ return (_seg)
+}
+
+# Create a new SromOffset instance
+function srom_offset_new(_obj) {
+ _obj = obj_new(SromOffset)
+ set(_obj, p_segments, array_new())
+ set(_obj, p_line, NR)
+
+ return (_obj)
+}
+
+# Return the number of SromSegment instances defined by this offset.
+function srom_offset_segment_count(offset) {
+ obj_assert_class(offset, SromOffset)
+ return (array_size(get(offset, p_segments)))
+}
+
+# Return the idx'th segment. Will throw an error if idx falls outside
+# the number of available segments.
+function srom_offset_get_segment(offset, idx, _segments, _seg) {
+ obj_assert_class(offset, SromOffset)
+
+ return (array_get(get(offset, p_segments), idx))
+}
+
+# Create a new SromSegment instance
+function srom_segment_new(offset, type, mask, shift, value, _obj) {
+ _obj = obj_new(SromSegment)
+ set(_obj, p_offset, offset)
+ set(_obj, p_type, type)
+ set(_obj, p_mask, mask)
+ set(_obj, p_shift, shift)
+ set(_obj, p_value, value)
+ set(_obj, p_line, NR)
+
+ return (_obj)
+}
+
+# Return true if the segment has an array type
+function srom_segment_has_array_type(seg, _type) {
+ _type = srom_segment_get_type(seg)
+ return (obj_is_instanceof(_type, ArrayType))
+}
+
+# Return the array count of the segment, or 1 if the segment does not have
+# an array type
+function srom_segment_get_array_len(seg, _type) {
+ if (!srom_segment_has_array_type(seg))
+ return (1)
+
+ _type = srom_segment_get_type(seg)
+ return (get(_type, p_count))
+}
+
+# Return the type of the segment
+function srom_segment_get_type(seg) {
+ obj_assert_class(seg, SromSegment)
+ return (get(seg, p_type))
+
+}
+
+# Return the base type of the segment
+function srom_segment_get_base_type(seg) {
+ return (type_get_base(srom_segment_get_type(seg)))
+}
+
+# Return true if the two segments have identical types and attributes (i.e.
+# differing only by offset)
+function srom_segment_attributes_equal(lhs, rhs) {
+ obj_assert_class(lhs, SromSegment)
+ obj_assert_class(rhs, SromSegment)
+
+ # type
+ if (!type_equal(get(lhs, p_type), get(rhs, p_type)))
+ return (0)
+
+ # mask
+ if (get(lhs, p_mask) != get(rhs, p_mask))
+ return (0)
+
+ # shift
+ if (get(lhs, p_shift) != get(rhs, p_shift))
+ return (0)
+
+ # value
+ if (get(lhs, p_value) != get(rhs, p_value))
+ return (0)
+
+ return (1)
+}
+
+# Return a human-readable representation of a Segment instance
+function segment_to_string(seg, _str, _t, _m, _s, _attrs, _attr_str) {
+ _attrs = array_new()
+
+ # include type (if specified)
+ if ((_t = get(seg, p_type)) != null)
+ _str = (type_to_string(_t) " ")
+
+ # include offset
+ _str = (_str sprintf("0x%X", get(seg, p_offset)))
+
+ # append list of attributes
+ if ((_m = get(seg, p_mask)) != null)
+ array_append(_attrs, ("&" _m))
+
+ if ((_s = get(seg, p_shift)) != null) {
+ if (_s > 0)
+ _s = ">>" _s
+ else
+ _s = "<<" _s
+ array_append(_attrs, _s)
+ }
+
+ _attr_str = array_join(_attrs, ", ")
+ obj_delete(_attrs)
+
+ if (_attr_str == "")
+ return (_str)
+ else
+ return (_str " (" _attr_str ")")
+}
+
+# return the flag definition for variable `v`
+function gen_var_flags(v, _type, _flags, _flag, _str)
+{
+ _num_flags = 0;
+ _type = get(v, p_type)
+ _flags = array_new()
+
+ # VF_PRIVATE
+ if (get(v, p_access) == VAccessPrivate)
+ array_append(_flags, VFlagPrivate)
+
+ # VF_IGNALL1
+ if (get(v, p_ignall1))
+ array_append(_flags, VFlagIgnoreAll1)
+
+ # If empty, return empty flag value
+ if (array_size(_flags) == 0) {
+ obj_delete(_flags)
+ return ("0")
+ }
+
+ # Join all flag constants with |
+ _str = array_join(_flags, "|", class_get_prop_id(VFlag, p_const))
+
+ # Clean up
+ obj_delete(_flags)
+
+ return (_str)
+}
+
+#
+# Return the absolute value
+#
+function abs(i) {
+ return (i < 0 ? -i : i)
+}
+
+#
+# Return the minimum of two values
+#
+function min(lhs, rhs) {
+ return (lhs < rhs ? lhs : rhs)
+}
+
+#
+# Return the maximum of two values
+#
+function max(lhs, rhs) {
+ return (lhs > rhs ? lhs : rhs)
+}
+
+#
+# Parse a hex string
+#
+function parse_hex_string(str, _hex_pstate, _out, _p, _count) {
+ if (!AWK_REQ_HEX_PARSING)
+ return (str + 0)
+
+ # Populate hex parsing lookup table on-demand
+ if (!("F" in _g_hex_table)) {
+ for (_p = 0; _p < 16; _p++) {
+ _g_hex_table[sprintf("%X", _p)] = _p
+ _g_hex_table[sprintf("%x", _p)] = _p
+ }
+ }
+
+ # Split input into an array
+ _count = split(toupper(str), _hex_pstate, "")
+ _p = 1
+
+ # Skip leading '0x'
+ if (_count >= 2 && _hex_pstate[1] == "0") {
+ if (_hex_pstate[2] == "x" || _hex_pstate[2] == "X")
+ _p += 2
+ }
+
+ # Parse the hex_digits
+ _out = 0
+ for (; _p <= _count; _p++)
+ _out = (_out * 16) + _g_hex_table[_hex_pstate[_p]]
- while (array[_qright] > _qpivot_val)
- _qright--
+ return (_out)
+}
- # swap
- if (_qleft <= _qright) {
- _qleft_val = array[_qleft]
- _qright_val = array[_qright]
-
- array[_qleft] = _qright_val
- array[_qright] = _qleft_val
+#
+# Return the integer representation of an unsigned decimal, hexadecimal, or
+# octal string
+#
+function parse_uint_string(str) {
+ if (str ~ UINT_REGEX)
+ return (int(str))
+ else if (str ~ HEX_REGEX)
+ return (parse_hex_string(str))
+ else
+ error("invalid integer value: '" str "'")
+}
- _qleft++
- _qright--
- }
- }
+#
+# Parse an offset string, stripping any leading '+' or trailing ':' or ','
+# characters
+#
+# +0x0:
+# 0x0,
+# ...
+#
+function parse_uint_offset(str) {
+ # Drop any leading '+'
+ sub(/^\+/, "", str)
- # sort the partitions
- _qsort(array, first, _qright)
- _qsort(array, _qleft, last)
+ # Drop any trailing ':', ',', or '|'
+ sub("[,|:]$", "", str)
+
+ # Parse the cleaned up string
+ return (parse_uint_string(str))
}
#
# Print msg to output file, without indentation
#
-function emit_ni (msg)
-{
+function emit_ni(msg) {
printf("%s", msg) >> OUTPUT_FILE
}
#
# Print msg to output file, indented for the current `output_depth`
#
-function emit (msg)
-{
+function emit(msg, _ind) {
for (_ind = 0; _ind < output_depth; _ind++)
emit_ni("\t")
@@ -551,24 +3457,36 @@
#
# Print a warning to stderr
#
-function warn (msg)
-{
+function warn(msg) {
print "warning:", msg, "at", FILENAME, "line", NR > "/dev/stderr"
}
#
+# Print an warning message without including the source line information
+#
+function warnx(msg) {
+ print "warning:", msg > "/dev/stderr"
+}
+
+#
+# Print a compiler error to stderr with a caller supplied
+# line number
+#
+function errorc(line, msg) {
+ errorx(msg " at " FILENAME " line " line)
+}
+
+#
# Print a compiler error to stderr
#
-function error (msg)
-{
+function error(msg) {
errorx(msg " at " FILENAME " line " NR ":\n\t" $0)
}
#
# Print an error message without including the source line information
#
-function errorx (msg)
-{
+function errorx(msg) {
print "error:", msg > "/dev/stderr"
_EARLY_EXIT=1
exit 1
@@ -577,33 +3495,18 @@
#
# Print a debug output message
#
-function debug (msg)
-{
+function debug(msg, _i) {
if (!DEBUG)
return
- for (_di = 0; _di < depth; _di++)
+ for (_i = 1; _i < _g_parse_stack_depth; _i++)
printf("\t") > "/dev/stderr"
print msg > "/dev/stderr"
}
#
-# Return an array key composed of the given (parent, selector, child)
-# tuple.
-# The child argument is optional and may be omitted.
-#
-function subkey (parent, selector, child)
-{
- if (child != null)
- return (parent SUBSEP selector SUBSEP child)
- else
- return (parent SUBSEP selector)
-}
-
-#
# Advance to the next non-comment input record
#
-function next_line ()
-{
+function next_line(_result) {
do {
_result = getline
} while (_result > 0 && $0 ~ /^[ \t]*#.*/) # skip comment lines
@@ -613,8 +3516,7 @@
#
# Advance to the next input record and verify that it matches @p regex
#
-function getline_matching (regex)
-{
+function getline_matching(regex, _result) {
_result = next_line()
if (_result <= 0)
return (_result)
@@ -631,532 +3533,668 @@
# If all fields are consumed and the optional do_getline argument is true,
# read the next line.
#
-function shiftf (n, do_getline)
-{
- if (n > NF) error("shift past end of line")
- for (_si = 1; _si <= NF-n; _si++) {
- $(_si) = $(_si+n)
+function shiftf(n, do_getline, _i) {
+ if (n > NF)
+ error("shift past end of line")
+
+ if (n == NF) {
+ # If shifting the entire line, just reset the line value
+ $0 = ""
+ } else {
+ for (_i = 1; _i <= NF-n; _i++) {
+ $(_i) = $(_i+n)
+ }
+ NF = NF - n
}
- NF = NF - n
if (NF == 0 && do_getline)
next_line()
}
-#
-# Parse a revision descriptor from the current line.
-#
-function parse_revdesc (result)
-{
- _rstart = 0
- _rend = 0
+# Push a new parser state.
+function parser_state_push(ctx, is_block, _state) {
+ _state = obj_new(ParseState)
+ set(_state, p_ctx, ctx)
+ set(_state, p_is_block, is_block)
+ set(_state, p_line, NR)
+
+ _g_parse_stack_depth++
+ _g_parse_stack[_g_parse_stack_depth] = _state
+}
- if ($2 ~ "[0-9]*-[0-9*]") {
- split($2, _revrange, "[ \t]*-[ \t]*")
- _rstart = _revrange[1]
- _rend = _revrange[2]
- } else if ($2 ~ "(>|>=|<|<=)" && $3 ~ "[1-9][0-9]*") {
- if ($2 == ">") {
- _rstart = int($3)+1
- _rend = REV_MAX
- } else if ($2 == ">=") {
- _rstart = int($3)
- _rend = REV_MAX
- } else if ($2 == "<" && int($3) > 0) {
- _rstart = 0
- _rend = int($3)-1
- } else if ($2 == "<=") {
- _rstart = 0
- _rend = int($3)-1
- } else {
- error("invalid revision descriptor")
- }
- } else if ($2 ~ "[1-9][0-9]*") {
- _rstart = int($2)
- _rend = int($2)
- } else {
- error("invalid revision descriptor")
- }
+# Fetch the current parser state
+function parser_state_get() {
+ if (_g_parse_stack_depth == 0)
+ errorx("parser_state_get() called with empty parse stack")
- result[REV_START] = _rstart
- result[REV_END] = _rend
+ return (_g_parse_stack[_g_parse_stack_depth])
}
-#
-# Push a new parser state.
-#
-# The name may be null, in which case the STATE_IDENT variable will not be
-# defined in this scope
-#
-function push_state (type, name, block) {
- depth++
- push(STATE_LINENO, NR)
- if (name != null)
- push(STATE_IDENT, name)
- push(STATE_TYPE, type)
- push(STATE_ISBLOCK, block)
+# Pop the current parser state
+function parser_state_pop(_block_state, _closes_block) {
+ if (_g_parse_stack_depth == 0)
+ errorx("parser_state_pop() called with empty parse stack")
+
+ _closes_block = get(parser_state_get(), p_is_block)
+
+ delete _g_parse_stack[_g_parse_stack_depth]
+ _g_parse_stack_depth--
+
+ if (_closes_block)
+ debug("}")
}
-#
-# Pop the top of the parser state stack.
-#
-function pop_state () {
- # drop all symbols defined at this depth
- for (s in symbols) {
- if (s ~ "^"depth"[^0-9]")
- delete symbols[s]
+# Fetch the current context object associated with this parser state
+# The object will be asserted as being an instance of the given class.
+function parser_state_get_context(class, _ctx_obj) {
+ _ctx_obj = get(parser_state_get(), p_ctx)
+ obj_assert_class(_ctx_obj, class)
+
+ return (_ctx_obj)
+}
+
+# Walk the parser state stack until a context object of the given class
+# is found. If the top of the stack is reached without finding a context object
+# of the requested type, an error will be thrown.
+function parser_state_find_context(class, _state, _ctx, _i) {
+ if (class == null)
+ errorx("parser_state_find_context() called with null class")
+
+ # Find the first context instance inheriting from `class`
+ for (_i = 0; _i < _g_parse_stack_depth; _i++) {
+ _state = _g_parse_stack[_g_parse_stack_depth - _i]
+ _ctx = get(_state, p_ctx)
+
+ # Check for match
+ if (obj_is_instanceof(_ctx, class))
+ return (_ctx)
}
- depth--
+
+ # Not found
+ errorx("no context instance of type '" class_get_name(class) "' " \
+ "found in parse stack")
}
#
# Find opening brace and push a new parser state for a brace-delimited block.
#
-# The name may be null, in which case the STATE_IDENT variable will not be
-# defined in this scope
-#
-function open_block (type, name)
-{
+function parser_state_open_block(ctx) {
if ($0 ~ "{" || getline_matching("^[ \t]*{") > 0) {
- push_state(type, name, 1)
- sub("^[^{]+{", "", $0)
+ parser_state_push(ctx, 1)
+ sub("^[^{]*{", "", $0)
return
}
- error("found '"$1 "' instead of expected '{' for '" name "'")
+ error("found '"$1 "' instead of expected '{'")
}
#
# Find closing brace and pop parser states until the first
# brace-delimited block is discarded.
#
-function close_block ()
-{
+function parser_state_close_block(_next_state, _found_block) {
if ($0 !~ "}")
error("internal error - no closing brace")
# pop states until we exit the first enclosing block
do {
- _closed_block = g(STATE_ISBLOCK)
- pop_state()
- } while (!_closed_block)
+ _next_state = parser_state_get()
+ _found_block = get(_next_state, p_is_block)
+ parser_state_pop()
+ } while (!_found_block)
# strip everything prior to the block closure
sub("^[^}]*}", "", $0)
}
-# Internal symbol table lookup function. Returns the symbol depth if
-# name is found at or above scope; if scope is null, it defauls to 0
-function _find_sym (name, scope)
-{
- if (scope == null)
- scope = 0;
-
- for (i = scope; i < depth; i++) {
- if ((depth-i,name) in symbols)
- return (depth-i)
- }
+# Evaluates to true if the current parser state is defined with a context of
+# the given class
+function in_parser_context(class, _ctx) {
+ if (class == null)
+ errorx("called in_parser_context() with null class")
- return (-1)
+ _ctx = get(parser_state_get(), p_ctx)
+ return (obj_is_instanceof(_ctx, class))
}
#
-# Look up a variable in the symbol table with `name` and return its value.
+# Parse and return a revision range from the current line.
#
-# If `scope` is not null, the variable search will start at the provided
-# scope level -- 0 is the current scope, 1 is the parent's scope, etc.
+# 4
+# 4-10 # revisions 4-10, inclusive
+# > 4
+# < 4
+# >= 4
+# <= 4
#
-function g (name, scope)
-{
- _g_depth = _find_sym(name, scope)
- if (_g_depth < 0)
- error("'" name "' is undefined")
+function parse_revrange(_start, _end, _robj) {
+ _start = 0
+ _end = 0
- return (symbols[_g_depth,name])
-}
+ if ($2 ~ "[0-9]*-[0-9*]") {
+ split($2, _g_rev_range, "[ \t]*-[ \t]*")
+ _start = int(_g_rev_range[1])
+ _end = int(_g_rev_range[2])
+ } else if ($2 ~ "(>|>=|<|<=)" && $3 ~ "[1-9][0-9]*") {
+ if ($2 == ">") {
+ _start = int($3)+1
+ _end = REV_MAX
+ } else if ($2 == ">=") {
+ _start = int($3)
+ _end = REV_MAX
+ } else if ($2 == "<" && int($3) > 0) {
+ _start = 0
+ _end = int($3)-1
+ } else if ($2 == "<=") {
+ _start = 0
+ _end = int($3)-1
+ } else {
+ error("invalid revision descriptor")
+ }
+ } else if ($2 ~ "[1-9][0-9]*") {
+ _start = int($2)
+ _end = int($2)
+ } else {
+ error("invalid revision descriptor")
+ }
-function is_defined (name, scope)
-{
- return (_find_sym(name, scope) >= 0)
+ return (revrange_new(_start, _end))
}
-# Define a new variable in the symbol table's current scope,
-# with the given value
-function push (name, value)
-{
- symbols[depth,name] = value
-}
+#
+# Parse a variable group block starting at the current line
+#
+# group "Group Name" {
+# u8 var_name[10] {
+# ...
+# }
+# ...
+# }
+#
+function parse_variable_group(_ctx, _groups, _group, _group_name) {
+ _ctx = parser_state_get_context(NVRAM)
-# Set an existing variable's value in the symbol table; if not yet defined,
-# will trigger an error
-function set (name, value, scope)
-{
- for (i = 0; i < depth; i++) {
- if ((depth-i,name) in symbols) {
- symbols[depth-i,name] = value
- return
- }
+ # Seek to the start of the name string
+ shiftf(1)
+
+ # Parse the first line
+ _group_name = stringconstant_parse_line($0)
+
+ # Incrementally parse line continuations
+ while (get(_group_name, p_continued)) {
+ getline
+ stringconstant_append_line(_group_name, $0)
}
- # No existing value, cannot define
- error("'" name "' is undefined")
-}
-# Evaluates to true if immediately within a block scope of the given type
-function in_state (type)
-{
- if (!is_defined(STATE_TYPE))
- return (type == ST_NONE)
+ debug("group \"" get(_group_name, p_value) "\" {")
- return (type == g(STATE_TYPE))
-}
+ # Register the new variable group
+ _groups = get(_ctx, p_var_groups)
+ _group = var_group_new(_group_name)
+ array_append(_groups, _group)
-# Evaluates to true if within an immediate or non-immediate block scope of the
-# given type
-function in_nested_state (type)
-{
- for (i = 0; i < depth; i++) {
- if ((depth-i,STATE_TYPE) in symbols) {
- if (symbols[depth-i,STATE_TYPE] == type)
- return (1)
- }
- }
- return (0)
+ # Push our variable group block
+ parser_state_open_block(_group)
}
-# Evaluates to true if definitions of the given type are permitted within
-# the current scope
-function allow_def (type)
-{
- if (type == ST_VAR_BLOCK) {
- return (in_state(ST_NONE) || in_state(ST_STRUCT_BLOCK))
- } else if (type == ST_STRUCT_BLOCK) {
- return (in_state(ST_NONE))
- } else if (type == ST_SROM_DEFN) {
- return (in_state(ST_VAR_BLOCK) || in_state(ST_STRUCT_BLOCK))
- }
- error("unknown type '" type "'")
-}
+#
+# Parse a variable definition block starting at the current line
+#
+# u8 var_name[10] {
+# all1 ignore
+# desc ...
+# }
+#
+function parse_variable_defn(_ctx, _vaccess, _type, _name, _fmt, _var,
+ _var_list)
+{
+ _ctx = parser_state_get_context(SymbolContext)
-# struct definition
-$1 == ST_STRUCT_BLOCK && allow_def($1) {
- name = $2
+ # Check for access modifier
+ if ($1 == "private") {
+ _vaccess = VAccessPrivate
+ shiftf(1)
+ } else if ($1 == "internal") {
+ _vaccess = VAccessInternal
+ shiftf(1)
+ } else {
+ _vaccess = VAccessPublic
+ }
- # Remove array[] specifier
- if (sub(/\[\]$/, "", name) == 0)
- error("expected '" name "[]', not '" name "'")
+ # Find the base type
+ if ((_type = type_named($1)) == null)
+ error("unknown type '" $1 "'")
- if (name !~ IDENT_REGEX || name ~ TYPES_REGEX)
- error("invalid identifier '" name "'")
+ # Parse (and trim) any array specifier from the variable name
+ _name = $2
+ _type = parse_array_type_specifier(_name, _type)
+ sub(ARRAY_REGEX"$", "", _name)
- # Add top-level struct entry
- if ((name,DEF_LINE) in structs)
- error("struct identifier '" name "' previously defined on " \
- "line " structs[name,DEF_LINE])
- structs[name,DEF_LINE] = NR
- structs[name,NUM_REVS] = 0
+ # Look for an existing variable definition
+ if (_name in _g_var_names) {
+ error("variable identifier '" _name "' previously defined at " \
+ "line " get(_g_var_names[_name], p_line))
+ }
- # Open the block
- debug("struct " name " {")
- open_block(ST_STRUCT_BLOCK, name)
-}
+ # Construct new variable instance
+ _var = var_new(_vaccess, _name, _type)
+ debug((_private ? "private " : "") type_to_string(_type) " " _name " {")
-# struct srom descriptor
-$1 == ST_SROM_DEFN && allow_def(ST_SROM_DEFN) && in_state(ST_STRUCT_BLOCK) {
- sid = g(STATE_IDENT)
+ # Register in global name table
+ _g_var_names[_name] = _var
- # parse revision descriptor
- rev_desc[REV_START] = 0
- parse_revdesc(rev_desc)
+ # Add to our parent context
+ _var_list = get(_ctx, p_vars)
+ array_append(_var_list, _var)
- # assign revision id
- rev = structs[sid,NUM_REVS] ""
- revk = subkey(sid, REV, rev)
- structs[sid,NUM_REVS]++
+ # Push our variable definition block
+ parser_state_open_block(_var)
+}
- # init basic revision state
- structs[revk,REV_START] = rev_desc[REV_START]
- structs[revk,REV_END] = rev_desc[REV_END]
- if (match($0, "\\[[^]]*\\]") <= 0)
- error("expected base address array")
+#
+# Return a string containing the human-readable list of valid Fmt names
+#
+function fmt_get_human_readable_list(_result, _fmts, _fmt, _nfmts, _i)
+{
+ # Build up a string listing the valid formats
+ _fmts = map_to_array(ValueFormats)
+ _result = ""
- addrs_str = substr($0, RSTART+1, RLENGTH-2)
- num_offs = split(addrs_str, addrs, ",[ \t]*")
- structs[revk, REV_NUM_OFFS] = num_offs
- for (i = 1; i <= num_offs; i++) {
- offk = subkey(revk, OFF, (i-1) "")
+ _nfmts = array_size(_fmts)
+ for (_i = 0; _i < _nfmts; _i++) {
+ _fmt = array_get(_fmts, _i)
+ if (_i+1 == _nfmts)
+ _result = _result "or "
- if (addrs[i] !~ HEX_REGEX)
- error("invalid base address '" addrs[i] "'")
+ _result = _name_str \
+ "'" get(_fmt, p_name) "'"
- structs[offk,SEG_ADDR] = addrs[i]
+ if (_i+1 < _nfmts)
+ _result = _result ", "
}
- debug("struct_srom " structs[revk,REV_START] "... [" addrs_str "]")
- next
+ obj_delete(_fmts)
+ return (_result)
}
-# close any previous srom revision descriptor
-$1 == ST_SROM_DEFN && in_state(ST_SROM_DEFN) {
- pop_state()
-}
+#
+# Parse a variable parameter from the current line
+#
+# fmt (decimal|hex|macaddr|...)
+# all1 ignore
+# desc "quoted string"
+# help "quoted string"
+#
+function parse_variable_param(param_name, _var, _vprops, _prop_id, _pval) {
+ _var = parser_state_get_context(Var)
+
+ if (param_name == "fmt") {
+ debug($1 " " $2)
-# open a new srom revision descriptor
-$1 == ST_SROM_DEFN && allow_def(ST_SROM_DEFN) {
- # parse revision descriptor
- parse_revdesc(rev_desc)
+ # Check for an existing definition
+ if ((_pval = get(_var, p_fmt)) != null) {
+ error("fmt previously specified on line " \
+ obj_get_prop_nr(_var, p_fmt))
+ }
- # assign revision id
- vid = g(STATE_IDENT)
- rev = vars[vid,NUM_REVS] ""
- revk = subkey(vid, REV, rev)
- vars[vid,NUM_REVS]++
+ # Validate arguments
+ if (NF != 2) {
+ error("'" $1 "' requires a single parameter value of " \
+ fmt_get_human_readable_list())
+ }
- # vend scoped rev/revk variables for use in the
- # revision offset block
- push("rev_id", rev)
- push("rev_key", revk)
+ if ((_pval = fmt_named($2)) == null) {
+ error("'" $1 "' value '" $2 "' unrecognized. Must be " \
+ "one of " fmt_get_human_readable_list())
+ }
- # init basic revision state
- vars[revk,DEF_LINE] = NR
- vars[revk,REV_START] = rev_desc[REV_START]
- vars[revk,REV_END] = rev_desc[REV_END]
- vars[revk,REV_NUM_OFFS] = 0
+ # Set fmt reference
+ set(_var, p_fmt, _pval)
+ } else if (param_name == "all1") {
+ debug($1 " " $2)
+
+ # Check for an existing definition
+ if ((_pval = get(_var, p_ignall1)) != null) {
+ error("all1 previously specified on line " \
+ obj_get_prop_nr(_var, p_ignall1))
+ }
- debug("srom " rev_desc[REV_START] "-" rev_desc[REV_END] " {")
- push_state(ST_SROM_DEFN, null, 0)
+ # Check argument
+ if (NF != 2)
+ error("'" $1 "'requires a single 'ignore' argument")
+ else if ($2 != "ignore")
+ error("unknown "$1" value '"$2"', expected 'ignore'")
+
+ # Set variable property
+ set(_var, p_ignall1, 1)
+ } else if (param_name == "desc" || param_name == "help") {
+ # Fetch an indirect property reference for either the 'desc'
+ # or 'help' property
+ _prop_id = obj_get_named_prop_id(_var, param_name)
+
+ # Check for an existing definition
+ if ((_pval = prop_get(_var, _prop_id)) != null) {
+ error(get(_var, p_name) " '" $1 "' redefined " \
+ "(previously defined on line " \
+ obj_get_prop_id_nr(_var, _prop_id) ")")
+ }
- # seek to the first offset definition
- do {
+ # Seek to the start of the desc/help string
shiftf(1)
- } while ($1 !~ SROM_OFF_REGEX && NF > 0)
+
+ # Parse the first line
+ _pval = stringconstant_parse_line($0)
+
+ # Incrementally parse line continuations
+ while (get(_pval, p_continued)) {
+ getline
+ stringconstant_append_line(_pval, $0)
+ }
+
+ debug(param_name " \"" get(_pval, p_value) "\"")
+
+ # Add to the var object
+ prop_set(_var, _prop_id, _pval)
+ } else {
+ error("unknown variable property type: '" param_name "'")
+ }
}
+
#
-# Extract and return the array length from the given type string.
-# Returns -1 if the type is not an array.
+# Parse a top-level SROM layout block starting at the current line
#
-function type_array_len (type)
-{
- # extract byte count[] and width
- if (match(type, ARRAY_REGEX"$") > 0) {
- return (substr(type, RSTART+1, RLENGTH-2))
- } else {
- return (-1)
- }
+# srom 4-7 {
+# 0x000: ...
+# }
+#
+function parse_srom_layout(_nvram, _srom_layouts, _revs, _layout) {
+ _nvram = parser_state_get_context(NVRAM)
+ _srom_layouts = get(_nvram, p_srom_layouts)
+
+ # Parse revision descriptor and register SROM
+ # instance
+ _revs = parse_revrange()
+ _layout = srom_layout_new(_revs)
+ nvram_add_srom_layout(_nvram, _layout)
+
+ debug("srom " revrange_to_string(_revs) " {")
+
+ # Push new SROM parser state
+ parser_state_open_block(_layout)
}
+
#
-# Parse an offset declaration from the current line.
+# Parse a nested srom range filter block starting at the current line
+# srom 4-7 {
+# # Filter block
+# srom 5 {
+# 0x000: ...
+# }
+# }
#
-function parse_offset_segment (revk, offk)
-{
- vid = g(STATE_IDENT)
+function parse_srom_layout_filter(_parent, _revs, _filter) {
+ _parent = parser_state_get_context(SromLayout)
- # use explicit type if specified, otherwise use the variable's
- # common type
- if ($1 !~ HEX_REGEX) {
- type = $1
- if (type !~ TYPES_REGEX)
- error("unknown field type '" type "'")
+ # Parse revision descriptor
+ _revs = parse_revrange()
- shiftf(1)
- } else {
- type = vars[vid,VAR_TYPE]
- }
+ # Construct the filter (which also validates the revision range)
+ _filter = srom_layout_filter_new(_parent, _revs)
- # read offset value
- offset = $1
- if (offset !~ HEX_REGEX)
- error("invalid offset value '" offset "'")
+ debug("srom " revrange_to_string(_revs) " {")
- # extract byte count[], base type, and width
- if (match(type, ARRAY_REGEX"$") > 0) {
- count = int(substr(type, RSTART+1, RLENGTH-2))
- type = substr(type, 1, RSTART-1)
- } else {
- count = 1
- }
- width = TSIZE[type]
+ # Push new SROM parser state
+ parser_state_open_block(_filter)
+}
- # seek to attributes or end of the offset expr
+
+#
+# Parse a SROM offset segment's attribute list from the current line
+#
+# <empty line>
+# (&0xF0, >>4, =0x5340)
+# ()
+#
+# Attribute designators:
+# &0xF Mask value with 0xF
+# <<4 Shift left 4 bits
+# >>4 Shift right 4 bits
+# =0x53 The parsed value must be equal to this constant value
+#
+# May be followed by a | indicating that this segment should be OR'd with the
+# segment that follows, or a terminating , indicating that a new offset's
+# list of segments may follow.
+#
+function parse_srom_segment_attributes(offset, type, _attrs, _num_attr, _attr,
+ _mask, _shift, _value, _i)
+{
+ # seek to offset (attributes...) or end of the offset expr (|,)
sub("^[^,(|){}]+", "", $0)
- # parse attributes
- mask=TMASK[type]
- shift=0
+ # defaults
+ _mask = type_get_default_mask(type)
+ _shift = 0
+ # parse attributes
if ($1 ~ "^\\(") {
# extract attribute list
- if (match($0, "\\([^|\(\)]*\\)") <= 0)
+ if (match($0, /\([^|\(\)]*\)/) <= 0)
error("expected attribute list")
- attr_str = substr($0, RSTART+1, RLENGTH-2)
- # drop from input line
+ _attrs = substr($0, RSTART+1, RLENGTH-2)
+
+ # drop attribute list from the input line
$0 = substr($0, RSTART+RLENGTH, length($0) - RSTART+RLENGTH)
# parse attributes
- num_attr = split(attr_str, attrs, ",[ \t]*")
- for (i = 1; i <= num_attr; i++) {
- attr = attrs[i]
- if (sub("^&[ \t]*", "", attr) > 0) {
- mask = attr
- } else if (sub("^<<[ \t]*", "", attr) > 0) {
- shift = "-"attr
- } else if (sub("^>>[ \t]*", "", attr) > 0) {
- shift = attr
+ _num_attr = split(_attrs, _g_attrs, ",[ \t]*")
+ for (_i = 1; _i <= _num_attr; _i++) {
+ _attr = _g_attrs[_i]
+
+ if (sub("^&[ \t]*", "", _attr) > 0) {
+ _mask = parse_uint_string(_attr)
+ } else if (sub("^<<[ \t]*", "", _attr) > 0) {
+ _shift = - parse_uint_string(_attr)
+ } else if (sub("^>>[ \t]*", "", _attr) > 0) {
+ _shift = parse_uint_string(_attr)
+ } else if (sub("^=[ \t]*", "", _attr) > 0) {
+ _value = _attr
} else {
- error("unknown attribute '" attr "'")
+ error("unknown attribute '" _attr "'")
}
}
}
- # assign segment id
- seg = vars[offk,OFF_NUM_SEGS] ""
- segk = subkey(offk, OFF_SEG, seg)
- vars[offk,OFF_NUM_SEGS]++
-
- vars[segk,SEG_ADDR] = offset + (width * _oi)
- vars[segk,SEG_COUNT] = count
- vars[segk,SEG_TYPE] = type
- vars[segk,SEG_MASK] = mask
- vars[segk,SEG_SHIFT] = shift
-
- debug("{"vars[segk,SEG_ADDR]", "type", "mask", "shift"}" \
- _comma)
+ return (srom_segment_new(offset, type, _mask, _shift, _value))
}
-# revision offset definition
-$1 ~ SROM_OFF_REGEX && in_state(ST_SROM_DEFN) {
- vid = g(STATE_IDENT)
-
- # fetch rev id/key defined by our parent block
- rev = g("rev_id")
- revk = g("rev_key")
+#
+# Parse a SROM offset's segment declaration from the current line
+#
+# +0x0: u8 (&0xF0, >>4) # read 8 bits at +0x0 (relative to srom entry
+# # offset, apply 0xF0 mask, shift >> 4
+# 0x10: u8 (&0xF0, >>4) # identical to above, but perform the read at
+# # absolute offset 0x10
+#
+# +0x0: u8 # no attributes
+# 0x10: u8
+#
+# +0x0 # simplified forms denoted by lack of ':'; the
+# 0x0 # type is inherited from the parent SromEntry
+#
+#
+function parse_srom_segment(base_offset, base_type, _simple, _type, _type_str,
+ _offset, _attrs, _num_attr, _attr, _mask, _shift, _off_desc)
+{
+ # Fetch the offset value
+ _offset = $1
+
+ # Offset string must be one of:
+ # simplified entry: <offset|+reloff>
+ # Provides only the offset, with the type inherited
+ # from the original variable definition
+ # standard entry: <offset|+reloff>:
+ # Provides the offset, followed by a type
+ #
+ # We differentiate the two by looking for (and simultaneously removing)
+ # the trailing ':'
+ if (!sub(/:$/, "", _offset))
+ _simple = 1
+
+ # The offset may either be absolute (e.g. 0x180) or relative (e.g.
+ # +0x01).
+ #
+ # If we find a relative offset definition, we must trim the leading '+'
+ # and then add the base offset
+ if (sub(/^\+/, "", _offset)) {
+ _offset = base_offset + parse_uint_offset(_offset)
+ } else {
+
+ _offset = parse_uint_offset(_offset)
+ }
- # parse all offsets
- do {
- # assign offset id
- off = vars[revk,REV_NUM_OFFS] ""
- offk = subkey(revk, OFF, off)
- vars[revk,REV_NUM_OFFS]++
+ # If simplified form, use the base type of the SROM entry. Otherwise,
+ # we need to parse the type.
+ if (_simple) {
+ _type = base_type
+ } else {
+ _type_str = $2
+ sub(/,$/, "", _type_str) # trim trailing ',', if any
- # initialize segment count
- vars[offk,DEF_LINE] = NR
- vars[offk,OFF_NUM_SEGS] = 0
+ if ((_type = parse_type_string(_type_str)) == null)
+ error("unknown type '" _type_str "'")
+ }
- debug("[")
- # parse all segments
- do {
- parse_offset_segment(revk, offk)
- _more_seg = ($1 == "|")
- if (_more_seg)
- shiftf(1, 1)
- } while (_more_seg)
- debug("],")
- _more_vals = ($1 == ",")
- if (_more_vals)
- shiftf(1, 1)
- } while (_more_vals)
+ # Parse the trailing (... attributes ...), if any
+ return (parse_srom_segment_attributes(_offset, _type))
}
-# variable definition
-(($1 == "private" && $2 ~ TYPES_REGEX) || $1 ~ TYPES_REGEX) &&
- allow_def(ST_VAR_BLOCK) \
+#
+# Parse a SROM variable entry from the current line
+# <offset>: <type> <varname><array spec> ...
+#
+function parse_srom_variable_entry(_srom, _srom_revs, _rev_start, _rev_end,
+ _srom_entries, _srom_revmap, _prev_entry, _ctx, _base_offset, _name,
+ _stype, _var, _entry, _offset, _seg, _i)
{
- # check for 'private' flag
- if ($1 == "private") {
- private = 1
- shiftf(1)
- } else {
- private = 0
+ # Fetch our parent context
+ _ctx = parser_state_get_context(SromContext)
+ _srom_revs = get(_ctx, p_revisions)
+ _rev_start = get(_srom_revs, p_start)
+ _rev_end = get(_srom_revs, p_end)
+
+ # Locate our enclosing layout
+ _srom = parser_state_find_context(SromLayout)
+ _srom_entries = get(_srom, p_entries)
+ _srom_revmap = get(_srom, p_revmap)
+
+ # Verify argument count
+ if (NF < 3) {
+ error("unrecognized srom entry syntax; must specify at " \
+ "least \"<offset>: <type> <variable name>\"")
}
- type = $1
- name = $2
- array = 0
- debug(type " " name " {")
-
- # Check for and remove any array[] specifier
- base_type = type
- if (sub(ARRAY_REGEX"$", "", base_type) > 0)
- array = 1
-
- # verify type
- if (!base_type in DTYPE)
- error("unknown type '" $1 "'")
-
- # Add top-level variable entry
- if (name in var_names)
- error("variable identifier '" name "' previously defined on " \
- "line " vars[name,DEF_LINE])
+ # Parse the base offset
+ _base_offset = parse_uint_offset($1)
+
+ # Parse the base type
+ if ((_stype = type_named($2)) == null)
+ error("unknown type '" $2 "'")
+
+ # Parse (and trim) any array specifier from the variable name
+ _name = $3
+ _stype = parse_array_type_specifier(_name, _stype)
+ sub(ARRAY_REGEX"$", "", _name)
+
+ # Locate the variable definition
+ if (!(_name in _g_var_names))
+ error("no definition found for variable '" _name "'")
+ _var = _g_var_names[_name]
+
+ # The SROM entry type must be a subtype of the variable's declared
+ # type
+ if (!type_can_represent(get(_var, p_type), _stype)) {
+ error("'" type_to_string(_stype) "' SROM value cannot be " \
+ "coerced to '" type_to_string(get(_var, p_type)) " " _name \
+ "' variable")
+ }
- var_names[name] = 0
- vars[name,VAR_NAME] = name
- vars[name,DEF_LINE] = NR
- vars[name,VAR_TYPE] = type
- vars[name,VAR_BASE_TYPE] = base_type
- vars[name,NUM_REVS] = 0
- vars[name,VAR_PRIVATE] = private
- vars[name,VAR_ARRAY] = array
- vars[name,VAR_FMT] = "hex" # default if not specified
+ # Create and register our new offset entry
+ _entry = srom_entry_new(_var, _srom_revs, _base_offset, _stype)
+ srom_layout_add_entry(_srom, _entry)
- open_block(ST_VAR_BLOCK, name)
+ # Seek to either the block start ('{'), or the attributes to be
+ # used for a single offset/segment entry at `offset`
+ shiftf(3)
- debug("type=" DTYPE[base_type])
+ # Using the block syntax? */
+ if ($1 == "{") {
+ debug(sprintf("0x%03x: %s %s {", _base_offset,
+ type_to_string(_stype), _name))
+ parser_state_open_block(_entry)
+ } else {
+ # Otherwise, we're using the simplified syntax -- create and
+ # register our implicit SromOffset
+ _offset = srom_offset_new()
+ array_append(get(_entry, p_offsets), _offset)
- if (in_nested_state(ST_STRUCT_BLOCK)) {
- # Fetch the enclosing struct's name
- sid = g(STATE_IDENT, 1)
+ # Parse and register simplified segment syntax
+ _seg = parse_srom_segment_attributes(_base_offset, _stype)
+ array_append(get(_offset, p_segments), _seg)
- # Mark as a struct-based variable
- vars[name,VAR_STRUCT] = sid
+ debug(sprintf("0x%03x: %s %s { %s }", _base_offset,
+ type_to_string(_stype), _name, segment_to_string(_seg)))
}
}
-# variable parameters
-$1 ~ IDENT_REGEX && $2 ~ IDENT_REGEX && in_state(ST_VAR_BLOCK) {
- vid = g(STATE_IDENT)
- if ($1 == PROP_T_SFMT) {
- if (!$2 in FMT)
- error("invalid fmt '" $2 "'")
+#
+# Parse all SromSegment entry segments readable starting at the current line
+#
+# <offset|+reloff>[,|]?
+# <offset|+reloff>: <type>[,|]?
+# <offset|+reloff>: <type> (<attributes>)[,|]?
+#
+function parse_srom_entry_segments(_entry, _base_off, _base_type, _offs,
+ _offset, _segs, _seg, _more_seg, _more_vals)
+{
+ _entry = parser_state_get_context(SromEntry)
+ _base_off = get(_entry, p_base_offset)
+ _offs = get(_entry, p_offsets)
- vars[vid,VAR_FMT] = $2
- debug($1 "=" FMT[$2])
- } else if ($1 == PROP_T_ALL1 && $2 == "ignore") {
- vars[vid,VAR_IGNALL1] = 1
- } else {
- error("unknown parameter " $1)
- }
- next
-}
+ _base_type = get(_entry, p_type)
+ _base_type = type_get_base(_base_type)
-# Skip comments and blank lines
-/^[ \t]*#/ || /^$/ {
- next
-}
+ # Parse all offsets
+ do {
+ # Create a SromOffset
+ _offset = srom_offset_new()
+ _segs = get(_offset, p_segments)
-# Close blocks
-/}/ && !in_state(ST_NONE) {
- while (!in_state(ST_NONE) && $0 ~ "}") {
- close_block();
- debug("}")
- }
- next
-}
+ array_append(_offs, _offset)
-# Report unbalanced '}'
-/}/ && in_state(ST_NONE) {
- error("extra '}'")
-}
+ # Parse all segments
+ do {
+ _seg = parse_srom_segment(_base_off, _base_type)
+ array_append(_segs, _seg)
-# Invalid variable type
-$1 && allow_def(ST_VAR_BLOCK) {
- error("unknown type '" $1 "'")
-}
+ # Do more segments follow?
+ _more_seg = ($1 == "|")
+ if (_more_seg)
+ shiftf(1, 1)
-# Generic parse failure
-{
- error("unrecognized statement")
+ if (_more_seg)
+ debug(segment_to_string(_seg) " |")
+ else
+ debug(segment_to_string(_seg))
+ } while (_more_seg)
+
+ # Do more offsets follow?
+ _more_vals = ($1 == ",")
+ if (_more_vals)
+ shiftf(1, 1)
+ } while (_more_vals)
}
Index: sys/dev/bwn/bwn_mac.c
===================================================================
--- sys/dev/bwn/bwn_mac.c
+++ sys/dev/bwn/bwn_mac.c
@@ -111,7 +111,7 @@
// TODO
uint8_t macaddr[6];
error = bhnd_nvram_getvar_array(dev, BHND_NVAR_MACADDR, macaddr,
- sizeof(macaddr), BHND_NVRAM_TYPE_UINT8);
+ sizeof(macaddr), BHND_NVRAM_TYPE_UINT8_ARRAY);
if (error)
device_printf(dev, "error fetching macaddr: %d\n", error);
else
Index: sys/mips/broadcom/bcm_nvram_cfe.c
===================================================================
--- /dev/null
+++ sys/mips/broadcom/bcm_nvram_cfe.c
@@ -0,0 +1,527 @@
+/*-
+ * Copyright (c) 2016 Landon Fuller <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 <dev/bhnd/nvram/bhnd_nvram_iovar.h>
+
+#include "bhnd_nvram_if.h"
+
+#include "bcm_nvram_cfevar.h"
+
+/**
+ * CFE-backed bhnd_nvram_io implementation.
+ */
+struct bhnd_nvram_iocfe {
+ struct bhnd_nvram_io io; /**< common I/O instance state */
+
+ char *dname; /**< CFE device name (borrowed) */
+ int fd; /**< CFE file descriptor */
+ size_t offset; /**< base offset */
+ size_t size; /**< device size */
+ bool req_blk_erase; /**< flash blocks must be erased
+ before writing */
+};
+
+BHND_NVRAM_IOPS_DEFN(iocfe)
+
+#define IOCFE_LOG(_io, _fmt, ...) \
+ printf("%s/%s: " _fmt, __FUNCTION__, (_io)->dname, ##__VA_ARGS__)
+
+static int bhnd_nvram_iocfe_new(struct bhnd_nvram_io **io,
+ char *dname);
+
+static struct bhnd_nvram_io *bhnd_nvram_find_cfedev(device_t dev,
+ char **dname,
+ bhnd_nvram_data_class_t **cls);
+
+/** Known CFE NVRAM device names, in probe order. */
+static char *nvram_cfe_devs[] = {
+ "nflash0.nvram", /* NAND */
+ "nflash1.nvram",
+ "flash0.nvram",
+ "flash1.nvram",
+};
+
+/** Supported CFE NVRAM formats, in probe order. */
+static bhnd_nvram_data_class_t * const nvram_cfe_fmts[] = {
+ &bhnd_nvram_bcm_class,
+ &bhnd_nvram_tlv_class
+};
+
+
+static int
+bhnd_nvram_cfe_probe(device_t dev)
+{
+ struct bhnd_nvram_io *io;
+ bhnd_nvram_data_class_t *cls;
+ const char *cls_desc;
+ char *dname;
+ char *desc;
+
+ /* Locate a usable CFE device */
+ io = bhnd_nvram_find_cfedev(dev, &dname, &cls);
+ if (io == NULL)
+ return (ENXIO);
+ bhnd_nvram_io_free(io);
+
+ /* Format the device description */
+ cls_desc = bhnd_nvram_data_class_desc(cls);
+ asprintf(&desc, M_DEVBUF, "%s CFE %s", cls_desc, dname);
+ if (desc != NULL) {
+ device_set_desc_copy(dev, desc);
+ free(desc, M_DEVBUF);
+ } else {
+ device_set_desc(dev, cls_desc);
+ }
+
+ /* Refuse wildcard attachments */
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+
+static int
+bhnd_nvram_cfe_attach(device_t dev)
+{
+ struct bhnd_nvram_cfe_softc *sc;
+ bhnd_nvram_data_class_t *cls;
+ struct bhnd_nvram_io *io;
+ char *dname;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ /* Locate NVRAM device via CFE */
+ io = bhnd_nvram_find_cfedev(dev, &dname, &cls);
+ if (io == NULL) {
+ device_printf(dev, "CFE NVRAM device not found\n");
+ return (ENXIO);
+ }
+
+ /* Initialize NVRAM store and free the I/O context */
+ error = bhnd_nvram_store_parse_new(&sc->store, io, cls);
+ bhnd_nvram_io_free(io);
+ if (error)
+ return (error);
+
+ return (error);
+}
+
+static int
+bhnd_nvram_cfe_resume(device_t dev)
+{
+ return (0);
+}
+
+static int
+bhnd_nvram_cfe_suspend(device_t dev)
+{
+ return (0);
+}
+
+static int
+bhnd_nvram_cfe_detach(device_t dev)
+{
+ struct bhnd_nvram_cfe_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bhnd_nvram_store_free(sc->store);
+
+ return (0);
+}
+
+static int
+bhnd_nvram_cfe_getvar(device_t dev, const char *name, void *buf, size_t *len,
+ bhnd_nvram_type type)
+{
+ struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev);
+
+ return (bhnd_nvram_store_getvar(sc->store, name, buf, len, type));
+}
+
+static int
+bhnd_nvram_cfe_setvar(device_t dev, const char *name, const void *buf,
+ size_t len, bhnd_nvram_type type)
+{
+ struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev);
+
+ return (bhnd_nvram_store_setvar(sc->store, name, buf, len, type));
+}
+
+/**
+ * Find, open, identify, and return an I/O context mapping our
+ * CFE NVRAM device.
+ *
+ * @param dev bhnd_nvram_cfe device.
+ * @param[out] dname On success, the CFE device name.
+ * @param[out] cls On success, the identified NVRAM data format
+ * class.
+ *
+ * @retval non-NULL success. the caller inherits ownership of the returned
+ * NVRAM I/O context.
+ * @retval NULL if no usable CFE NVRAM device could be found.
+ */
+static struct bhnd_nvram_io *
+bhnd_nvram_find_cfedev(device_t dev, char **dname,
+ bhnd_nvram_data_class_t **cls)
+{
+ struct bhnd_nvram_io *io;
+ int devinfo;
+ int error, result;
+
+ for (u_int i = 0; i < nitems(nvram_cfe_fmts); i++) {
+ *cls = nvram_cfe_fmts[i];
+
+ for (u_int j = 0; j < nitems(nvram_cfe_devs); j++) {
+ *dname = nvram_cfe_devs[j];
+
+ /* Does the device exist? */
+ if ((devinfo = cfe_getdevinfo(*dname)) < 0) {
+ if (devinfo != CFE_ERR_DEVNOTFOUND) {
+ device_printf(dev, "cfe_getdevinfo(%s) "
+ "failed: %d\n", *dname, devinfo);
+ }
+
+ continue;
+ }
+
+ /* Open for reading */
+ if ((error = bhnd_nvram_iocfe_new(&io, *dname)))
+ continue;
+
+ /* Probe */
+ result = bhnd_nvram_data_probe(*cls, io);
+ if (result <= 0) {
+ /* Found a supporting NVRAM data class */
+ return (io);
+ }
+
+ /* Keep searching */
+ bhnd_nvram_io_free(io);
+ io = NULL;
+ }
+ }
+
+ return (NULL);
+}
+
+
+/**
+ * Allocate and return a new I/O context backed by a CFE device.
+ *
+ * The caller is responsible for deallocating the returned I/O context via
+ * bhnd_nvram_io_free().
+ *
+ * @param[out] io On success, a valid I/O context for @p dname.
+ * @param dname The name of the CFE device to be opened for reading.
+ *
+ * @retval 0 success.
+ * @retval non-zero if opening @p dname otherwise fails, a standard unix error
+ * will be returned.
+ */
+static int
+bhnd_nvram_iocfe_new(struct bhnd_nvram_io **io, char *dname)
+{
+ struct bhnd_nvram_iocfe *iocfe;
+ nvram_info_t nvram_info;
+ int cerr, devinfo, dtype, rlen;
+ int64_t nv_offset;
+ u_int nv_size;
+ bool req_blk_erase;
+ int error;
+
+ iocfe = malloc(sizeof(*iocfe), M_DEVBUF, M_WAITOK);
+ iocfe->io.iops = &bhnd_nvram_iocfe_ops;
+ iocfe->dname = dname;
+
+ /* Try to open the device */
+ iocfe->fd = cfe_open(dname);
+ if (iocfe->fd <= 0) {
+ IOCFE_LOG(iocfe, "cfe_open() failed: %d\n", iocfe->fd);
+
+ error = ENXIO;
+ goto failed;
+ }
+
+ /* Try to fetch device info */
+ if ((devinfo = cfe_getdevinfo(iocfe->dname)) < 0) {
+ IOCFE_LOG(iocfe, "cfe_getdevinfo() failed: %d\n", devinfo);
+ error = ENXIO;
+ goto failed;
+ }
+
+ /* Verify device type */
+ dtype = devinfo & CFE_DEV_MASK;
+ switch (dtype) {
+ case CFE_DEV_FLASH:
+ case CFE_DEV_NVRAM:
+ /* Valid device type */
+ break;
+ default:
+ IOCFE_LOG(iocfe, "unknown device type: %d\n", dtype);
+ error = ENXIO;
+ goto failed;
+ }
+
+ /* Try to fetch nvram info from CFE */
+ cerr = cfe_ioctl(iocfe->fd, IOCTL_NVRAM_GETINFO,
+ (unsigned char *)&nvram_info, sizeof(nvram_info), &rlen, 0);
+ if (cerr == CFE_OK) {
+ /* Sanity check the result; must not be a negative integer */
+ if (nvram_info.nvram_size < 0 ||
+ nvram_info.nvram_offset < 0)
+ {
+ IOCFE_LOG(iocfe, "invalid NVRAM layout (%d/%d)\n",
+ nvram_info.nvram_size, nvram_info.nvram_offset);
+ error = ENXIO;
+ goto failed;
+ }
+
+ nv_offset = nvram_info.nvram_offset;
+ nv_size = nvram_info.nvram_size;
+ req_blk_erase = (nvram_info.nvram_eraseflg != 0);
+ } else if (cerr != CFE_OK && cerr != CFE_ERR_INV_COMMAND) {
+ IOCFE_LOG(iocfe, "IOCTL_NVRAM_GETINFO failed: %d\n", cerr);
+ error = ENXIO;
+ goto failed;
+ }
+
+ /* Fall back on flash info.
+ *
+ * This is known to be required on the Asus RT-N53 (CFE 5.70.55.33,
+ * BBP 1.0.37, BCM5358UB0), where IOCTL_NVRAM_GETINFO returns
+ * CFE_ERR_INV_COMMAND.
+ */
+ if (cerr == CFE_ERR_INV_COMMAND) {
+ flash_info_t fi;
+
+ cerr = cfe_ioctl(iocfe->fd, IOCTL_FLASH_GETINFO,
+ (unsigned char *)&fi, sizeof(fi), &rlen, 0);
+
+ if (cerr != CFE_OK) {
+ IOCFE_LOG(iocfe, "IOCTL_FLASH_GETINFO failed %d\n",
+ cerr);
+ error = ENXIO;
+ goto failed;
+ }
+
+ nv_offset = 0x0;
+ nv_size = fi.flash_size;
+ req_blk_erase = !(fi.flash_flags & FLASH_FLAG_NOERASE);
+ }
+
+
+ /* Verify that the full NVRAM layout can be represented via size_t */
+ if (nv_size > SIZE_MAX || SIZE_MAX - nv_size < nv_offset) {
+ IOCFE_LOG(iocfe, "invalid NVRAM layout (%#x/%#jx)\n",
+ nv_size, (intmax_t)nv_offset);
+ error = ENXIO;
+ goto failed;
+ }
+
+ iocfe->offset = nv_offset;
+ iocfe->size = nv_size;
+ iocfe->req_blk_erase = req_blk_erase;
+
+ *io = &iocfe->io;
+ return (CFE_OK);
+
+failed:
+ if (iocfe->fd >= 0)
+ cfe_close(iocfe->fd);
+
+ free(iocfe, M_DEVBUF);
+
+ *io = NULL;
+ return (error);
+}
+
+static void
+bhnd_nvram_iocfe_free(struct bhnd_nvram_io *io)
+{
+ struct bhnd_nvram_iocfe *iocfe = (struct bhnd_nvram_iocfe *)io;
+
+ cfe_close(iocfe->fd);
+ free(io, M_DEVBUF);
+}
+
+static size_t
+bhnd_nvram_iocfe_getsize(struct bhnd_nvram_io *io)
+{
+ struct bhnd_nvram_iocfe *iocfe = (struct bhnd_nvram_iocfe *)io;
+ return (iocfe->size);
+}
+
+static int
+bhnd_nvram_iocfe_setsize(struct bhnd_nvram_io *io, size_t size)
+{
+ /* unsupported */
+ return (ENODEV);
+}
+
+static int
+bhnd_nvram_iocfe_read_ptr(struct bhnd_nvram_io *io, size_t offset,
+ const void **ptr, size_t nbytes, size_t *navail)
+{
+ /* unsupported */
+ return (ENODEV);
+}
+
+static int
+bhnd_nvram_iocfe_write_ptr(struct bhnd_nvram_io *io, size_t offset,
+ void **ptr, size_t nbytes, size_t *navail)
+{
+ /* unsupported */
+ return (ENODEV);
+}
+
+static int
+bhnd_nvram_iocfe_write(struct bhnd_nvram_io *io, size_t offset, void *buffer,
+ size_t nbytes)
+{
+ /* unsupported */
+ return (ENODEV);
+}
+
+static int
+bhnd_nvram_iocfe_read(struct bhnd_nvram_io *io, size_t offset, void *buffer,
+ size_t nbytes)
+{
+ struct bhnd_nvram_iocfe *iocfe;
+ size_t remain;
+ int64_t cfe_offset;
+ int nr, nreq;
+
+ iocfe = (struct bhnd_nvram_iocfe *)io;
+
+ /* Determine (and validate) the base CFE offset */
+#if (SIZE_MAX > INT64_MAX)
+ if (iocfe->offset > INT64_MAX || offset > INT64_MAX)
+ return (ENXIO);
+#endif
+
+ if (INT64_MAX - offset < iocfe->offset)
+ return (ENXIO);
+
+ cfe_offset = iocfe->offset + offset;
+
+ /* Verify that cfe_offset + nbytes is representable */
+ if (INT64_MAX - cfe_offset < nbytes)
+ return (ENXIO);
+
+ /* Perform the read */
+ for (remain = nbytes; remain > 0;) {
+ void *p;
+ size_t nread;
+ int64_t cfe_noff;
+
+ nread = (nbytes - remain);
+ cfe_noff = cfe_offset + nread;
+ p = ((uint8_t *)buffer + nread);
+ nreq = ummin(INT_MAX, remain);
+
+ nr = cfe_readblk(iocfe->fd, cfe_noff, p, nreq);
+ if (nr < 0) {
+ IOCFE_LOG(iocfe, "cfe_readblk() failed: %d\n", nr);
+ return (ENXIO);
+ }
+
+ /* Check for unexpected short read */
+ if (nr == 0 && remain > 0) {
+ /* If the request fits entirely within the CFE
+ * device range, we shouldn't hit EOF */
+ if (remain < iocfe->size &&
+ iocfe->size - remain > offset)
+ {
+ IOCFE_LOG(iocfe, "cfe_readblk() returned "
+ "unexpected short read (%d/%d)\n", nr,
+ nreq);
+ return (ENXIO);
+ }
+ }
+
+ if (nr == 0)
+ break;
+
+ remain -= nr;
+ }
+
+ /* Check for short read */
+ if (remain > 0)
+ return (ENXIO);
+
+ return (0);
+}
+
+static device_method_t bhnd_nvram_cfe_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, bhnd_nvram_cfe_probe),
+ DEVMETHOD(device_attach, bhnd_nvram_cfe_attach),
+ DEVMETHOD(device_resume, bhnd_nvram_cfe_resume),
+ DEVMETHOD(device_suspend, bhnd_nvram_cfe_suspend),
+ DEVMETHOD(device_detach, bhnd_nvram_cfe_detach),
+
+ /* NVRAM interface */
+ DEVMETHOD(bhnd_nvram_getvar, bhnd_nvram_cfe_getvar),
+ DEVMETHOD(bhnd_nvram_setvar, bhnd_nvram_cfe_setvar),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(bhnd_nvram, bhnd_nvram_cfe, bhnd_nvram_cfe_methods,
+ sizeof(struct bhnd_nvram_cfe_softc));
+EARLY_DRIVER_MODULE(bhnd_nvram_cfe, nexus, bhnd_nvram_cfe,
+ bhnd_nvram_devclass, NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);
Index: sys/mips/broadcom/bcm_nvram_cfevar.h
===================================================================
--- sys/mips/broadcom/bcm_nvram_cfevar.h
+++ sys/mips/broadcom/bcm_nvram_cfevar.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2015-2016 Landon Fuller <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
@@ -29,36 +29,19 @@
* $FreeBSD$
*/
-#ifndef _BHND_NVRAM_BHND_SPROM_PARSER_H_
-#define _BHND_NVRAM_BHND_SPROM_PARSER_H_
+#ifndef _MIPS_BROADCOM_BCM_NVRAM_CFE_H_
+#define _MIPS_BROADCOM_BCM_NVRAM_CFE_H_
-#include <dev/bhnd/bhnd.h>
+#include <sys/param.h>
+#include <sys/bus.h>
-struct bhnd_sprom;
+#include <dev/bhnd/nvram/bhnd_nvram.h>
+#include <dev/bhnd/nvram/bhnd_nvram_store.h>
-int bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r,
- bus_size_t offset);
-void bhnd_sprom_fini(struct bhnd_sprom *sprom);
-int bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf,
- size_t *len, bhnd_nvram_type type);
-int bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name,
- const void *buf, size_t len, bhnd_nvram_type type);
-
-/**
- * bhnd sprom parser instance state.
- */
-struct bhnd_sprom {
- device_t dev; /**< sprom parent device */
-
- uint8_t sp_rev; /**< sprom revision */
-
- struct bhnd_resource *sp_res; /**< sprom resource. */
- bus_size_t sp_res_off; /**< offset to sprom image */
-
- uint8_t *sp_shadow; /**< sprom shadow */
- bus_size_t sp_size_max; /**< maximum possible sprom length */
- size_t sp_size; /**< shadow size */
- size_t sp_capacity; /**< shadow buffer capacity */
+/** bhnd_nvram_cfe driver instance state. */
+struct bhnd_nvram_cfe_softc {
+ device_t dev;
+ struct bhnd_nvram_store *store; /**< nvram store */
};
-#endif /* _BHND_NVRAM_BHND_SPROM_PARSER_H_ */
+#endif /* _MIPS_BROADCOM_BCM_NVRAM_CFE_H_ */
Index: sys/mips/broadcom/files.broadcom
===================================================================
--- sys/mips/broadcom/files.broadcom
+++ sys/mips/broadcom/files.broadcom
@@ -7,6 +7,8 @@
mips/broadcom/bcm_machdep.c standard
mips/broadcom/bcm_bmips.c optional siba_nexus siba
mips/broadcom/bcm_mips74k.c optional bcma_nexus bcma
+mips/broadcom/bcm_nvram_cfe.c optional bhnd siba_nexus cfe | \
+ bhnd bcma_nexus cfe
mips/broadcom/bcm_pmu.c standard
mips/mips/tick.c standard
Index: sys/modules/bhnd/Makefile
===================================================================
--- sys/modules/bhnd/Makefile
+++ sys/modules/bhnd/Makefile
@@ -27,11 +27,21 @@
SRCS+= bhnd_pmu_if.c bhnd_pmu_if.h
# NVRAM/SPROM
-SRCS+= bhnd_nvram.c \
- bhnd_nvram_parser.c \
- bhnd_sprom.c \
- bhnd_sprom_parser.c
-SRCS+= bhnd_nvram_common.c
+SRCS+= bhnd_nvram_data.c \
+ bhnd_nvram_data_bcm.c \
+ bhnd_nvram_data_bcmraw.c \
+ bhnd_nvram_data_btxt.c \
+ bhnd_nvram_data_sprom.c \
+ bhnd_nvram_data_tlv.c \
+ bhnd_nvram_io.c \
+ bhnd_nvram_iobuf.c \
+ bhnd_nvram_iores.c \
+ bhnd_nvram_store.c \
+ bhnd_nvram_subr.c \
+ bhnd_nvram_value.c \
+ bhnd_nvram_value_fmts.c \
+ bhnd_nvram_value_prf.c \
+ bhnd_sprom.c
SRCS+= bhnd_nvram_map.h bhnd_nvram_map_data.h
SRCS+= bhnd_nvram_if.c bhnd_nvram_if.h

File Metadata

Mime Type
text/plain
Expires
Wed, Aug 5, 6:50 AM (7 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
36009072
Default Alt Text
D8645.id22521.diff (715 KB)

Event Timeline