Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c
39536 views
/*-1* Copyright (c) 2015-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>3031#include <net/ethernet.h>3233#ifdef _KERNEL3435#include <sys/ctype.h>36#include <sys/kernel.h>37#include <sys/malloc.h>38#include <sys/systm.h>3940#include <machine/_inttypes.h>4142#else /* !_KERNEL */4344#include <ctype.h>45#include <errno.h>46#include <inttypes.h>47#include <stdlib.h>48#include <string.h>4950#endif /* _KERNEL */5152#include "bhnd_nvram_private.h"5354#include "bhnd_nvram_valuevar.h"5556static bool bhnd_nvram_ident_octet_string(const char *inp,57size_t ilen, char *delim, size_t *nelem);58static bool bhnd_nvram_ident_num_string(const char *inp,59size_t ilen, u_int base, u_int *obase);6061static int bhnd_nvram_val_bcm_macaddr_filter(62const bhnd_nvram_val_fmt **fmt, const void *inp,63size_t ilen, bhnd_nvram_type itype);64static int bhnd_nvram_val_bcm_macaddr_encode(65bhnd_nvram_val *value, void *outp, size_t *olen,66bhnd_nvram_type otype);6768static int bhnd_nvram_val_bcm_macaddr_string_filter(69const bhnd_nvram_val_fmt **fmt, const void *inp,70size_t ilen, bhnd_nvram_type itype);71static int bhnd_nvram_val_bcm_macaddr_string_encode_elem(72bhnd_nvram_val *value, const void *inp,73size_t ilen, void *outp, size_t *olen,74bhnd_nvram_type otype);75static const void *bhnd_nvram_val_bcm_macaddr_string_next(76bhnd_nvram_val *value, const void *prev,77size_t *len);7879static int bhnd_nvram_val_bcm_int_filter(80const bhnd_nvram_val_fmt **fmt, const void *inp,81size_t ilen, bhnd_nvram_type itype);82static int bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val *value,83void *outp, size_t *olen, bhnd_nvram_type otype);8485static int bhnd_nvram_val_bcm_decimal_encode_elem(86bhnd_nvram_val *value, const void *inp,87size_t ilen, void *outp, size_t *olen,88bhnd_nvram_type otype);89static int bhnd_nvram_val_bcm_hex_encode_elem(90bhnd_nvram_val *value, const void *inp,91size_t ilen, void *outp, size_t *olen,92bhnd_nvram_type otype);9394static int bhnd_nvram_val_bcm_leddc_filter(95const bhnd_nvram_val_fmt **fmt, const void *inp,96size_t ilen, bhnd_nvram_type itype);97static int bhnd_nvram_val_bcm_leddc_encode_elem(98bhnd_nvram_val *value, const void *inp,99size_t ilen, void *outp, size_t *olen,100bhnd_nvram_type otype);101102static int bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val *value,103void *outp, size_t *olen, bhnd_nvram_type otype);104105static int bhnd_nvram_val_bcmstr_csv_filter(106const bhnd_nvram_val_fmt **fmt, const void *inp,107size_t ilen, bhnd_nvram_type itype);108static const void *bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val *value,109const void *prev, size_t *len);110111/**112* Broadcom NVRAM MAC address format.113*/114const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_macaddr_fmt = {115.name = "bcm-macaddr",116.native_type = BHND_NVRAM_TYPE_UINT8_ARRAY,117.op_filter = bhnd_nvram_val_bcm_macaddr_filter,118.op_encode = bhnd_nvram_val_bcm_macaddr_encode,119};120121/** Broadcom NVRAM MAC address string format. */122static const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_macaddr_string_fmt = {123.name = "bcm-macaddr-string",124.native_type = BHND_NVRAM_TYPE_STRING,125.op_filter = bhnd_nvram_val_bcm_macaddr_string_filter,126.op_encode_elem = bhnd_nvram_val_bcm_macaddr_string_encode_elem,127.op_next = bhnd_nvram_val_bcm_macaddr_string_next,128};129130/**131* Broadcom NVRAM LED duty-cycle format.132*/133const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_leddc_fmt = {134.name = "bcm-leddc",135.native_type = BHND_NVRAM_TYPE_UINT32,136.op_filter = bhnd_nvram_val_bcm_leddc_filter,137.op_encode_elem = bhnd_nvram_val_bcm_leddc_encode_elem,138};139140/**141* Broadcom NVRAM decimal integer format.142*143* Extends standard integer handling, encoding the string representation of144* the integer value as a decimal string:145* - Positive values will be string-encoded without a prefix.146* - Negative values will be string-encoded with a leading '-' sign.147*/148const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_decimal_fmt = {149.name = "bcm-decimal",150.native_type = BHND_NVRAM_TYPE_UINT64,151.op_filter = bhnd_nvram_val_bcm_int_filter,152.op_encode = bhnd_nvram_val_bcm_int_encode,153.op_encode_elem = bhnd_nvram_val_bcm_decimal_encode_elem,154};155156/**157* Broadcom NVRAM decimal integer format.158*159* Extends standard integer handling, encoding the string representation of160* unsigned and positive signed integer values as an 0x-prefixed hexadecimal161* string.162*163* For compatibility with standard Broadcom NVRAM parsing, if the integer is164* both signed and negative, it will be string encoded as a negative decimal165* value, not as a twos-complement hexadecimal value.166*/167const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_hex_fmt = {168.name = "bcm-hex",169.native_type = BHND_NVRAM_TYPE_UINT64,170.op_filter = bhnd_nvram_val_bcm_int_filter,171.op_encode = bhnd_nvram_val_bcm_int_encode,172.op_encode_elem = bhnd_nvram_val_bcm_hex_encode_elem,173};174175/**176* Broadcom NVRAM string format.177*178* Handles standard, comma-delimited, and octet-string values as used in179* Broadcom NVRAM data.180*/181const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_string_fmt = {182.name = "bcm-string",183.native_type = BHND_NVRAM_TYPE_STRING,184.op_encode = bhnd_nvram_val_bcmstr_encode,185};186187/** Broadcom comma-delimited string. */188static const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_string_csv_fmt = {189.name = "bcm-string[]",190.native_type = BHND_NVRAM_TYPE_STRING,191.op_filter = bhnd_nvram_val_bcmstr_csv_filter,192.op_next = bhnd_nvram_val_bcmstr_csv_next,193};194195/* Built-in format definitions */196#define BHND_NVRAM_VAL_FMT_NATIVE(_n, _type) \197const bhnd_nvram_val_fmt bhnd_nvram_val_ ## _n ## _fmt = { \198.name = __STRING(_n), \199.native_type = BHND_NVRAM_TYPE_ ## _type, \200}201202BHND_NVRAM_VAL_FMT_NATIVE(uint8, UINT8);203BHND_NVRAM_VAL_FMT_NATIVE(uint16, UINT16);204BHND_NVRAM_VAL_FMT_NATIVE(uint32, UINT32);205BHND_NVRAM_VAL_FMT_NATIVE(uint64, UINT64);206BHND_NVRAM_VAL_FMT_NATIVE(int8, INT8);207BHND_NVRAM_VAL_FMT_NATIVE(int16, INT16);208BHND_NVRAM_VAL_FMT_NATIVE(int32, INT32);209BHND_NVRAM_VAL_FMT_NATIVE(int64, INT64);210BHND_NVRAM_VAL_FMT_NATIVE(char, CHAR);211BHND_NVRAM_VAL_FMT_NATIVE(bool, BOOL);212BHND_NVRAM_VAL_FMT_NATIVE(string, STRING);213BHND_NVRAM_VAL_FMT_NATIVE(data, DATA);214BHND_NVRAM_VAL_FMT_NATIVE(null, NULL);215216BHND_NVRAM_VAL_FMT_NATIVE(uint8_array, UINT8_ARRAY);217BHND_NVRAM_VAL_FMT_NATIVE(uint16_array, UINT16_ARRAY);218BHND_NVRAM_VAL_FMT_NATIVE(uint32_array, UINT32_ARRAY);219BHND_NVRAM_VAL_FMT_NATIVE(uint64_array, UINT64_ARRAY);220BHND_NVRAM_VAL_FMT_NATIVE(int8_array, INT8_ARRAY);221BHND_NVRAM_VAL_FMT_NATIVE(int16_array, INT16_ARRAY);222BHND_NVRAM_VAL_FMT_NATIVE(int32_array, INT32_ARRAY);223BHND_NVRAM_VAL_FMT_NATIVE(int64_array, INT64_ARRAY);224BHND_NVRAM_VAL_FMT_NATIVE(char_array, CHAR_ARRAY);225BHND_NVRAM_VAL_FMT_NATIVE(bool_array, BOOL_ARRAY);226BHND_NVRAM_VAL_FMT_NATIVE(string_array, STRING_ARRAY);227228/**229* Common hex/decimal integer filter implementation.230*/231static int232bhnd_nvram_val_bcm_int_filter(const bhnd_nvram_val_fmt **fmt, const void *inp,233size_t ilen, bhnd_nvram_type itype)234{235bhnd_nvram_type itype_base;236237itype_base = bhnd_nvram_base_type(itype);238239switch (itype_base) {240case BHND_NVRAM_TYPE_STRING:241/*242* If the input is a string, delegate to the Broadcom243* string format -- preserving the original string value244* takes priority over enforcing hexadecimal/integer string245* formatting.246*/247*fmt = &bhnd_nvram_val_bcm_string_fmt;248return (0);249250default:251if (bhnd_nvram_is_int_type(itype_base))252return (0);253254return (EFTYPE);255}256}257258/**259* Broadcom hex/decimal integer encode implementation.260*/261static int262bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val *value, void *outp, size_t *olen,263bhnd_nvram_type otype)264{265/* If encoding to a string, format multiple elements (if any) with a266* comma delimiter. */267if (otype == BHND_NVRAM_TYPE_STRING)268return (bhnd_nvram_val_printf(value, "%[]s", outp, olen, ","));269270return (bhnd_nvram_val_generic_encode(value, outp, olen, otype));271}272273/**274* Broadcom hex integer encode_elem implementation.275*/276static int277bhnd_nvram_val_bcm_hex_encode_elem(bhnd_nvram_val *value, const void *inp,278size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)279{280bhnd_nvram_type itype;281ssize_t width;282int error;283284itype = bhnd_nvram_val_elem_type(value);285BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type"));286287/* If not encoding as a string, perform generic value encoding */288if (otype != BHND_NVRAM_TYPE_STRING)289return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen,290outp, olen, otype));291292/* If the value is a signed, negative value, encode as a decimal293* string */294if (bhnd_nvram_is_signed_type(itype)) {295int64_t sval;296size_t slen;297bhnd_nvram_type stype;298299stype = BHND_NVRAM_TYPE_INT64;300slen = sizeof(sval);301302/* Fetch 64-bit signed representation */303error = bhnd_nvram_value_coerce(inp, ilen, itype, &sval, &slen,304stype);305if (error)306return (error);307308/* Decimal encoding required? */309if (sval < 0)310return (bhnd_nvram_value_printf("%I64d", &sval, slen,311stype, outp, olen, otype));312}313314/*315* Encode the value as a hex string.316*317* Most producers of Broadcom NVRAM values zero-pad hex values out to318* their native width (width * two hex characters), and we do the same319* for compatibility320*/321width = bhnd_nvram_type_width(itype) * 2;322return (bhnd_nvram_value_printf("0x%0*I64X", inp, ilen, itype,323outp, olen, width));324}325326/**327* Broadcom decimal integer encode_elem implementation.328*/329static int330bhnd_nvram_val_bcm_decimal_encode_elem(bhnd_nvram_val *value, const void *inp,331size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)332{333const char *sfmt;334bhnd_nvram_type itype;335336itype = bhnd_nvram_val_elem_type(value);337BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type"));338339/* If not encoding as a string, perform generic value encoding */340if (otype != BHND_NVRAM_TYPE_STRING)341return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen,342outp, olen, otype));343344sfmt = bhnd_nvram_is_signed_type(itype) ? "%I64d" : "%I64u";345return (bhnd_nvram_value_printf(sfmt, inp, ilen, itype, outp, olen));346}347348/**349* Broadcom LED duty-cycle filter.350*/351static int352bhnd_nvram_val_bcm_leddc_filter(const bhnd_nvram_val_fmt **fmt,353const void *inp, size_t ilen, bhnd_nvram_type itype)354{355const char *p;356size_t plen;357358switch (itype) {359case BHND_NVRAM_TYPE_UINT16:360case BHND_NVRAM_TYPE_UINT32:361return (0);362363case BHND_NVRAM_TYPE_STRING:364/* Trim any whitespace */365p = inp;366plen = bhnd_nvram_trim_field(&p, ilen, '\0');367368/* If the value is not a valid integer string, delegate to the369* Broadcom string format */370if (!bhnd_nvram_ident_num_string(p, plen, 0, NULL))371*fmt = &bhnd_nvram_val_bcm_string_fmt;372373return (0);374default:375return (EFTYPE);376}377}378379/**380* Broadcom LED duty-cycle encode.381*/382static int383bhnd_nvram_val_bcm_leddc_encode_elem(bhnd_nvram_val *value, const void *inp,384size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)385{386bhnd_nvram_type itype;387size_t limit, nbytes;388int error;389uint16_t led16;390uint32_t led32;391bool led16_lossy;392union {393uint16_t u16;394uint32_t u32;395} strval;396397/*398* LED duty-cycle values represent the on/off periods as a 32-bit399* integer, with the top 16 bits representing on cycles, and the400* bottom 16 representing off cycles.401*402* LED duty cycle values have three different formats:403*404* - SPROM: A 16-bit unsigned integer, with on/off cycles encoded405* as 8-bit values.406* - NVRAM: A 16-bit decimal or hexadecimal string, with on/off407* cycles encoded as 8-bit values as per the SPROM format.408* - NVRAM: A 32-bit decimal or hexadecimal string, with on/off409* cycles encoded as 16-bit values.410*411* To convert from a 16-bit representation to a 32-bit representation:412* ((value & 0xFF00) << 16) | ((value & 0x00FF) << 8)413*414* To convert from a 32-bit representation to a 16-bit representation,415* perform the same operation in reverse, discarding the lower 8-bits416* of each half of the 32-bit representation:417* ((value >> 16) & 0xFF00) | ((value >> 8) & 0x00FF)418*/419420itype = bhnd_nvram_val_elem_type(value);421nbytes = 0;422led16_lossy = false;423424/* Determine output byte limit */425if (outp != NULL)426limit = *olen;427else428limit = 0;429430/* If the input/output types match, just delegate to standard value431* encoding support */432if (otype == itype) {433return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen,434otype));435}436437/* If our value is a string, it may either be a 16-bit or a 32-bit438* representation of the duty cycle */439if (itype == BHND_NVRAM_TYPE_STRING) {440const char *p;441uint32_t ival;442size_t nlen, parsed;443444/* Parse integer value */445p = inp;446nlen = sizeof(ival);447error = bhnd_nvram_parse_int(p, ilen, 0, &parsed, &ival, &nlen,448BHND_NVRAM_TYPE_UINT32);449if (error)450return (error);451452/* Trailing garbage? */453if (parsed < ilen && *(p+parsed) != '\0')454return (EFTYPE);455456/* Point inp and itype to either our parsed 32-bit or 16-bit457* value */458inp = &strval;459if (ival & 0xFFFF0000) {460strval.u32 = ival;461itype = BHND_NVRAM_TYPE_UINT32;462} else {463strval.u16 = ival;464itype = BHND_NVRAM_TYPE_UINT16;465}466}467468/* Populate both u32 and (possibly lossy) u16 LEDDC representations */469switch (itype) {470case BHND_NVRAM_TYPE_UINT16: {471led16 = *(const uint16_t *)inp;472led32 = ((led16 & 0xFF00) << 16) | ((led16 & 0x00FF) << 8);473474/* If all bits are set in the 16-bit value (indicating that475* the value is 'unset' in SPROM), we must update the 32-bit476* representation to match. */477if (led16 == UINT16_MAX)478led32 = UINT32_MAX;479480break;481}482483case BHND_NVRAM_TYPE_UINT32:484led32 = *(const uint32_t *)inp;485led16 = ((led32 >> 16) & 0xFF00) | ((led32 >> 8) & 0x00FF);486487/*488* Determine whether the led16 conversion is lossy:489*490* - If the lower 8 bits of each half of the 32-bit value491* aren't set, we can safely use the 16-bit representation492* without losing data.493* - If all bits in the 32-bit value are set, the variable is494* treated as unset in SPROM. We can safely use the 16-bit495* representation without losing data.496*/497if ((led32 & 0x00FF00FF) != 0 && led32 != UINT32_MAX)498led16_lossy = true;499500break;501default:502BHND_NV_PANIC("unsupported backing data type: %s",503bhnd_nvram_type_name(itype));504}505506/*507* Encode as requested output type.508*/509switch (otype) {510case BHND_NVRAM_TYPE_STRING:511/*512* Prefer 16-bit format.513*/514if (!led16_lossy) {515return (bhnd_nvram_value_printf("0x%04hX", &led16,516sizeof(led16), BHND_NVRAM_TYPE_UINT16, outp, olen));517} else {518return (bhnd_nvram_value_printf("0x%04X", &led32,519sizeof(led32), BHND_NVRAM_TYPE_UINT32, outp, olen));520}521522break;523524case BHND_NVRAM_TYPE_UINT16: {525/* Can we encode as uint16 without losing data? */526if (led16_lossy)527return (ERANGE);528529/* Write led16 format */530nbytes += sizeof(uint16_t);531if (limit >= nbytes)532*(uint16_t *)outp = led16;533534break;535}536537case BHND_NVRAM_TYPE_UINT32:538/* Write led32 format */539nbytes += sizeof(uint32_t);540if (limit >= nbytes)541*(uint32_t *)outp = led32;542break;543544default:545/* No other output formats are supported */546return (EFTYPE);547}548549/* Provide the actual length */550*olen = nbytes;551552/* Report insufficient space (if output was requested) */553if (limit < nbytes && outp != NULL)554return (ENOMEM);555556return (0);557}558559/**560* Broadcom NVRAM string encoding.561*/562static int563bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val *value, void *outp, size_t *olen,564bhnd_nvram_type otype)565{566bhnd_nvram_val array;567const bhnd_nvram_val_fmt *array_fmt;568const void *inp;569bhnd_nvram_type itype;570size_t ilen;571int error;572573inp = bhnd_nvram_val_bytes(value, &ilen, &itype);574575/* If the output is not an array type (or if it's a character array),576* we can fall back on standard string encoding */577if (!bhnd_nvram_is_array_type(otype) ||578otype == BHND_NVRAM_TYPE_CHAR_ARRAY)579{580return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen,581otype));582}583584/* Otherwise, we need to interpret our value as either a macaddr585* string, or a comma-delimited string. */586inp = bhnd_nvram_val_bytes(value, &ilen, &itype);587if (bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL))588array_fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt;589else590array_fmt = &bhnd_nvram_val_bcm_string_csv_fmt;591592/* Wrap in array-typed representation */593error = bhnd_nvram_val_init(&array, array_fmt, inp, ilen, itype,594BHND_NVRAM_VAL_BORROW_DATA);595if (error) {596BHND_NV_LOG("error initializing array representation: %d\n",597error);598return (error);599}600601/* Ask the array-typed value to perform the encode */602error = bhnd_nvram_val_encode(&array, outp, olen, otype);603if (error)604BHND_NV_LOG("error encoding array representation: %d\n", error);605606bhnd_nvram_val_release(&array);607608return (error);609}610611/**612* Broadcom NVRAM comma-delimited string filter.613*/614static int615bhnd_nvram_val_bcmstr_csv_filter(const bhnd_nvram_val_fmt **fmt,616const void *inp, size_t ilen, bhnd_nvram_type itype)617{618switch (itype) {619case BHND_NVRAM_TYPE_STRING:620case BHND_NVRAM_TYPE_STRING_ARRAY:621return (0);622default:623return (EFTYPE);624}625}626627/**628* Broadcom NVRAM comma-delimited string iteration.629*/630static const void *631bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val *value, const void *prev,632size_t *len)633{634const char *next;635const char *inp;636bhnd_nvram_type itype;637size_t ilen, remain;638char delim;639640/* Fetch backing representation */641inp = bhnd_nvram_val_bytes(value, &ilen, &itype);642643/* Fetch next value */644switch (itype) {645case BHND_NVRAM_TYPE_STRING:646/* Zero-length array? */647if (ilen == 0)648return (NULL);649650if (prev == NULL) {651/* First element */652next = inp;653remain = ilen;654delim = ',';655} else {656/* Advance to the previous element's delimiter */657next = (const char *)prev + *len;658659/* Did we hit the end of the string? */660if ((size_t)(next - inp) >= ilen)661return (NULL);662663/* Fetch (and skip past) the delimiter */664delim = *next;665next++;666remain = ilen - (size_t)(next - inp);667668/* Was the delimiter the final character? */669if (remain == 0)670return (NULL);671}672673/* Parse the field value, up to the next delimiter */674*len = bhnd_nvram_parse_field(&next, remain, delim);675676return (next);677678case BHND_NVRAM_TYPE_STRING_ARRAY:679/* Delegate to default array iteration */680return (bhnd_nvram_value_array_next(inp, ilen, itype, prev,681len));682default:683BHND_NV_PANIC("unsupported type: %d", itype);684}685}686687/**688* MAC address filter.689*/690static int691bhnd_nvram_val_bcm_macaddr_filter(const bhnd_nvram_val_fmt **fmt,692const void *inp, size_t ilen, bhnd_nvram_type itype)693{694switch (itype) {695case BHND_NVRAM_TYPE_UINT8_ARRAY:696return (0);697case BHND_NVRAM_TYPE_STRING:698/* Let bcm_macaddr_string format handle it */699*fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt;700return (0);701default:702return (EFTYPE);703}704}705706/**707* MAC address encoding.708*/709static int710bhnd_nvram_val_bcm_macaddr_encode(bhnd_nvram_val *value, void *outp,711size_t *olen, bhnd_nvram_type otype)712{713const void *inp;714bhnd_nvram_type itype;715size_t ilen;716717/*718* If converting to a string (or a single-element string array),719* produce an octet string (00:00:...).720*/721if (bhnd_nvram_base_type(otype) == BHND_NVRAM_TYPE_STRING) {722return (bhnd_nvram_val_printf(value, "%[]02hhX", outp, olen,723":"));724}725726/* Otherwise, use standard encoding support */727inp = bhnd_nvram_val_bytes(value, &ilen, &itype);728return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen, otype));}729730/**731* MAC address string filter.732*/733static int734bhnd_nvram_val_bcm_macaddr_string_filter(const bhnd_nvram_val_fmt **fmt,735const void *inp, size_t ilen, bhnd_nvram_type itype)736{737switch (itype) {738case BHND_NVRAM_TYPE_STRING:739/* Use the standard Broadcom string format implementation if740* the input is not an octet string. */741if (!bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL))742*fmt = &bhnd_nvram_val_bcm_string_fmt;743744return (0);745default:746return (EFTYPE);747}748}749750/**751* MAC address string octet encoding.752*/753static int754bhnd_nvram_val_bcm_macaddr_string_encode_elem(bhnd_nvram_val *value,755const void *inp, size_t ilen, void *outp, size_t *olen,756bhnd_nvram_type otype)757{758size_t nparsed;759int error;760761/* If integer encoding is requested, explicitly parse our762* non-0x-prefixed as a base 16 integer value */763if (bhnd_nvram_is_int_type(otype)) {764error = bhnd_nvram_parse_int(inp, ilen, 16, &nparsed, outp,765olen, otype);766if (error)767return (error);768769if (nparsed != ilen)770return (EFTYPE);771772return (0);773}774775/* Otherwise, use standard encoding support */776return (bhnd_nvram_value_coerce(inp, ilen,777bhnd_nvram_val_elem_type(value), outp, olen, otype));778}779780/**781* MAC address string octet iteration.782*/783static const void *784bhnd_nvram_val_bcm_macaddr_string_next(bhnd_nvram_val *value, const void *prev,785size_t *len)786{787const char *next;788const char *str;789bhnd_nvram_type stype;790size_t slen, remain;791char delim;792793/* Fetch backing string */794str = bhnd_nvram_val_bytes(value, &slen, &stype);795BHND_NV_ASSERT(stype == BHND_NVRAM_TYPE_STRING,796("unsupported type: %d", stype));797798/* Zero-length array? */799if (slen == 0)800return (NULL);801802if (prev == NULL) {803/* First element */804805/* Determine delimiter */806if (!bhnd_nvram_ident_octet_string(str, slen, &delim, NULL)) {807/* Default to comma-delimited parsing */808delim = ',';809}810811/* Parsing will start at the base string pointer */812next = str;813remain = slen;814} else {815/* Advance to the previous element's delimiter */816next = (const char *)prev + *len;817818/* Did we hit the end of the string? */819if ((size_t)(next - str) >= slen)820return (NULL);821822/* Fetch (and skip past) the delimiter */823delim = *next;824next++;825remain = slen - (size_t)(next - str);826827/* Was the delimiter the final character? */828if (remain == 0)829return (NULL);830}831832/* Parse the field value, up to the next delimiter */833*len = bhnd_nvram_parse_field(&next, remain, delim);834835return (next);836}837838/**839* Determine whether @p inp is in octet string format, consisting of a840* fields of two hex characters, separated with ':' or '-' delimiters.841*842* This may be used to identify MAC address octet strings843* (BHND_NVRAM_SFMT_MACADDR).844*845* @param inp The string to be parsed.846* @param ilen The length of @p inp, in bytes.847* @param[out] delim On success, the delimiter used by this octet848* string. May be set to NULL if the field849* delimiter is not desired.850* @param[out] nelem On success, the number of fields in this851* octet string. May be set to NULL if the field852* count is not desired.853*854*855* @retval true if @p inp is a valid octet string856* @retval false if @p inp is not a valid octet string.857*/858static bool859bhnd_nvram_ident_octet_string(const char *inp, size_t ilen, char *delim,860size_t *nelem)861{862size_t elem_count;863size_t max_elem_count, min_elem_count;864size_t field_count;865char idelim;866867field_count = 0;868869/* Require exactly two digits. If we relax this, there is room870* for ambiguity with signed integers and the '-' delimiter */871min_elem_count = 2;872max_elem_count = 2;873874/* Identify the delimiter used. The standard delimiter for MAC875* addresses is ':', but some earlier NVRAM formats may use '-' */876for (const char *d = ":-";; d++) {877const char *loc;878879/* No delimiter found, not an octet string */880if (*d == '\0')881return (false);882883/* Look for the delimiter */884if ((loc = memchr(inp, *d, ilen)) == NULL)885continue;886887/* Delimiter found */888idelim = *loc;889break;890}891892/* To disambiguate from signed integers, if the delimiter is "-",893* the octets must be exactly 2 chars each */894if (idelim == '-')895min_elem_count = 2;896897/* String must be composed of individual octets (zero or more hex898* digits) separated by our delimiter. */899elem_count = 0;900for (const char *p = inp; (size_t)(p - inp) < ilen; p++) {901switch (*p) {902case ':':903case '-':904case '\0':905/* Hit a delim character; all delims must match906* the first delimiter used */907if (*p != '\0' && *p != idelim)908return (false);909910/* Must have parsed at least min_elem_count digits */911if (elem_count < min_elem_count)912return (false);913914/* Reset element count */915elem_count = 0;916917/* Bump field count */918field_count++;919break;920default:921/* More than maximum number of hex digits? */922if (elem_count >= max_elem_count)923return (false);924925/* Octet values must be hex digits */926if (!bhnd_nv_isxdigit(*p))927return (false);928929elem_count++;930break;931}932}933934if (delim != NULL)935*delim = idelim;936937if (nelem != NULL)938*nelem = field_count;939940return (true);941}942943/**944* Determine whether @p inp is in hexadecimal, octal, or decimal string945* format.946*947* - A @p str may be prefixed with a single optional '+' or '-' sign denoting948* signedness.949* - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a950* base 16 integer follows.951* - An octal @p str may include a '0' prefix, denoting that an octal integer952* follows.953*954* @param inp The string to be parsed.955* @param ilen The length of @p inp, in bytes.956* @param base The input string's base (2-36), or 0.957* @param[out] obase On success, will be set to the base of the parsed958* integer. May be set to NULL if the base is not959* desired.960*961* @retval true if @p inp is a valid number string962* @retval false if @p inp is not a valid number string.963* @retval false if @p base is invalid.964*/965static bool966bhnd_nvram_ident_num_string(const char *inp, size_t ilen, u_int base,967u_int *obase)968{969size_t nbytes, ndigits;970971nbytes = 0;972ndigits = 0;973974/* Parse and skip sign */975if (nbytes >= ilen)976return (false);977978if (inp[nbytes] == '-' || inp[nbytes] == '+')979nbytes++;980981/* Truncated after sign character? */982if (nbytes == ilen)983return (false);984985/* Identify (or validate) hex base, skipping 0x/0X prefix */986if (base == 16 || base == 0) {987/* Check for (and skip) 0x/0X prefix */988if (ilen - nbytes >= 2 && inp[nbytes] == '0' &&989(inp[nbytes+1] == 'x' || inp[nbytes+1] == 'X'))990{991base = 16;992nbytes += 2;993}994}995996/* Truncated after hex prefix? */997if (nbytes == ilen)998return (false);9991000/* Differentiate decimal/octal by looking for a leading 0 */1001if (base == 0) {1002if (inp[nbytes] == '0') {1003base = 8;1004} else {1005base = 10;1006}1007}10081009/* Consume and validate all remaining digit characters */1010for (; nbytes < ilen; nbytes++) {1011u_int carry;1012char c;10131014/* Parse carry value */1015c = inp[nbytes];1016if (bhnd_nv_isdigit(c)) {1017carry = c - '0';1018} else if (bhnd_nv_isxdigit(c)) {1019if (bhnd_nv_isupper(c))1020carry = (c - 'A') + 10;1021else1022carry = (c - 'a') + 10;1023} else {1024/* Hit a non-digit character */1025return (false);1026}10271028/* If carry is outside the base, it's not a valid digit1029* in the current parse context; consider it a non-digit1030* character */1031if (carry >= base)1032return (false);10331034/* Increment parsed digit count */1035ndigits++;1036}10371038/* Empty integer string? */1039if (ndigits == 0)1040return (false);10411042/* Valid integer -- provide the base and return */1043if (obase != NULL)1044*obase = base;1045return (true);1046}104710481049