Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.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 _KERNEL3132#include <sys/param.h>33#include <sys/ctype.h>34#include <sys/malloc.h>35#include <sys/systm.h>3637#else /* !_KERNEL */3839#include <ctype.h>40#include <stdint.h>41#include <stdio.h>42#include <stdlib.h>43#include <string.h>4445#endif /* _KERNEL */4647#include "bhnd_nvram_private.h"4849#include "bhnd_nvram_datavar.h"50#include "bhnd_nvram_data_bcmvar.h"5152/*53* Broadcom-RAW NVRAM data class.54*55* The Broadcom NVRAM NUL-delimited ASCII format is used by most56* Broadcom SoCs.57*58* The NVRAM data is encoded as a stream of NUL-terminated 'key=value'59* strings; the end of the stream is denoted by a single extra NUL character.60*/6162struct bhnd_nvram_bcmraw;6364/** BCM-RAW NVRAM data class instance */65struct bhnd_nvram_bcmraw {66struct bhnd_nvram_data nv; /**< common instance state */67char *data; /**< backing buffer */68size_t size; /**< buffer size */69size_t count; /**< variable count */70};7172BHND_NVRAM_DATA_CLASS_DEFN(bcmraw, "Broadcom (RAW)",73BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_bcmraw))7475static int76bhnd_nvram_bcmraw_probe(struct bhnd_nvram_io *io)77{78char envp[16];79size_t envp_len;80size_t io_size;81int error;8283io_size = bhnd_nvram_io_getsize(io);8485/*86* Fetch initial bytes87*/88envp_len = bhnd_nv_ummin(sizeof(envp), io_size);89if ((error = bhnd_nvram_io_read(io, 0x0, envp, envp_len)))90return (error);9192/* An empty BCM-RAW buffer should still contain a single terminating93* NUL */94if (envp_len == 0)95return (ENXIO);9697if (envp_len == 1) {98if (envp[0] != '\0')99return (ENXIO);100101return (BHND_NVRAM_DATA_PROBE_MAYBE);102}103104/* Must contain only printable ASCII characters delimited105* by NUL record delimiters */106for (size_t i = 0; i < envp_len; i++) {107char c = envp[i];108109/* If we hit a newline, this is probably BCM-TXT */110if (c == '\n')111return (ENXIO);112113if (c == '\0' && !bhnd_nv_isprint(c))114continue;115}116117/* A valid BCM-RAW buffer should contain a terminating NUL for118* the last record, followed by a final empty record terminated by119* NUL */120envp_len = 2;121if (io_size < envp_len)122return (ENXIO);123124if ((error = bhnd_nvram_io_read(io, io_size-envp_len, envp, envp_len)))125return (error);126127if (envp[0] != '\0' || envp[1] != '\0')128return (ENXIO);129130return (BHND_NVRAM_DATA_PROBE_MAYBE + 1);131}132133static int134bhnd_nvram_bcmraw_getvar_direct(struct bhnd_nvram_io *io, const char *name,135void *buf, size_t *len, bhnd_nvram_type type)136{137return (bhnd_nvram_bcm_getvar_direct_common(io, name, buf, len, type,138false));139}140141static int142bhnd_nvram_bcmraw_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,143bhnd_nvram_plist *options, void *outp, size_t *olen)144{145bhnd_nvram_prop *prop;146size_t limit, nbytes;147int error;148149/* Determine output byte limit */150if (outp != NULL)151limit = *olen;152else153limit = 0;154155nbytes = 0;156157/* Write all properties */158prop = NULL;159while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {160const char *name;161char *p;162size_t prop_limit;163size_t name_len, value_len;164165if (outp == NULL || limit < nbytes) {166p = NULL;167prop_limit = 0;168} else {169p = ((char *)outp) + nbytes;170prop_limit = limit - nbytes;171}172173/* Fetch and write name + '=' to output */174name = bhnd_nvram_prop_name(prop);175name_len = strlen(name) + 1;176177if (prop_limit > name_len) {178memcpy(p, name, name_len - 1);179p[name_len - 1] = '=';180181prop_limit -= name_len;182p += name_len;183} else {184prop_limit = 0;185p = NULL;186}187188/* Advance byte count */189if (SIZE_MAX - nbytes < name_len)190return (EFTYPE); /* would overflow size_t */191192nbytes += name_len;193194/* Attempt to write NUL-terminated value to output */195value_len = prop_limit;196error = bhnd_nvram_prop_encode(prop, p, &value_len,197BHND_NVRAM_TYPE_STRING);198199/* If encoding failed for any reason other than ENOMEM (which200* we'll detect and report after encoding all properties),201* return immediately */202if (error && error != ENOMEM) {203BHND_NV_LOG("error serializing %s to required type "204"%s: %d\n", name,205bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),206error);207return (error);208}209210/* Advance byte count */211if (SIZE_MAX - nbytes < value_len)212return (EFTYPE); /* would overflow size_t */213214nbytes += value_len;215}216217/* Write terminating '\0' */218if (limit > nbytes)219*((char *)outp + nbytes) = '\0';220221if (nbytes == SIZE_MAX)222return (EFTYPE); /* would overflow size_t */223else224nbytes++;225226/* Provide required length */227*olen = nbytes;228if (limit < *olen) {229if (outp == NULL)230return (0);231232return (ENOMEM);233}234235return (0);236}237238/**239* Initialize @p bcm with the provided NVRAM data mapped by @p src.240*241* @param bcm A newly allocated data instance.242*/243static int244bhnd_nvram_bcmraw_init(struct bhnd_nvram_bcmraw *bcm, struct bhnd_nvram_io *src)245{246size_t io_size;247size_t capacity, offset;248int error;249250/* Fetch the input image size */251io_size = bhnd_nvram_io_getsize(src);252253/* Allocate a buffer large enough to hold the NVRAM image, and254* an extra EOF-signaling NUL (on the chance it's missing from the255* source data) */256if (io_size == SIZE_MAX)257return (ENOMEM);258259capacity = io_size + 1 /* room for extra NUL */;260bcm->size = io_size;261if ((bcm->data = bhnd_nv_malloc(capacity)) == NULL)262return (ENOMEM);263264/* Copy in the NVRAM image */265if ((error = bhnd_nvram_io_read(src, 0x0, bcm->data, io_size)))266return (error);267268/* Process the buffer */269bcm->count = 0;270for (offset = 0; offset < bcm->size; offset++) {271char *envp;272const char *name, *value;273size_t envp_len;274size_t name_len, value_len;275276/* Parse the key=value string */277envp = (char *) (bcm->data + offset);278envp_len = strnlen(envp, bcm->size - offset);279error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,280&name_len, &value, &value_len);281if (error) {282BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",283offset, error);284return (error);285}286287/* Insert a '\0' character, replacing the '=' delimiter and288* allowing us to vend references directly to the variable289* name */290*(envp + name_len) = '\0';291292/* Add to variable count */293bcm->count++;294295/* Seek past the value's terminating '\0' */296offset += envp_len;297if (offset == io_size) {298BHND_NV_LOG("missing terminating NUL at offset %#zx\n",299offset);300return (EINVAL);301}302303/* If we hit EOF without finding a terminating NUL304* byte, we need to append it */305if (++offset == bcm->size) {306BHND_NV_ASSERT(offset < capacity,307("appending past end of buffer"));308bcm->size++;309*(bcm->data + offset) = '\0';310}311312/* Check for explicit EOF (encoded as a single empty NUL313* terminated string) */314if (*(bcm->data + offset) == '\0')315break;316}317318/* Reclaim any unused space in the backing buffer */319if (offset < bcm->size) {320bcm->data = bhnd_nv_reallocf(bcm->data, bcm->size);321if (bcm->data == NULL)322return (ENOMEM);323}324325return (0);326}327328static int329bhnd_nvram_bcmraw_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)330{331struct bhnd_nvram_bcmraw *bcm;332int error;333334bcm = (struct bhnd_nvram_bcmraw *)nv;335336/* Parse the BCM input data and initialize our backing337* data representation */338if ((error = bhnd_nvram_bcmraw_init(bcm, io))) {339bhnd_nvram_bcmraw_free(nv);340return (error);341}342343return (0);344}345346static void347bhnd_nvram_bcmraw_free(struct bhnd_nvram_data *nv)348{349struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;350351if (bcm->data != NULL)352bhnd_nv_free(bcm->data);353}354355static bhnd_nvram_plist *356bhnd_nvram_bcmraw_options(struct bhnd_nvram_data *nv)357{358return (NULL);359}360361static size_t362bhnd_nvram_bcmraw_count(struct bhnd_nvram_data *nv)363{364struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;365366return (bcm->count);367}368369static uint32_t370bhnd_nvram_bcmraw_caps(struct bhnd_nvram_data *nv)371{372return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);373}374375static const char *376bhnd_nvram_bcmraw_next(struct bhnd_nvram_data *nv, void **cookiep)377{378struct bhnd_nvram_bcmraw *bcm;379const char *envp;380381bcm = (struct bhnd_nvram_bcmraw *)nv;382383if (*cookiep == NULL) {384/* Start at the first NVRAM data record */385envp = bcm->data;386} else {387/* Seek to next record */388envp = *cookiep;389envp += strlen(envp) + 1; /* key + '\0' */390envp += strlen(envp) + 1; /* value + '\0' */391}392393/* EOF? */394if (*envp == '\0')395return (NULL);396397*cookiep = (void *)(uintptr_t)envp;398return (envp);399}400401static void *402bhnd_nvram_bcmraw_find(struct bhnd_nvram_data *nv, const char *name)403{404return (bhnd_nvram_data_generic_find(nv, name));405}406407static int408bhnd_nvram_bcmraw_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,409void *cookiep2)410{411if (cookiep1 < cookiep2)412return (-1);413414if (cookiep1 > cookiep2)415return (1);416417return (0);418}419420static int421bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,422size_t *len, bhnd_nvram_type type)423{424return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));425}426427static int428bhnd_nvram_bcmraw_copy_val(struct bhnd_nvram_data *nv, void *cookiep,429bhnd_nvram_val **value)430{431return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));432}433434static const void *435bhnd_nvram_bcmraw_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,436size_t *len, bhnd_nvram_type *type)437{438const char *envp;439440/* Cookie points to key\0value\0 -- get the value address */441envp = cookiep;442envp += strlen(envp) + 1; /* key + '\0' */443*len = strlen(envp) + 1; /* value + '\0' */444*type = BHND_NVRAM_TYPE_STRING;445446return (envp);447}448449static const char *450bhnd_nvram_bcmraw_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)451{452/* Cookie points to key\0value\0 */453return (cookiep);454}455456static int457bhnd_nvram_bcmraw_filter_setvar(struct bhnd_nvram_data *nv, const char *name,458bhnd_nvram_val *value, bhnd_nvram_val **result)459{460bhnd_nvram_val *str;461int error;462463/* Name (trimmed of any path prefix) must be valid */464if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))465return (EINVAL);466467/* Value must be bcm-formatted string */468error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,469value, BHND_NVRAM_VAL_DYNAMIC);470if (error)471return (error);472473/* Success. Transfer result ownership to the caller. */474*result = str;475return (0);476}477478static int479bhnd_nvram_bcmraw_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)480{481/* We permit deletion of any variable */482return (0);483}484485486