Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.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/cdefs.h>30#ifdef _KERNEL31#include <sys/param.h>32#include <sys/ctype.h>33#include <sys/limits.h>34#include <sys/malloc.h>35#include <sys/systm.h>36#else /* !_KERNEL */37#include <ctype.h>38#include <errno.h>39#include <stdint.h>40#include <stdio.h>41#include <stdlib.h>42#include <string.h>43#endif /* _KERNEL */4445#include "bhnd_nvram_private.h"4647#include "bhnd_nvram_datavar.h"4849#include "bhnd_nvram_data_tlvreg.h"5051/*52* CFE TLV NVRAM data class.53*54* The CFE-defined TLV NVRAM format is used on the WGT634U.55*/5657struct bhnd_nvram_tlv {58struct bhnd_nvram_data nv; /**< common instance state */59struct bhnd_nvram_io *data; /**< backing buffer */60size_t count; /**< variable count */61};6263BHND_NVRAM_DATA_CLASS_DEFN(tlv, "WGT634U", BHND_NVRAM_DATA_CAP_DEVPATHS,64sizeof(struct bhnd_nvram_tlv))6566/** Minimal TLV_ENV record header */67struct bhnd_nvram_tlv_env_hdr {68uint8_t tag;69uint8_t size;70} __packed;7172/** Minimal TLV_ENV record */73struct bhnd_nvram_tlv_env {74struct bhnd_nvram_tlv_env_hdr hdr;75uint8_t flags;76char envp[];77} __packed;7879/* Return the length in bytes of an TLV_ENV's envp data */80#define NVRAM_TLV_ENVP_DATA_LEN(_env) \81(((_env)->hdr.size < sizeof((_env)->flags)) ? 0 : \82((_env)->hdr.size - sizeof((_env)->flags)))8384/* Maximum supported length of the envp data field, in bytes */85#define NVRAM_TLV_ENVP_DATA_MAX_LEN \86(UINT8_MAX - sizeof(uint8_t) /* flags */)8788static int bhnd_nvram_tlv_parse_size(89struct bhnd_nvram_io *io,90size_t *size);9192static int bhnd_nvram_tlv_next_record(93struct bhnd_nvram_io *io,94size_t *next, size_t *offset,95uint8_t *tag);9697static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_next_env(98struct bhnd_nvram_tlv *tlv,99size_t *next, void **cookiep);100101static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_get_env(102struct bhnd_nvram_tlv *tlv,103void *cookiep);104105static void *bhnd_nvram_tlv_to_cookie(106struct bhnd_nvram_tlv *tlv,107size_t io_offset);108static size_t bhnd_nvram_tlv_to_offset(109struct bhnd_nvram_tlv *tlv,110void *cookiep);111112static int113bhnd_nvram_tlv_probe(struct bhnd_nvram_io *io)114{115struct bhnd_nvram_tlv_env ident;116size_t nbytes;117int error;118119nbytes = bhnd_nvram_io_getsize(io);120121/* Handle what might be an empty TLV image */122if (nbytes < sizeof(ident)) {123uint8_t tag;124125/* Fetch just the first tag */126error = bhnd_nvram_io_read(io, 0x0, &tag, sizeof(tag));127if (error)128return (error);129130/* This *could* be an empty TLV image, but all we're131* testing for here is a single 0x0 byte followed by EOF */132if (tag == NVRAM_TLV_TYPE_END)133return (BHND_NVRAM_DATA_PROBE_MAYBE);134135return (ENXIO);136}137138/* Otherwise, look at the initial header for a valid TLV ENV tag,139* plus one byte of the entry data */140error = bhnd_nvram_io_read(io, 0x0, &ident,141sizeof(ident) + sizeof(ident.envp[0]));142if (error)143return (error);144145/* First entry should be a variable record (which we statically146* assert as being defined to use a single byte size field) */147if (ident.hdr.tag != NVRAM_TLV_TYPE_ENV)148return (ENXIO);149150_Static_assert(NVRAM_TLV_TYPE_ENV & NVRAM_TLV_TF_U8_LEN,151"TYPE_ENV is not a U8-sized field");152153/* The entry must be at least 3 characters ('x=\0') in length */154if (ident.hdr.size < 3)155return (ENXIO);156157/* The first character should be a valid key char (alpha) */158if (!bhnd_nv_isalpha(ident.envp[0]))159return (ENXIO);160161return (BHND_NVRAM_DATA_PROBE_DEFAULT);162}163164static int165bhnd_nvram_tlv_getvar_direct(struct bhnd_nvram_io *io, const char *name,166void *buf, size_t *len, bhnd_nvram_type type)167{168struct bhnd_nvram_tlv_env env;169char data[NVRAM_TLV_ENVP_DATA_MAX_LEN];170size_t data_len;171const char *key, *value;172size_t keylen, vlen;173size_t namelen;174size_t next, off;175uint8_t tag;176int error;177178namelen = strlen(name);179180/* Iterate over the input looking for the requested variable */181next = 0;182while (!(error = bhnd_nvram_tlv_next_record(io, &next, &off, &tag))) {183switch (tag) {184case NVRAM_TLV_TYPE_END:185/* Not found */186return (ENOENT);187188case NVRAM_TLV_TYPE_ENV:189/* Read the record header */190error = bhnd_nvram_io_read(io, off, &env, sizeof(env));191if (error) {192BHND_NV_LOG("error reading TLV_ENV record "193"header: %d\n", error);194return (error);195}196197/* Read the record data */198data_len = NVRAM_TLV_ENVP_DATA_LEN(&env);199error = bhnd_nvram_io_read(io, off + sizeof(env), data,200data_len);201if (error) {202BHND_NV_LOG("error reading TLV_ENV record "203"data: %d\n", error);204return (error);205}206207/* Parse the key=value string */208error = bhnd_nvram_parse_env(data, data_len, '=', &key,209&keylen, &value, &vlen);210if (error) {211BHND_NV_LOG("error parsing TLV_ENV data: %d\n",212error);213return (error);214}215216/* Match against requested variable name */217if (keylen == namelen &&218strncmp(key, name, namelen) == 0)219{220return (bhnd_nvram_value_coerce(value, vlen,221BHND_NVRAM_TYPE_STRING, buf, len, type));222}223224break;225226default:227/* Skip unknown tags */228break;229}230}231232/* Hit I/O error */233return (error);234}235236static int237bhnd_nvram_tlv_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,238bhnd_nvram_plist *options, void *outp, size_t *olen)239{240bhnd_nvram_prop *prop;241size_t limit, nbytes;242int error;243244/* Determine output byte limit */245if (outp != NULL)246limit = *olen;247else248limit = 0;249250nbytes = 0;251252/* Write all properties */253prop = NULL;254while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {255struct bhnd_nvram_tlv_env env;256const char *name;257uint8_t *p;258size_t name_len, value_len;259size_t rec_size;260261env.hdr.tag = NVRAM_TLV_TYPE_ENV;262env.hdr.size = sizeof(env.flags);263env.flags = 0x0;264265/* Fetch name value and add to record length */266name = bhnd_nvram_prop_name(prop);267name_len = strlen(name) + 1 /* '=' */;268269if (UINT8_MAX - env.hdr.size < name_len) {270BHND_NV_LOG("%s name exceeds maximum TLV record "271"length\n", name);272return (EFTYPE); /* would overflow TLV size */273}274275env.hdr.size += name_len;276277/* Add string value to record length */278error = bhnd_nvram_prop_encode(prop, NULL, &value_len,279BHND_NVRAM_TYPE_STRING);280if (error) {281BHND_NV_LOG("error serializing %s to required type "282"%s: %d\n", name,283bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),284error);285return (error);286}287288if (UINT8_MAX - env.hdr.size < value_len) {289BHND_NV_LOG("%s value exceeds maximum TLV record "290"length\n", name);291return (EFTYPE); /* would overflow TLV size */292}293294env.hdr.size += value_len;295296/* Calculate total record size */297rec_size = sizeof(env.hdr) + env.hdr.size;298if (SIZE_MAX - nbytes < rec_size)299return (EFTYPE); /* would overflow size_t */300301/* Calculate our output pointer */302if (nbytes > limit || limit - nbytes < rec_size) {303/* buffer is full; cannot write */304p = NULL;305} else {306p = (uint8_t *)outp + nbytes;307}308309/* Write to output */310if (p != NULL) {311memcpy(p, &env, sizeof(env));312p += sizeof(env);313314memcpy(p, name, name_len - 1);315p[name_len - 1] = '=';316p += name_len;317318error = bhnd_nvram_prop_encode(prop, p, &value_len,319BHND_NVRAM_TYPE_STRING);320if (error) {321BHND_NV_LOG("error serializing %s to required "322"type %s: %d\n", name,323bhnd_nvram_type_name(324BHND_NVRAM_TYPE_STRING),325error);326return (error);327}328}329330nbytes += rec_size;331}332333/* Write terminating END record */334if (limit > nbytes)335*((uint8_t *)outp + nbytes) = NVRAM_TLV_TYPE_END;336337if (nbytes == SIZE_MAX)338return (EFTYPE); /* would overflow size_t */339nbytes++;340341/* Provide required length */342*olen = nbytes;343if (limit < *olen) {344if (outp == NULL)345return (0);346347return (ENOMEM);348}349350return (0);351}352353/**354* Initialize @p tlv with the provided NVRAM TLV data mapped by @p src.355*356* @param tlv A newly allocated data instance.357*/358static int359bhnd_nvram_tlv_init(struct bhnd_nvram_tlv *tlv, struct bhnd_nvram_io *src)360{361struct bhnd_nvram_tlv_env *env;362size_t size;363size_t next;364int error;365366BHND_NV_ASSERT(tlv->data == NULL, ("tlv data already initialized"));367368/* Determine the actual size of the TLV source data */369if ((error = bhnd_nvram_tlv_parse_size(src, &size)))370return (error);371372/* Copy to our own internal buffer */373if ((tlv->data = bhnd_nvram_iobuf_copy_range(src, 0x0, size)) == NULL)374return (ENOMEM);375376/* Initialize our backing buffer */377tlv->count = 0;378next = 0;379while ((env = bhnd_nvram_tlv_next_env(tlv, &next, NULL)) != NULL) {380size_t env_len;381size_t name_len;382383/* TLV_ENV data must not be empty */384env_len = NVRAM_TLV_ENVP_DATA_LEN(env);385if (env_len == 0) {386BHND_NV_LOG("cannot parse zero-length TLV_ENV record "387"data\n");388return (EINVAL);389}390391/* Parse the key=value string, and then replace the '='392* delimiter with '\0' to allow us to provide direct393* name pointers from our backing buffer */394error = bhnd_nvram_parse_env(env->envp, env_len, '=', NULL,395&name_len, NULL, NULL);396if (error) {397BHND_NV_LOG("error parsing TLV_ENV data: %d\n", error);398return (error);399}400401/* Replace '=' with '\0' */402*(env->envp + name_len) = '\0';403404/* Add to variable count */405tlv->count++;406};407408return (0);409}410411static int412bhnd_nvram_tlv_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)413{414415struct bhnd_nvram_tlv *tlv;416int error;417418/* Allocate and initialize the TLV data instance */419tlv = (struct bhnd_nvram_tlv *)nv;420421/* Parse the TLV input data and initialize our backing422* data representation */423if ((error = bhnd_nvram_tlv_init(tlv, io))) {424bhnd_nvram_tlv_free(nv);425return (error);426}427428return (0);429}430431static void432bhnd_nvram_tlv_free(struct bhnd_nvram_data *nv)433{434struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv;435if (tlv->data != NULL)436bhnd_nvram_io_free(tlv->data);437}438439size_t440bhnd_nvram_tlv_count(struct bhnd_nvram_data *nv)441{442struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv;443return (tlv->count);444}445446static bhnd_nvram_plist *447bhnd_nvram_tlv_options(struct bhnd_nvram_data *nv)448{449return (NULL);450}451452static uint32_t453bhnd_nvram_tlv_caps(struct bhnd_nvram_data *nv)454{455return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);456}457458static const char *459bhnd_nvram_tlv_next(struct bhnd_nvram_data *nv, void **cookiep)460{461struct bhnd_nvram_tlv *tlv;462struct bhnd_nvram_tlv_env *env;463size_t io_offset;464465tlv = (struct bhnd_nvram_tlv *)nv;466467/* Find next readable TLV record */468if (*cookiep == NULL) {469/* Start search at offset 0x0 */470io_offset = 0x0;471env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep);472} else {473/* Seek past the previous env record */474io_offset = bhnd_nvram_tlv_to_offset(tlv, *cookiep);475env = bhnd_nvram_tlv_next_env(tlv, &io_offset, NULL);476if (env == NULL)477BHND_NV_PANIC("invalid cookiep; record missing");478479/* Advance to next env record, update the caller's cookiep */480env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep);481}482483/* Check for EOF */484if (env == NULL)485return (NULL);486487/* Return the NUL terminated name */488return (env->envp);489}490491static void *492bhnd_nvram_tlv_find(struct bhnd_nvram_data *nv, const char *name)493{494return (bhnd_nvram_data_generic_find(nv, name));495}496497static int498bhnd_nvram_tlv_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,499void *cookiep2)500{501if (cookiep1 < cookiep2)502return (-1);503504if (cookiep1 > cookiep2)505return (1);506507return (0);508}509510static int511bhnd_nvram_tlv_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,512size_t *len, bhnd_nvram_type type)513{514return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));515}516517static int518bhnd_nvram_tlv_copy_val(struct bhnd_nvram_data *nv, void *cookiep,519bhnd_nvram_val **value)520{521return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));522}523524static const void *525bhnd_nvram_tlv_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,526size_t *len, bhnd_nvram_type *type)527{528struct bhnd_nvram_tlv *tlv;529struct bhnd_nvram_tlv_env *env;530const char *val;531int error;532533tlv = (struct bhnd_nvram_tlv *)nv;534535/* Fetch pointer to the TLV_ENV record */536if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL)537BHND_NV_PANIC("invalid cookiep: %p", cookiep);538539/* Parse value pointer and length from key\0value data */540error = bhnd_nvram_parse_env(env->envp, NVRAM_TLV_ENVP_DATA_LEN(env),541'\0', NULL, NULL, &val, len);542if (error)543BHND_NV_PANIC("unexpected error parsing '%s'", env->envp);544545/* Type is always CSTR */546*type = BHND_NVRAM_TYPE_STRING;547548return (val);549}550551static const char *552bhnd_nvram_tlv_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)553{554struct bhnd_nvram_tlv *tlv;555const struct bhnd_nvram_tlv_env *env;556557tlv = (struct bhnd_nvram_tlv *)nv;558559/* Fetch pointer to the TLV_ENV record */560if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL)561BHND_NV_PANIC("invalid cookiep: %p", cookiep);562563/* Return name pointer */564return (&env->envp[0]);565}566567static int568bhnd_nvram_tlv_filter_setvar(struct bhnd_nvram_data *nv, const char *name,569bhnd_nvram_val *value, bhnd_nvram_val **result)570{571bhnd_nvram_val *str;572const char *inp;573bhnd_nvram_type itype;574size_t ilen;575size_t name_len, tlv_nremain;576int error;577578tlv_nremain = NVRAM_TLV_ENVP_DATA_MAX_LEN;579580/* Name (trimmed of any path prefix) must be valid */581if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))582return (EINVAL);583584/* 'name=' must fit within the maximum TLV_ENV record length */585name_len = strlen(name) + 1; /* '=' */586if (tlv_nremain < name_len) {587BHND_NV_LOG("'%s=' exceeds maximum TLV_ENV record length\n",588name);589return (EINVAL);590}591tlv_nremain -= name_len;592593/* Convert value to a (bcm-formatted) string */594error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,595value, BHND_NVRAM_VAL_DYNAMIC);596if (error)597return (error);598599/* The string value must fit within remaining TLV_ENV record length */600inp = bhnd_nvram_val_bytes(str, &ilen, &itype);601if (tlv_nremain < ilen) {602BHND_NV_LOG("'%.*s\\0' exceeds maximum TLV_ENV record length\n",603BHND_NV_PRINT_WIDTH(ilen), inp);604605bhnd_nvram_val_release(str);606return (EINVAL);607}608tlv_nremain -= name_len;609610/* Success. Transfer result ownership to the caller. */611*result = str;612return (0);613}614615static int616bhnd_nvram_tlv_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)617{618/* We permit deletion of any variable */619return (0);620}621622/**623* Iterate over the records starting at @p next, returning the parsed624* record's @p tag, @p size, and @p offset.625*626* @param io The I/O context to parse.627* @param[in,out] next The next offset to be parsed, or 0x0628* to begin parsing. Upon successful629* return, will be set to the offset of the630* next record (or EOF, if631* NVRAM_TLV_TYPE_END was parsed).632* @param[out] offset The record's value offset.633* @param[out] tag The record's tag.634*635* @retval 0 success636* @retval EINVAL if parsing @p io as TLV fails.637* @retval non-zero if reading @p io otherwise fails, a regular unix error638* code will be returned.639*/640static int641bhnd_nvram_tlv_next_record(struct bhnd_nvram_io *io, size_t *next, size_t642*offset, uint8_t *tag)643{644size_t io_offset, io_size;645uint16_t parsed_len;646uint8_t len_hdr[2];647int error;648649io_offset = *next;650io_size = bhnd_nvram_io_getsize(io);651652/* Save the record offset */653if (offset != NULL)654*offset = io_offset;655656/* Fetch initial tag */657error = bhnd_nvram_io_read(io, io_offset, tag, sizeof(*tag));658if (error)659return (error);660io_offset++;661662/* EOF */663if (*tag == NVRAM_TLV_TYPE_END) {664*next = io_offset;665return (0);666}667668/* Read length field */669if (*tag & NVRAM_TLV_TF_U8_LEN) {670error = bhnd_nvram_io_read(io, io_offset, &len_hdr,671sizeof(len_hdr[0]));672if (error) {673BHND_NV_LOG("error reading TLV record size: %d\n",674error);675return (error);676}677678parsed_len = len_hdr[0];679io_offset++;680} else {681error = bhnd_nvram_io_read(io, io_offset, &len_hdr,682sizeof(len_hdr));683if (error) {684BHND_NV_LOG("error reading 16-bit TLV record "685"size: %d\n", error);686return (error);687}688689parsed_len = (len_hdr[0] << 8) | len_hdr[1];690io_offset += 2;691}692693/* Advance to next record */694if (parsed_len > io_size || io_size - parsed_len < io_offset) {695/* Hit early EOF */696BHND_NV_LOG("TLV record length %hu truncated by input "697"size of %zu\n", parsed_len, io_size);698return (EINVAL);699}700701*next = io_offset + parsed_len;702703/* Valid record found */704return (0);705}706707/**708* Parse the TLV data in @p io to determine the total size of the TLV709* data mapped by @p io (which may be less than the size of @p io).710*/711static int712bhnd_nvram_tlv_parse_size(struct bhnd_nvram_io *io, size_t *size)713{714size_t next;715uint8_t tag;716int error;717718/* We have to perform a minimal parse to determine the actual length */719next = 0x0;720*size = 0x0;721722/* Iterate over the input until we hit END tag or the read fails */723do {724error = bhnd_nvram_tlv_next_record(io, &next, NULL, &tag);725if (error)726return (error);727} while (tag != NVRAM_TLV_TYPE_END);728729/* Offset should now point to EOF */730BHND_NV_ASSERT(next <= bhnd_nvram_io_getsize(io),731("parse returned invalid EOF offset"));732733*size = next;734return (0);735}736737/**738* Iterate over the records in @p tlv, returning a pointer to the next739* NVRAM_TLV_TYPE_ENV record, or NULL if EOF is reached.740*741* @param tlv The TLV instance.742* @param[in,out] next The next offset to be parsed, or 0x0743* to begin parsing. Upon successful744* return, will be set to the offset of the745* next record.746*/747static struct bhnd_nvram_tlv_env *748bhnd_nvram_tlv_next_env(struct bhnd_nvram_tlv *tlv, size_t *next,749void **cookiep)750{751uint8_t tag;752int error;753754/* Find the next TLV_ENV record, starting at @p next */755do {756void *c;757size_t offset;758759/* Fetch the next TLV record */760error = bhnd_nvram_tlv_next_record(tlv->data, next, &offset,761&tag);762if (error) {763BHND_NV_LOG("unexpected error in next_record(): %d\n",764error);765return (NULL);766}767768/* Only interested in ENV records */769if (tag != NVRAM_TLV_TYPE_ENV)770continue;771772/* Map and return TLV_ENV record pointer */773c = bhnd_nvram_tlv_to_cookie(tlv, offset);774775/* Provide the cookiep value for the returned record */776if (cookiep != NULL)777*cookiep = c;778779return (bhnd_nvram_tlv_get_env(tlv, c));780} while (tag != NVRAM_TLV_TYPE_END);781782/* No remaining ENV records */783return (NULL);784}785786/**787* Return a pointer to the TLV_ENV record for @p cookiep, or NULL788* if none vailable.789*/790static struct bhnd_nvram_tlv_env *791bhnd_nvram_tlv_get_env(struct bhnd_nvram_tlv *tlv, void *cookiep)792{793struct bhnd_nvram_tlv_env *env;794void *ptr;795size_t navail;796size_t io_offset, io_size;797int error;798799io_size = bhnd_nvram_io_getsize(tlv->data);800io_offset = bhnd_nvram_tlv_to_offset(tlv, cookiep);801802/* At EOF? */803if (io_offset == io_size)804return (NULL);805806/* Fetch non-const pointer to the record entry */807error = bhnd_nvram_io_write_ptr(tlv->data, io_offset, &ptr,808sizeof(env->hdr), &navail);809if (error) {810/* Should never occur with a valid cookiep */811BHND_NV_LOG("error mapping record for cookiep: %d\n", error);812return (NULL);813}814815/* Validate the record pointer */816env = ptr;817if (env->hdr.tag != NVRAM_TLV_TYPE_ENV) {818/* Should never occur with a valid cookiep */819BHND_NV_LOG("non-ENV record mapped for %p\n", cookiep);820return (NULL);821}822823/* Is the required variable name data is mapped? */824if (navail < sizeof(struct bhnd_nvram_tlv_env_hdr) + env->hdr.size ||825env->hdr.size == sizeof(env->flags))826{827/* Should never occur with a valid cookiep */828BHND_NV_LOG("TLV_ENV variable data not mapped for %p\n",829cookiep);830return (NULL);831}832833return (env);834}835836/**837* Return a cookiep for the given I/O offset.838*/839static void *840bhnd_nvram_tlv_to_cookie(struct bhnd_nvram_tlv *tlv, size_t io_offset)841{842const void *ptr;843int error;844845BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(tlv->data),846("io_offset %zu out-of-range", io_offset));847BHND_NV_ASSERT(io_offset < UINTPTR_MAX,848("io_offset %#zx exceeds UINTPTR_MAX", io_offset));849850error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_offset, NULL);851if (error)852BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error);853854ptr = (const uint8_t *)ptr + io_offset;855return (__DECONST(void *, ptr));856}857858/* Convert a cookiep back to an I/O offset */859static size_t860bhnd_nvram_tlv_to_offset(struct bhnd_nvram_tlv *tlv, void *cookiep)861{862const void *ptr;863intptr_t offset;864size_t io_size;865int error;866867BHND_NV_ASSERT(cookiep != NULL, ("null cookiep"));868869io_size = bhnd_nvram_io_getsize(tlv->data);870871error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_size, NULL);872if (error)873BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error);874875offset = (const uint8_t *)cookiep - (const uint8_t *)ptr;876BHND_NV_ASSERT(offset >= 0, ("invalid cookiep"));877BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)"));878BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)"));879880return ((size_t)offset);881}882883884