Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c
39536 views
/*-1* Copyright (c) 2016 Landon Fuller <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer,9* without modification.10* 2. Redistributions in binary form must reproduce at minimum a disclaimer11* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any12* redistribution must be conditioned upon including a substantially13* similar Disclaimer requirement for further binary redistribution.14*15* NO WARRANTY16* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY19* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL20* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,21* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF22* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS23* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER24* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)25* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF26* THE POSSIBILITY OF SUCH DAMAGES.27*/2829#include <sys/param.h>30#include <sys/endian.h>3132#ifdef _KERNEL3334#include <sys/bus.h>35#include <sys/ctype.h>36#include <sys/malloc.h>37#include <sys/systm.h>3839#else /* !_KERNEL */4041#include <ctype.h>42#include <stdint.h>43#include <stdio.h>44#include <stdlib.h>45#include <string.h>4647#endif /* _KERNEL */4849#include "bhnd_nvram_private.h"5051#include "bhnd_nvram_datavar.h"5253#include "bhnd_nvram_data_bcmreg.h"54#include "bhnd_nvram_data_bcmvar.h"5556/*57* Broadcom NVRAM data class.58*59* The Broadcom NVRAM NUL-delimited ASCII format is used by most60* Broadcom SoCs.61*62* The NVRAM data is encoded as a standard header, followed by series of63* NUL-terminated 'key=value' strings; the end of the stream is denoted64* by a single extra NUL character.65*/6667struct bhnd_nvram_bcm;6869static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_gethdrvar(70struct bhnd_nvram_bcm *bcm,71const char *name);72static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_to_hdrvar(73struct bhnd_nvram_bcm *bcm,74void *cookiep);75static size_t bhnd_nvram_bcm_hdrvar_index(76struct bhnd_nvram_bcm *bcm,77struct bhnd_nvram_bcm_hvar *hvar);78/*79* Set of BCM NVRAM header values that are required to be mirrored in the80* NVRAM data itself.81*82* If they're not included in the parsed NVRAM data, we need to vend the83* header-parsed values with their appropriate keys, and add them in any84* updates to the NVRAM data.85*86* If they're modified in NVRAM, we need to sync the changes with the87* the NVRAM header values.88*/89static const struct bhnd_nvram_bcm_hvar bhnd_nvram_bcm_hvars[] = {90{91.name = BCM_NVRAM_CFG0_SDRAM_INIT_VAR,92.type = BHND_NVRAM_TYPE_UINT16,93.len = sizeof(uint16_t),94.nelem = 1,95},96{97.name = BCM_NVRAM_CFG1_SDRAM_CFG_VAR,98.type = BHND_NVRAM_TYPE_UINT16,99.len = sizeof(uint16_t),100.nelem = 1,101},102{103.name = BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR,104.type = BHND_NVRAM_TYPE_UINT16,105.len = sizeof(uint16_t),106.nelem = 1,107},108{109.name = BCM_NVRAM_SDRAM_NCDL_VAR,110.type = BHND_NVRAM_TYPE_UINT32,111.len = sizeof(uint32_t),112.nelem = 1,113},114};115116/** BCM NVRAM data class instance */117struct bhnd_nvram_bcm {118struct bhnd_nvram_data nv; /**< common instance state */119struct bhnd_nvram_io *data; /**< backing buffer */120bhnd_nvram_plist *opts; /**< serialization options */121122/** BCM header values */123struct bhnd_nvram_bcm_hvar hvars[nitems(bhnd_nvram_bcm_hvars)];124125size_t count; /**< total variable count */126};127128BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", BHND_NVRAM_DATA_CAP_DEVPATHS,129sizeof(struct bhnd_nvram_bcm))130131static int132bhnd_nvram_bcm_probe(struct bhnd_nvram_io *io)133{134struct bhnd_nvram_bcmhdr hdr;135int error;136137if ((error = bhnd_nvram_io_read(io, 0x0, &hdr, sizeof(hdr))))138return (error);139140if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)141return (ENXIO);142143if (le32toh(hdr.size) > bhnd_nvram_io_getsize(io))144return (ENXIO);145146return (BHND_NVRAM_DATA_PROBE_DEFAULT);147}148149/**150* Parser states for bhnd_nvram_bcm_getvar_direct_common().151*/152typedef enum {153BCM_PARSE_KEY_START,154BCM_PARSE_KEY_CONT,155BCM_PARSE_KEY,156BCM_PARSE_NEXT_KEY,157BCM_PARSE_VALUE_START,158BCM_PARSE_VALUE159} bcm_parse_state;160161static int162bhnd_nvram_bcm_getvar_direct(struct bhnd_nvram_io *io, const char *name,163void *outp, size_t *olen, bhnd_nvram_type otype)164{165return (bhnd_nvram_bcm_getvar_direct_common(io, name, outp, olen, otype,166true));167}168169/**170* Common BCM/BCMRAW implementation of bhnd_nvram_getvar_direct().171*/172int173bhnd_nvram_bcm_getvar_direct_common(struct bhnd_nvram_io *io, const char *name,174void *outp, size_t *olen, bhnd_nvram_type otype, bool have_header)175{176struct bhnd_nvram_bcmhdr hdr;177char buf[512];178bcm_parse_state pstate;179size_t limit, offset;180size_t buflen, bufpos;181size_t namelen, namepos;182size_t vlen;183int error;184185limit = bhnd_nvram_io_getsize(io);186offset = 0;187188/* Fetch and validate the header */189if (have_header) {190if ((error = bhnd_nvram_io_read(io, offset, &hdr, sizeof(hdr))))191return (error);192193if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)194return (ENXIO);195196offset += sizeof(hdr);197limit = bhnd_nv_ummin(le32toh(hdr.size), limit);198}199200/* Loop our parser until we find the requested variable, or hit EOF */201pstate = BCM_PARSE_KEY_START;202buflen = 0;203bufpos = 0;204namelen = strlen(name);205namepos = 0;206vlen = 0;207208while ((offset - bufpos) < limit) {209BHND_NV_ASSERT(bufpos <= buflen,210("buf position invalid (%zu > %zu)", bufpos, buflen));211BHND_NV_ASSERT(buflen <= sizeof(buf),212("buf length invalid (%zu > %zu", buflen, sizeof(buf)));213214/* Repopulate our parse buffer? */215if (buflen - bufpos == 0) {216BHND_NV_ASSERT(offset < limit, ("offset overrun"));217218buflen = bhnd_nv_ummin(sizeof(buf), limit - offset);219bufpos = 0;220221error = bhnd_nvram_io_read(io, offset, buf, buflen);222if (error)223return (error);224225offset += buflen;226}227228switch (pstate) {229case BCM_PARSE_KEY_START:230BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!"));231232/* An extra '\0' denotes NVRAM EOF */233if (buf[bufpos] == '\0')234return (ENOENT);235236/* Reset name matching position */237namepos = 0;238239/* Start name matching */240pstate = BCM_PARSE_KEY_CONT;241break;242243case BCM_PARSE_KEY_CONT: {244size_t navail, nleft;245246nleft = namelen - namepos;247navail = bhnd_nv_ummin(buflen - bufpos, nleft);248249if (strncmp(name+namepos, buf+bufpos, navail) == 0) {250/* Matched */251namepos += navail;252bufpos += navail;253254/* If we've matched the full variable name,255* look for its trailing delimiter */256if (namepos == namelen)257pstate = BCM_PARSE_KEY;258} else {259/* No match; advance to next entry and restart260* name matching */261pstate = BCM_PARSE_NEXT_KEY;262}263264break;265}266267case BCM_PARSE_KEY:268BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!"));269270if (buf[bufpos] == '=') {271/* Key fully matched; advance past '=' and272* parse the value */273bufpos++;274pstate = BCM_PARSE_VALUE_START;275} else {276/* No match; advance to next entry and restart277* name matching */278pstate = BCM_PARSE_NEXT_KEY;279}280281break;282283case BCM_PARSE_NEXT_KEY: {284const char *p;285286/* Scan for a '\0' terminator */287p = memchr(buf+bufpos, '\0', buflen - bufpos);288289if (p != NULL) {290/* Found entry terminator; restart name291* matching at next entry */292pstate = BCM_PARSE_KEY_START;293bufpos = (p - buf) + 1 /* skip '\0' */;294} else {295/* Consumed full buffer looking for '\0';296* force repopulation of the buffer and297* retry */298bufpos = buflen;299}300301break;302}303304case BCM_PARSE_VALUE_START: {305const char *p;306307/* Scan for a '\0' terminator */308p = memchr(buf+bufpos, '\0', buflen - bufpos);309310if (p != NULL) {311/* Found entry terminator; parse the value */312vlen = p - &buf[bufpos];313pstate = BCM_PARSE_VALUE;314315} else if (p == NULL && offset == limit) {316/* Hit EOF without a terminating '\0';317* treat the entry as implicitly terminated */318vlen = buflen - bufpos;319pstate = BCM_PARSE_VALUE;320321} else if (p == NULL && bufpos > 0) {322size_t nread;323324/* Move existing value data to start of325* buffer */326memmove(buf, buf+bufpos, buflen - bufpos);327buflen = bufpos;328bufpos = 0;329330/* Populate full buffer to allow retry of331* value parsing */332nread = bhnd_nv_ummin(sizeof(buf) - buflen,333limit - offset);334335error = bhnd_nvram_io_read(io, offset,336buf+buflen, nread);337if (error)338return (error);339340offset += nread;341buflen += nread;342} else {343/* Value exceeds our buffer capacity */344BHND_NV_LOG("cannot parse value for '%s' "345"(exceeds %zu byte limit)\n", name,346sizeof(buf));347348return (ENXIO);349}350351break;352}353354case BCM_PARSE_VALUE:355BHND_NV_ASSERT(vlen <= buflen, ("value buf overrun"));356357return (bhnd_nvram_value_coerce(buf+bufpos, vlen,358BHND_NVRAM_TYPE_STRING, outp, olen, otype));359}360}361362/* Variable not found */363return (ENOENT);364}365366static int367bhnd_nvram_bcm_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,368bhnd_nvram_plist *options, void *outp, size_t *olen)369{370struct bhnd_nvram_bcmhdr hdr;371bhnd_nvram_prop *prop;372size_t limit, nbytes;373uint32_t sdram_ncdl;374uint16_t sdram_init, sdram_cfg, sdram_refresh;375uint8_t bcm_ver, crc8;376int error;377378/* Determine output byte limit */379if (outp != NULL)380limit = *olen;381else382limit = 0;383384/* Fetch required header variables */385#define PROPS_GET_HDRVAR(_name, _dest, _type) do { \386const char *name = BCM_NVRAM_ ## _name ## _VAR; \387if (!bhnd_nvram_plist_contains(props, name)) { \388BHND_NV_LOG("missing required property: %s\n", \389name); \390return (EFTYPE); \391} \392\393error = bhnd_nvram_plist_get_encoded(props, name, \394(_dest), sizeof(*(_dest)), \395BHND_NVRAM_TYPE_ ##_type); \396if (error) { \397BHND_NV_LOG("error reading required header " \398"%s property: %d\n", name, error); \399return (EFTYPE); \400} \401} while (0)402403PROPS_GET_HDRVAR(SDRAM_NCDL, &sdram_ncdl, UINT32);404PROPS_GET_HDRVAR(CFG0_SDRAM_INIT, &sdram_init, UINT16);405PROPS_GET_HDRVAR(CFG1_SDRAM_CFG, &sdram_cfg, UINT16);406PROPS_GET_HDRVAR(CFG1_SDRAM_REFRESH, &sdram_refresh, UINT16);407408#undef PROPS_GET_HDRVAR409410/* Fetch BCM nvram version from options */411if (options != NULL &&412bhnd_nvram_plist_contains(options, BCM_NVRAM_ENCODE_OPT_VERSION))413{414error = bhnd_nvram_plist_get_uint8(options,415BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver);416if (error) {417BHND_NV_LOG("error reading %s uint8 option value: %d\n",418BCM_NVRAM_ENCODE_OPT_VERSION, error);419return (EINVAL);420}421} else {422bcm_ver = BCM_NVRAM_CFG0_VER_DEFAULT;423}424425/* Construct our header */426hdr = (struct bhnd_nvram_bcmhdr) {427.magic = htole32(BCM_NVRAM_MAGIC),428.size = 0,429.cfg0 = 0,430.cfg1 = 0,431.sdram_ncdl = htole32(sdram_ncdl)432};433434hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, 0x0);435hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER, bcm_ver);436hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_SDRAM_INIT,437htole16(sdram_init));438439hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_CFG,440htole16(sdram_cfg));441hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_REFRESH,442htole16(sdram_refresh));443444/* Write the header */445nbytes = sizeof(hdr);446if (limit >= nbytes)447memcpy(outp, &hdr, sizeof(hdr));448449/* Write all properties */450prop = NULL;451while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {452const char *name;453char *p;454size_t prop_limit;455size_t name_len, value_len;456457if (outp == NULL || limit < nbytes) {458p = NULL;459prop_limit = 0;460} else {461p = ((char *)outp) + nbytes;462prop_limit = limit - nbytes;463}464465/* Fetch and write name + '=' to output */466name = bhnd_nvram_prop_name(prop);467name_len = strlen(name) + 1;468469if (prop_limit > name_len) {470memcpy(p, name, name_len - 1);471p[name_len - 1] = '=';472473prop_limit -= name_len;474p += name_len;475} else {476prop_limit = 0;477p = NULL;478}479480/* Advance byte count */481if (SIZE_MAX - nbytes < name_len)482return (EFTYPE); /* would overflow size_t */483484nbytes += name_len;485486/* Attempt to write NUL-terminated value to output */487value_len = prop_limit;488error = bhnd_nvram_prop_encode(prop, p, &value_len,489BHND_NVRAM_TYPE_STRING);490491/* If encoding failed for any reason other than ENOMEM (which492* we'll detect and report after encoding all properties),493* return immediately */494if (error && error != ENOMEM) {495BHND_NV_LOG("error serializing %s to required type "496"%s: %d\n", name,497bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),498error);499return (error);500}501502/* Advance byte count */503if (SIZE_MAX - nbytes < value_len)504return (EFTYPE); /* would overflow size_t */505506nbytes += value_len;507}508509/* Write terminating '\0' */510if (limit > nbytes)511*((char *)outp + nbytes) = '\0';512513if (nbytes == SIZE_MAX)514return (EFTYPE); /* would overflow size_t */515else516nbytes++;517518/* Update header length; this must fit within the header's 32-bit size519* field */520if (nbytes <= UINT32_MAX) {521hdr.size = (uint32_t)nbytes;522} else {523BHND_NV_LOG("size %zu exceeds maximum supported size of %u "524"bytes\n", nbytes, UINT32_MAX);525return (EFTYPE);526}527528/* Provide required length */529*olen = nbytes;530if (limit < *olen) {531if (outp == NULL)532return (0);533534return (ENOMEM);535}536537/* Calculate the CRC value */538BHND_NV_ASSERT(nbytes >= BCM_NVRAM_CRC_SKIP, ("invalid output size"));539crc8 = bhnd_nvram_crc8((uint8_t *)outp + BCM_NVRAM_CRC_SKIP,540nbytes - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);541542/* Update CRC and write the finalized header */543BHND_NV_ASSERT(nbytes >= sizeof(hdr), ("invalid output size"));544hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, crc8);545memcpy(outp, &hdr, sizeof(hdr));546547return (0);548}549550/**551* Initialize @p bcm with the provided NVRAM data mapped by @p src.552*553* @param bcm A newly allocated data instance.554*/555static int556bhnd_nvram_bcm_init(struct bhnd_nvram_bcm *bcm, struct bhnd_nvram_io *src)557{558struct bhnd_nvram_bcmhdr hdr;559uint8_t *p;560void *ptr;561size_t io_offset, io_size;562uint8_t crc, valid, bcm_ver;563int error;564565if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr))))566return (error);567568if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)569return (ENXIO);570571/* Fetch the actual NVRAM image size */572io_size = le32toh(hdr.size);573if (io_size < sizeof(hdr)) {574/* The header size must include the header itself */575BHND_NV_LOG("corrupt header size: %zu\n", io_size);576return (EINVAL);577}578579if (io_size > bhnd_nvram_io_getsize(src)) {580BHND_NV_LOG("header size %zu exceeds input size %zu\n",581io_size, bhnd_nvram_io_getsize(src));582return (EINVAL);583}584585/* Allocate a buffer large enough to hold the NVRAM image, and586* an extra EOF-signaling NUL (on the chance it's missing from the587* source data) */588if (io_size == SIZE_MAX)589return (ENOMEM);590591bcm->data = bhnd_nvram_iobuf_empty(io_size, io_size + 1);592if (bcm->data == NULL)593return (ENOMEM);594595/* Fetch a pointer into our backing buffer and copy in the596* NVRAM image. */597error = bhnd_nvram_io_write_ptr(bcm->data, 0x0, &ptr, io_size, NULL);598if (error)599return (error);600601p = ptr;602if ((error = bhnd_nvram_io_read(src, 0x0, p, io_size)))603return (error);604605/* Verify the CRC */606valid = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC);607crc = bhnd_nvram_crc8(p + BCM_NVRAM_CRC_SKIP,608io_size - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);609610if (crc != valid) {611BHND_NV_LOG("warning: NVRAM CRC error (crc=%#hhx, "612"expected=%hhx)\n", crc, valid);613}614615/* Populate header variable definitions */616#define BCM_READ_HDR_VAR(_name, _dest, _swap) do { \617struct bhnd_nvram_bcm_hvar *data; \618data = bhnd_nvram_bcm_gethdrvar(bcm, _name ##_VAR); \619BHND_NV_ASSERT(data != NULL, \620("no such header variable: " __STRING(_name))); \621\622\623data->value. _dest = _swap(BCM_NVRAM_GET_BITS( \624hdr. _name ## _FIELD, _name)); \625} while(0)626627BCM_READ_HDR_VAR(BCM_NVRAM_CFG0_SDRAM_INIT, u16, le16toh);628BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_CFG, u16, le16toh);629BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_REFRESH, u16, le16toh);630BCM_READ_HDR_VAR(BCM_NVRAM_SDRAM_NCDL, u32, le32toh);631632_Static_assert(nitems(bcm->hvars) == 4, "missing initialization for"633"NVRAM header variable(s)");634635#undef BCM_READ_HDR_VAR636637/* Process the buffer */638bcm->count = 0;639io_offset = sizeof(hdr);640while (io_offset < io_size) {641char *envp;642const char *name, *value;643size_t envp_len;644size_t name_len, value_len;645646/* Parse the key=value string */647envp = (char *) (p + io_offset);648envp_len = strnlen(envp, io_size - io_offset);649error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,650&name_len, &value, &value_len);651if (error) {652BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",653io_offset, error);654return (error);655}656657/* Insert a '\0' character, replacing the '=' delimiter and658* allowing us to vend references directly to the variable659* name */660*(envp + name_len) = '\0';661662/* Record any NVRAM variables that mirror our header variables.663* This is a brute-force search -- for the amount of data we're664* operating on, it shouldn't be an issue. */665for (size_t i = 0; i < nitems(bcm->hvars); i++) {666struct bhnd_nvram_bcm_hvar *hvar;667union bhnd_nvram_bcm_hvar_value hval;668size_t hval_len;669670hvar = &bcm->hvars[i];671672/* Already matched? */673if (hvar->envp != NULL)674continue;675676/* Name matches? */677if ((strcmp(name, hvar->name)) != 0)678continue;679680/* Save pointer to mirrored envp */681hvar->envp = envp;682683/* Check for stale value */684hval_len = sizeof(hval);685error = bhnd_nvram_value_coerce(value, value_len,686BHND_NVRAM_TYPE_STRING, &hval, &hval_len,687hvar->type);688if (error) {689/* If parsing fails, we can likely only make690* things worse by trying to synchronize the691* variables */692BHND_NV_LOG("error parsing header variable "693"'%s=%s': %d\n", name, value, error);694} else if (hval_len != hvar->len) {695hvar->stale = true;696} else if (memcmp(&hval, &hvar->value, hval_len) != 0) {697hvar->stale = true;698}699}700701/* Seek past the value's terminating '\0' */702io_offset += envp_len;703if (io_offset == io_size) {704BHND_NV_LOG("missing terminating NUL at offset %#zx\n",705io_offset);706return (EINVAL);707}708709if (*(p + io_offset) != '\0') {710BHND_NV_LOG("invalid terminator '%#hhx' at offset "711"%#zx\n", *(p + io_offset), io_offset);712return (EINVAL);713}714715/* Update variable count */716bcm->count++;717718/* Seek to the next record */719if (++io_offset == io_size) {720char ch;721722/* Hit EOF without finding a terminating NUL723* byte; we need to grow our buffer and append724* it */725io_size++;726if ((error = bhnd_nvram_io_setsize(bcm->data, io_size)))727return (error);728729/* Write NUL byte */730ch = '\0';731error = bhnd_nvram_io_write(bcm->data, io_size-1, &ch,732sizeof(ch));733if (error)734return (error);735}736737/* Check for explicit EOF (encoded as a single empty NUL738* terminated string) */739if (*(p + io_offset) == '\0')740break;741}742743/* Add non-mirrored header variables to total count variable */744for (size_t i = 0; i < nitems(bcm->hvars); i++) {745if (bcm->hvars[i].envp == NULL)746bcm->count++;747}748749/* Populate serialization options from our header */750bcm_ver = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER);751error = bhnd_nvram_plist_append_bytes(bcm->opts,752BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver, sizeof(bcm_ver),753BHND_NVRAM_TYPE_UINT8);754if (error)755return (error);756757return (0);758}759760static int761bhnd_nvram_bcm_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)762{763struct bhnd_nvram_bcm *bcm;764int error;765766bcm = (struct bhnd_nvram_bcm *)nv;767768/* Populate default BCM mirrored header variable set */769_Static_assert(sizeof(bcm->hvars) == sizeof(bhnd_nvram_bcm_hvars),770"hvar declarations must match bhnd_nvram_bcm_hvars template");771memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars));772773/* Allocate (empty) option list, to be populated by774* bhnd_nvram_bcm_init() */775bcm->opts = bhnd_nvram_plist_new();776if (bcm->opts == NULL)777return (ENOMEM);778779/* Parse the BCM input data and initialize our backing780* data representation */781if ((error = bhnd_nvram_bcm_init(bcm, io))) {782bhnd_nvram_bcm_free(nv);783return (error);784}785786return (0);787}788789static void790bhnd_nvram_bcm_free(struct bhnd_nvram_data *nv)791{792struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;793794if (bcm->data != NULL)795bhnd_nvram_io_free(bcm->data);796797if (bcm->opts != NULL)798bhnd_nvram_plist_release(bcm->opts);799}800801size_t802bhnd_nvram_bcm_count(struct bhnd_nvram_data *nv)803{804struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;805return (bcm->count);806}807808static bhnd_nvram_plist *809bhnd_nvram_bcm_options(struct bhnd_nvram_data *nv)810{811struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;812return (bcm->opts);813}814815static uint32_t816bhnd_nvram_bcm_caps(struct bhnd_nvram_data *nv)817{818return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);819}820821static const char *822bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep)823{824struct bhnd_nvram_bcm *bcm;825struct bhnd_nvram_bcm_hvar *hvar, *hvar_next;826const void *ptr;827const char *envp, *basep;828size_t io_size, io_offset;829int error;830831bcm = (struct bhnd_nvram_bcm *)nv;832833io_offset = sizeof(struct bhnd_nvram_bcmhdr);834io_size = bhnd_nvram_io_getsize(bcm->data) - io_offset;835836/* Map backing buffer */837error = bhnd_nvram_io_read_ptr(bcm->data, io_offset, &ptr, io_size,838NULL);839if (error) {840BHND_NV_LOG("error mapping backing buffer: %d\n", error);841return (NULL);842}843844basep = ptr;845846/* If cookiep pointers into our header variable array, handle as header847* variable iteration. */848hvar = bhnd_nvram_bcm_to_hdrvar(bcm, *cookiep);849if (hvar != NULL) {850size_t idx;851852/* Advance to next entry, if any */853idx = bhnd_nvram_bcm_hdrvar_index(bcm, hvar) + 1;854855/* Find the next header-defined variable that isn't defined in856* the NVRAM data, start iteration there */857for (size_t i = idx; i < nitems(bcm->hvars); i++) {858hvar_next = &bcm->hvars[i];859if (hvar_next->envp != NULL && !hvar_next->stale)860continue;861862*cookiep = hvar_next;863return (hvar_next->name);864}865866/* No further header-defined variables; iteration867* complete */868return (NULL);869}870871/* Handle standard NVRAM data iteration */872if (*cookiep == NULL) {873/* Start at the first NVRAM data record */874envp = basep;875} else {876/* Seek to next record */877envp = *cookiep;878envp += strlen(envp) + 1; /* key + '\0' */879envp += strlen(envp) + 1; /* value + '\0' */880}881882/*883* Skip entries that have an existing header variable entry that takes884* precedence over the NVRAM data value.885*886* The header's value will be provided when performing header variable887* iteration888*/889while ((size_t)(envp - basep) < io_size && *envp != '\0') {890/* Locate corresponding header variable */891hvar = NULL;892for (size_t i = 0; i < nitems(bcm->hvars); i++) {893if (bcm->hvars[i].envp != envp)894continue;895896hvar = &bcm->hvars[i];897break;898}899900/* If no corresponding hvar entry, or the entry does not take901* precedence over this NVRAM value, we can safely return this902* value as-is. */903if (hvar == NULL || !hvar->stale)904break;905906/* Seek to next record */907envp += strlen(envp) + 1; /* key + '\0' */908envp += strlen(envp) + 1; /* value + '\0' */909}910911/* On NVRAM data EOF, try switching to header variables */912if ((size_t)(envp - basep) == io_size || *envp == '\0') {913/* Find first valid header variable */914for (size_t i = 0; i < nitems(bcm->hvars); i++) {915if (bcm->hvars[i].envp != NULL)916continue;917918*cookiep = &bcm->hvars[i];919return (bcm->hvars[i].name);920}921922/* No header variables */923return (NULL);924}925926*cookiep = __DECONST(void *, envp);927return (envp);928}929930static void *931bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name)932{933return (bhnd_nvram_data_generic_find(nv, name));934}935936static int937bhnd_nvram_bcm_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,938void *cookiep2)939{940struct bhnd_nvram_bcm *bcm;941struct bhnd_nvram_bcm_hvar *hvar1, *hvar2;942943bcm = (struct bhnd_nvram_bcm *)nv;944945hvar1 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep1);946hvar2 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep2);947948/* Header variables are always ordered below any variables defined949* in the BCM data */950if (hvar1 != NULL && hvar2 == NULL) {951return (1); /* hvar follows non-hvar */952} else if (hvar1 == NULL && hvar2 != NULL) {953return (-1); /* non-hvar precedes hvar */954}955956/* Otherwise, both cookies are either hvars or non-hvars. We can957* safely fall back on pointer order, which will provide a correct958* ordering matching the behavior of bhnd_nvram_data_next() for959* both cases */960if (cookiep1 < cookiep2)961return (-1);962963if (cookiep1 > cookiep2)964return (1);965966return (0);967}968969static int970bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,971size_t *len, bhnd_nvram_type type)972{973return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));974}975976static int977bhnd_nvram_bcm_copy_val(struct bhnd_nvram_data *nv, void *cookiep,978bhnd_nvram_val **value)979{980return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));981}982983static const void *984bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,985size_t *len, bhnd_nvram_type *type)986{987struct bhnd_nvram_bcm *bcm;988struct bhnd_nvram_bcm_hvar *hvar;989const char *envp;990991bcm = (struct bhnd_nvram_bcm *)nv;992993/* Handle header variables */994if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {995BHND_NV_ASSERT(bhnd_nvram_value_check_aligned(&hvar->value,996hvar->len, hvar->type) == 0, ("value misaligned"));997998*type = hvar->type;999*len = hvar->len;1000return (&hvar->value);1001}10021003/* Cookie points to key\0value\0 -- get the value address */1004BHND_NV_ASSERT(cookiep != NULL, ("NULL cookiep"));10051006envp = cookiep;1007envp += strlen(envp) + 1; /* key + '\0' */1008*len = strlen(envp) + 1; /* value + '\0' */1009*type = BHND_NVRAM_TYPE_STRING;10101011return (envp);1012}10131014static const char *1015bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)1016{1017struct bhnd_nvram_bcm *bcm;1018struct bhnd_nvram_bcm_hvar *hvar;10191020bcm = (struct bhnd_nvram_bcm *)nv;10211022/* Handle header variables */1023if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {1024return (hvar->name);1025}10261027/* Cookie points to key\0value\0 */1028return (cookiep);1029}10301031static int1032bhnd_nvram_bcm_filter_setvar(struct bhnd_nvram_data *nv, const char *name,1033bhnd_nvram_val *value, bhnd_nvram_val **result)1034{1035bhnd_nvram_val *str;1036int error;10371038/* Name (trimmed of any path prefix) must be valid */1039if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))1040return (EINVAL);10411042/* Value must be bcm-formatted string */1043error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,1044value, BHND_NVRAM_VAL_DYNAMIC);1045if (error)1046return (error);10471048/* Success. Transfer result ownership to the caller. */1049*result = str;1050return (0);1051}10521053static int1054bhnd_nvram_bcm_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)1055{1056/* We permit deletion of any variable */1057return (0);1058}10591060/**1061* Return the internal BCM data reference for a header-defined variable1062* with @p name, or NULL if none exists.1063*/1064static struct bhnd_nvram_bcm_hvar *1065bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm *bcm, const char *name)1066{1067for (size_t i = 0; i < nitems(bcm->hvars); i++) {1068if (strcmp(bcm->hvars[i].name, name) == 0)1069return (&bcm->hvars[i]);1070}10711072/* Not found */1073return (NULL);1074}10751076/**1077* If @p cookiep references a header-defined variable, return the1078* internal BCM data reference. Otherwise, returns NULL.1079*/1080static struct bhnd_nvram_bcm_hvar *1081bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm *bcm, void *cookiep)1082{1083#ifdef BHND_NVRAM_INVARIANTS1084uintptr_t base, ptr;1085#endif10861087/* If the cookie falls within the hvar array, it's a1088* header variable cookie */1089if (nitems(bcm->hvars) == 0)1090return (NULL);10911092if (cookiep < (void *)&bcm->hvars[0])1093return (NULL);10941095if (cookiep > (void *)&bcm->hvars[nitems(bcm->hvars)-1])1096return (NULL);10971098#ifdef BHND_NVRAM_INVARIANTS1099base = (uintptr_t)bcm->hvars;1100ptr = (uintptr_t)cookiep;11011102BHND_NV_ASSERT((ptr - base) % sizeof(bcm->hvars[0]) == 0,1103("misaligned hvar pointer %p/%p", cookiep, bcm->hvars));1104#endif /* INVARIANTS */11051106return ((struct bhnd_nvram_bcm_hvar *)cookiep);1107}11081109/**1110* Return the index of @p hdrvar within @p bcm's backing hvars array.1111*/1112static size_t1113bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm *bcm,1114struct bhnd_nvram_bcm_hvar *hdrvar)1115{1116BHND_NV_ASSERT(bhnd_nvram_bcm_to_hdrvar(bcm, (void *)hdrvar) != NULL,1117("%p is not a valid hdrvar reference", hdrvar));11181119return (hdrvar - &bcm->hvars[0]);1120}112111221123