Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_subr.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#ifdef _KERNEL3233#include <sys/ctype.h>34#include <sys/kernel.h>35#include <sys/limits.h>36#include <sys/malloc.h>37#include <sys/systm.h>3839#include <machine/_inttypes.h>4041#else /* !_KERNEL */4243#include <ctype.h>44#include <errno.h>45#include <inttypes.h>46#include <limits.h>47#include <stdbool.h>48#include <stdio.h>49#include <stdint.h>50#include <stdlib.h>51#include <string.h>5253#endif /* _KERNEL */5455#include "bhnd_nvram_io.h"56#include "bhnd_nvram_private.h"57#include "bhnd_nvram_value.h"5859#include "bhnd_nvram_map_data.h"6061/*62* Common NVRAM/SPROM support, including NVRAM variable map63* lookup.64*/6566#ifdef _KERNEL67MALLOC_DEFINE(M_BHND_NVRAM, "bhnd_nvram", "bhnd nvram data");68#endif6970/*71* CRC-8 lookup table used to checksum SPROM and NVRAM data via72* bhnd_nvram_crc8().73*74* Generated with following parameters:75* polynomial: CRC-8 (x^8 + x^7 + x^6 + x^4 + x^2 + 1)76* reflected bits: false77* reversed: true78*/79const uint8_t bhnd_nvram_crc8_tab[] = {800x00, 0xf7, 0xb9, 0x4e, 0x25, 0xd2, 0x9c, 0x6b, 0x4a, 0xbd, 0xf3,810x04, 0x6f, 0x98, 0xd6, 0x21, 0x94, 0x63, 0x2d, 0xda, 0xb1, 0x46,820x08, 0xff, 0xde, 0x29, 0x67, 0x90, 0xfb, 0x0c, 0x42, 0xb5, 0x7f,830x88, 0xc6, 0x31, 0x5a, 0xad, 0xe3, 0x14, 0x35, 0xc2, 0x8c, 0x7b,840x10, 0xe7, 0xa9, 0x5e, 0xeb, 0x1c, 0x52, 0xa5, 0xce, 0x39, 0x77,850x80, 0xa1, 0x56, 0x18, 0xef, 0x84, 0x73, 0x3d, 0xca, 0xfe, 0x09,860x47, 0xb0, 0xdb, 0x2c, 0x62, 0x95, 0xb4, 0x43, 0x0d, 0xfa, 0x91,870x66, 0x28, 0xdf, 0x6a, 0x9d, 0xd3, 0x24, 0x4f, 0xb8, 0xf6, 0x01,880x20, 0xd7, 0x99, 0x6e, 0x05, 0xf2, 0xbc, 0x4b, 0x81, 0x76, 0x38,890xcf, 0xa4, 0x53, 0x1d, 0xea, 0xcb, 0x3c, 0x72, 0x85, 0xee, 0x19,900x57, 0xa0, 0x15, 0xe2, 0xac, 0x5b, 0x30, 0xc7, 0x89, 0x7e, 0x5f,910xa8, 0xe6, 0x11, 0x7a, 0x8d, 0xc3, 0x34, 0xab, 0x5c, 0x12, 0xe5,920x8e, 0x79, 0x37, 0xc0, 0xe1, 0x16, 0x58, 0xaf, 0xc4, 0x33, 0x7d,930x8a, 0x3f, 0xc8, 0x86, 0x71, 0x1a, 0xed, 0xa3, 0x54, 0x75, 0x82,940xcc, 0x3b, 0x50, 0xa7, 0xe9, 0x1e, 0xd4, 0x23, 0x6d, 0x9a, 0xf1,950x06, 0x48, 0xbf, 0x9e, 0x69, 0x27, 0xd0, 0xbb, 0x4c, 0x02, 0xf5,960x40, 0xb7, 0xf9, 0x0e, 0x65, 0x92, 0xdc, 0x2b, 0x0a, 0xfd, 0xb3,970x44, 0x2f, 0xd8, 0x96, 0x61, 0x55, 0xa2, 0xec, 0x1b, 0x70, 0x87,980xc9, 0x3e, 0x1f, 0xe8, 0xa6, 0x51, 0x3a, 0xcd, 0x83, 0x74, 0xc1,990x36, 0x78, 0x8f, 0xe4, 0x13, 0x5d, 0xaa, 0x8b, 0x7c, 0x32, 0xc5,1000xae, 0x59, 0x17, 0xe0, 0x2a, 0xdd, 0x93, 0x64, 0x0f, 0xf8, 0xb6,1010x41, 0x60, 0x97, 0xd9, 0x2e, 0x45, 0xb2, 0xfc, 0x0b, 0xbe, 0x49,1020x07, 0xf0, 0x9b, 0x6c, 0x22, 0xd5, 0xf4, 0x03, 0x4d, 0xba, 0xd1,1030x26, 0x68, 0x9f104};105106/**107* Return a human readable name for @p type.108*109* @param type The type to query.110*/111const char *112bhnd_nvram_type_name(bhnd_nvram_type type)113{114switch (type) {115case BHND_NVRAM_TYPE_UINT8:116return ("uint8");117case BHND_NVRAM_TYPE_UINT16:118return ("uint16");119case BHND_NVRAM_TYPE_UINT32:120return ("uint32");121case BHND_NVRAM_TYPE_UINT64:122return ("uint64");123case BHND_NVRAM_TYPE_CHAR:124return ("char");125case BHND_NVRAM_TYPE_INT8:126return ("int8");127case BHND_NVRAM_TYPE_INT16:128return ("int16");129case BHND_NVRAM_TYPE_INT32:130return ("int32");131case BHND_NVRAM_TYPE_INT64:132return ("int64");133case BHND_NVRAM_TYPE_STRING:134return ("string");135case BHND_NVRAM_TYPE_BOOL:136return ("bool");137case BHND_NVRAM_TYPE_NULL:138return ("null");139case BHND_NVRAM_TYPE_DATA:140return ("data");141case BHND_NVRAM_TYPE_UINT8_ARRAY:142return ("uint8[]");143case BHND_NVRAM_TYPE_UINT16_ARRAY:144return ("uint16[]");145case BHND_NVRAM_TYPE_UINT32_ARRAY:146return ("uint32[]");147case BHND_NVRAM_TYPE_UINT64_ARRAY:148return ("uint64[]");149case BHND_NVRAM_TYPE_INT8_ARRAY:150return ("int8[]");151case BHND_NVRAM_TYPE_INT16_ARRAY:152return ("int16[]");153case BHND_NVRAM_TYPE_INT32_ARRAY:154return ("int32[]");155case BHND_NVRAM_TYPE_INT64_ARRAY:156return ("int64[]");157case BHND_NVRAM_TYPE_CHAR_ARRAY:158return ("char[]");159case BHND_NVRAM_TYPE_STRING_ARRAY:160return ("string[]");161case BHND_NVRAM_TYPE_BOOL_ARRAY:162return ("bool[]");163}164165/* Quiesce gcc4.2 */166BHND_NV_PANIC("bhnd nvram type %u unknown", type);167}168169/**170* Return true if @p type is a signed integer type, false otherwise.171*172* Will return false for all array types.173*174* @param type The type to query.175*/176bool177bhnd_nvram_is_signed_type(bhnd_nvram_type type)178{179switch (type) {180case BHND_NVRAM_TYPE_INT8:181case BHND_NVRAM_TYPE_INT16:182case BHND_NVRAM_TYPE_INT32:183case BHND_NVRAM_TYPE_INT64:184BHND_NV_ASSERT(bhnd_nvram_is_int_type(type), ("non-int type?"));185return (true);186187case BHND_NVRAM_TYPE_CHAR:188case BHND_NVRAM_TYPE_UINT8:189case BHND_NVRAM_TYPE_UINT16:190case BHND_NVRAM_TYPE_UINT32:191case BHND_NVRAM_TYPE_UINT64:192case BHND_NVRAM_TYPE_STRING:193case BHND_NVRAM_TYPE_BOOL:194case BHND_NVRAM_TYPE_NULL:195case BHND_NVRAM_TYPE_DATA:196case BHND_NVRAM_TYPE_UINT8_ARRAY:197case BHND_NVRAM_TYPE_UINT16_ARRAY:198case BHND_NVRAM_TYPE_UINT32_ARRAY:199case BHND_NVRAM_TYPE_UINT64_ARRAY:200case BHND_NVRAM_TYPE_INT8_ARRAY:201case BHND_NVRAM_TYPE_INT16_ARRAY:202case BHND_NVRAM_TYPE_INT32_ARRAY:203case BHND_NVRAM_TYPE_INT64_ARRAY:204case BHND_NVRAM_TYPE_CHAR_ARRAY:205case BHND_NVRAM_TYPE_STRING_ARRAY:206case BHND_NVRAM_TYPE_BOOL_ARRAY:207return (false);208}209210/* Quiesce gcc4.2 */211BHND_NV_PANIC("bhnd nvram type %u unknown", type);212}213214/**215* Return true if @p type is an unsigned integer type, false otherwise.216*217* @param type The type to query.218*219* @return Will return false for all array types.220* @return Will return true for BHND_NVRAM_TYPE_CHAR.221*/222bool223bhnd_nvram_is_unsigned_type(bhnd_nvram_type type)224{225/* If an integer type, must be either signed or unsigned */226if (!bhnd_nvram_is_int_type(type))227return (false);228229return (!bhnd_nvram_is_signed_type(type));230}231232/**233* Return true if bhnd_nvram_is_signed_type() or bhnd_nvram_is_unsigned_type()234* returns true for @p type.235*236* @param type The type to query.237*/238bool239bhnd_nvram_is_int_type(bhnd_nvram_type type)240{241switch (type) {242case BHND_NVRAM_TYPE_UINT8:243case BHND_NVRAM_TYPE_UINT16:244case BHND_NVRAM_TYPE_UINT32:245case BHND_NVRAM_TYPE_UINT64:246case BHND_NVRAM_TYPE_INT8:247case BHND_NVRAM_TYPE_INT16:248case BHND_NVRAM_TYPE_INT32:249case BHND_NVRAM_TYPE_INT64:250return (true);251252case BHND_NVRAM_TYPE_CHAR:253case BHND_NVRAM_TYPE_STRING:254case BHND_NVRAM_TYPE_BOOL:255case BHND_NVRAM_TYPE_NULL:256case BHND_NVRAM_TYPE_DATA:257case BHND_NVRAM_TYPE_UINT8_ARRAY:258case BHND_NVRAM_TYPE_UINT16_ARRAY:259case BHND_NVRAM_TYPE_UINT32_ARRAY:260case BHND_NVRAM_TYPE_UINT64_ARRAY:261case BHND_NVRAM_TYPE_INT8_ARRAY:262case BHND_NVRAM_TYPE_INT16_ARRAY:263case BHND_NVRAM_TYPE_INT32_ARRAY:264case BHND_NVRAM_TYPE_INT64_ARRAY:265case BHND_NVRAM_TYPE_CHAR_ARRAY:266case BHND_NVRAM_TYPE_STRING_ARRAY:267case BHND_NVRAM_TYPE_BOOL_ARRAY:268return (false);269}270271/* Quiesce gcc4.2 */272BHND_NV_PANIC("bhnd nvram type %u unknown", type);273}274275/**276* Return true if @p type is an array type, false otherwise.277*278* @param type The type to query.279*/280bool281bhnd_nvram_is_array_type(bhnd_nvram_type type)282{283switch (type) {284case BHND_NVRAM_TYPE_UINT8:285case BHND_NVRAM_TYPE_UINT16:286case BHND_NVRAM_TYPE_UINT32:287case BHND_NVRAM_TYPE_UINT64:288case BHND_NVRAM_TYPE_INT8:289case BHND_NVRAM_TYPE_INT16:290case BHND_NVRAM_TYPE_INT32:291case BHND_NVRAM_TYPE_INT64:292case BHND_NVRAM_TYPE_CHAR:293case BHND_NVRAM_TYPE_STRING:294case BHND_NVRAM_TYPE_BOOL:295case BHND_NVRAM_TYPE_NULL:296case BHND_NVRAM_TYPE_DATA:297return (false);298299case BHND_NVRAM_TYPE_UINT8_ARRAY:300case BHND_NVRAM_TYPE_UINT16_ARRAY:301case BHND_NVRAM_TYPE_UINT32_ARRAY:302case BHND_NVRAM_TYPE_UINT64_ARRAY:303case BHND_NVRAM_TYPE_INT8_ARRAY:304case BHND_NVRAM_TYPE_INT16_ARRAY:305case BHND_NVRAM_TYPE_INT32_ARRAY:306case BHND_NVRAM_TYPE_INT64_ARRAY:307case BHND_NVRAM_TYPE_CHAR_ARRAY:308case BHND_NVRAM_TYPE_STRING_ARRAY:309case BHND_NVRAM_TYPE_BOOL_ARRAY:310return (true);311}312313/* Quiesce gcc4.2 */314BHND_NV_PANIC("bhnd nvram type %u unknown", type);315}316317/**318* If @p type is an array type, return the base element type. Otherwise,319* returns @p type.320*321* @param type The type to query.322*/323bhnd_nvram_type324bhnd_nvram_base_type(bhnd_nvram_type type)325{326switch (type) {327case BHND_NVRAM_TYPE_UINT8:328case BHND_NVRAM_TYPE_UINT16:329case BHND_NVRAM_TYPE_UINT32:330case BHND_NVRAM_TYPE_UINT64:331case BHND_NVRAM_TYPE_INT8:332case BHND_NVRAM_TYPE_INT16:333case BHND_NVRAM_TYPE_INT32:334case BHND_NVRAM_TYPE_INT64:335case BHND_NVRAM_TYPE_CHAR:336case BHND_NVRAM_TYPE_STRING:337case BHND_NVRAM_TYPE_BOOL:338case BHND_NVRAM_TYPE_NULL:339case BHND_NVRAM_TYPE_DATA:340return (type);341342case BHND_NVRAM_TYPE_UINT8_ARRAY: return (BHND_NVRAM_TYPE_UINT8);343case BHND_NVRAM_TYPE_UINT16_ARRAY: return (BHND_NVRAM_TYPE_UINT16);344case BHND_NVRAM_TYPE_UINT32_ARRAY: return (BHND_NVRAM_TYPE_UINT32);345case BHND_NVRAM_TYPE_UINT64_ARRAY: return (BHND_NVRAM_TYPE_UINT64);346case BHND_NVRAM_TYPE_INT8_ARRAY: return (BHND_NVRAM_TYPE_INT8);347case BHND_NVRAM_TYPE_INT16_ARRAY: return (BHND_NVRAM_TYPE_INT16);348case BHND_NVRAM_TYPE_INT32_ARRAY: return (BHND_NVRAM_TYPE_INT32);349case BHND_NVRAM_TYPE_INT64_ARRAY: return (BHND_NVRAM_TYPE_INT64);350case BHND_NVRAM_TYPE_CHAR_ARRAY: return (BHND_NVRAM_TYPE_CHAR);351case BHND_NVRAM_TYPE_STRING_ARRAY: return (BHND_NVRAM_TYPE_STRING);352case BHND_NVRAM_TYPE_BOOL_ARRAY: return (BHND_NVRAM_TYPE_BOOL);353}354355/* Quiesce gcc4.2 */356BHND_NV_PANIC("bhnd nvram type %u unknown", type);357}358359/**360* Return the raw data type used to represent values of @p type, or return361* @p type is @p type is not a complex type.362*363* @param type The type to query.364*/365bhnd_nvram_type366bhnd_nvram_raw_type(bhnd_nvram_type type)367{368switch (type) {369case BHND_NVRAM_TYPE_CHAR:370return (BHND_NVRAM_TYPE_UINT8);371372case BHND_NVRAM_TYPE_CHAR_ARRAY:373return (BHND_NVRAM_TYPE_UINT8_ARRAY);374375case BHND_NVRAM_TYPE_BOOL: {376_Static_assert(sizeof(bhnd_nvram_bool_t) == sizeof(uint8_t),377"bhnd_nvram_bool_t must be uint8-representable");378return (BHND_NVRAM_TYPE_UINT8);379}380381case BHND_NVRAM_TYPE_BOOL_ARRAY:382return (BHND_NVRAM_TYPE_UINT8_ARRAY);383384case BHND_NVRAM_TYPE_DATA:385return (BHND_NVRAM_TYPE_UINT8_ARRAY);386387case BHND_NVRAM_TYPE_STRING:388case BHND_NVRAM_TYPE_STRING_ARRAY:389return (BHND_NVRAM_TYPE_UINT8_ARRAY);390391case BHND_NVRAM_TYPE_UINT8:392case BHND_NVRAM_TYPE_UINT16:393case BHND_NVRAM_TYPE_UINT32:394case BHND_NVRAM_TYPE_UINT64:395case BHND_NVRAM_TYPE_INT8:396case BHND_NVRAM_TYPE_INT16:397case BHND_NVRAM_TYPE_INT32:398case BHND_NVRAM_TYPE_INT64:399case BHND_NVRAM_TYPE_NULL:400case BHND_NVRAM_TYPE_UINT8_ARRAY:401case BHND_NVRAM_TYPE_UINT16_ARRAY:402case BHND_NVRAM_TYPE_UINT32_ARRAY:403case BHND_NVRAM_TYPE_UINT64_ARRAY:404case BHND_NVRAM_TYPE_INT8_ARRAY:405case BHND_NVRAM_TYPE_INT16_ARRAY:406case BHND_NVRAM_TYPE_INT32_ARRAY:407case BHND_NVRAM_TYPE_INT64_ARRAY:408return (type);409}410411/* Quiesce gcc4.2 */412BHND_NV_PANIC("bhnd nvram type %u unknown", type);413}414415/**416* Return the size, in bytes, of a single element of @p type, or 0417* if @p type is a variable-width type.418*419* @param type The type to query.420*/421size_t422bhnd_nvram_type_width(bhnd_nvram_type type)423{424switch (type) {425case BHND_NVRAM_TYPE_STRING:426case BHND_NVRAM_TYPE_STRING_ARRAY:427case BHND_NVRAM_TYPE_DATA:428return (0);429430case BHND_NVRAM_TYPE_NULL:431return (0);432433case BHND_NVRAM_TYPE_BOOL:434case BHND_NVRAM_TYPE_BOOL_ARRAY:435return (sizeof(bhnd_nvram_bool_t));436437case BHND_NVRAM_TYPE_CHAR:438case BHND_NVRAM_TYPE_CHAR_ARRAY:439case BHND_NVRAM_TYPE_UINT8:440case BHND_NVRAM_TYPE_UINT8_ARRAY:441case BHND_NVRAM_TYPE_INT8:442case BHND_NVRAM_TYPE_INT8_ARRAY:443return (sizeof(uint8_t));444445case BHND_NVRAM_TYPE_UINT16:446case BHND_NVRAM_TYPE_UINT16_ARRAY:447case BHND_NVRAM_TYPE_INT16:448case BHND_NVRAM_TYPE_INT16_ARRAY:449return (sizeof(uint16_t));450451case BHND_NVRAM_TYPE_UINT32:452case BHND_NVRAM_TYPE_UINT32_ARRAY:453case BHND_NVRAM_TYPE_INT32:454case BHND_NVRAM_TYPE_INT32_ARRAY:455return (sizeof(uint32_t));456457case BHND_NVRAM_TYPE_UINT64:458case BHND_NVRAM_TYPE_UINT64_ARRAY:459case BHND_NVRAM_TYPE_INT64:460case BHND_NVRAM_TYPE_INT64_ARRAY:461return (sizeof(uint64_t));462}463464/* Quiesce gcc4.2 */465BHND_NV_PANIC("bhnd nvram type %u unknown", type);466}467468/**469* Return the native host alignment for values of @p type.470*471* @param type The type to query.472*/473size_t474bhnd_nvram_type_host_align(bhnd_nvram_type type)475{476switch (type) {477case BHND_NVRAM_TYPE_CHAR:478case BHND_NVRAM_TYPE_CHAR_ARRAY:479case BHND_NVRAM_TYPE_DATA:480case BHND_NVRAM_TYPE_STRING:481case BHND_NVRAM_TYPE_STRING_ARRAY:482return (_Alignof(uint8_t));483case BHND_NVRAM_TYPE_BOOL:484case BHND_NVRAM_TYPE_BOOL_ARRAY: {485_Static_assert(sizeof(bhnd_nvram_bool_t) == sizeof(uint8_t),486"bhnd_nvram_bool_t must be uint8-representable");487return (_Alignof(uint8_t));488}489case BHND_NVRAM_TYPE_NULL:490return (1);491case BHND_NVRAM_TYPE_UINT8:492case BHND_NVRAM_TYPE_UINT8_ARRAY:493return (_Alignof(uint8_t));494case BHND_NVRAM_TYPE_UINT16:495case BHND_NVRAM_TYPE_UINT16_ARRAY:496return (_Alignof(uint16_t));497case BHND_NVRAM_TYPE_UINT32:498case BHND_NVRAM_TYPE_UINT32_ARRAY:499return (_Alignof(uint32_t));500case BHND_NVRAM_TYPE_UINT64:501case BHND_NVRAM_TYPE_UINT64_ARRAY:502return (_Alignof(uint64_t));503case BHND_NVRAM_TYPE_INT8:504case BHND_NVRAM_TYPE_INT8_ARRAY:505return (_Alignof(int8_t));506case BHND_NVRAM_TYPE_INT16:507case BHND_NVRAM_TYPE_INT16_ARRAY:508return (_Alignof(int16_t));509case BHND_NVRAM_TYPE_INT32:510case BHND_NVRAM_TYPE_INT32_ARRAY:511return (_Alignof(int32_t));512case BHND_NVRAM_TYPE_INT64:513case BHND_NVRAM_TYPE_INT64_ARRAY:514return (_Alignof(int64_t));515}516517/* Quiesce gcc4.2 */518BHND_NV_PANIC("bhnd nvram type %u unknown", type);519}520521/**522* Iterate over all strings in the @p inp string array (see523* BHND_NVRAM_TYPE_STRING_ARRAY).524*525* @param inp The string array to be iterated. This must be a526* buffer of one or more NUL-terminated strings.527* @param ilen The size, in bytes, of @p inp, including any528* terminating NUL character(s).529* @param prev The pointer previously returned by530* bhnd_nvram_string_array_next(), or NULL to begin531* iteration.532* @param[in,out] olen If @p prev is non-NULL, @p olen must be a533* pointer to the length previously returned by534* bhnd_nvram_string_array_next(). On success, will535* be set to the next element's length, in bytes.536*537* @retval non-NULL A reference to the next NUL-terminated string538* @retval NULL If the end of the string array is reached.539*540* @see BHND_NVRAM_TYPE_STRING_ARRAY541*/542const char *543bhnd_nvram_string_array_next(const char *inp, size_t ilen, const char *prev,544size_t *olen)545{546return (bhnd_nvram_value_array_next(inp, ilen,547BHND_NVRAM_TYPE_STRING_ARRAY, prev, olen));548}549550/* used by bhnd_nvram_find_vardefn() */551static int552bhnd_nvram_find_vardefn_compare(const void *key, const void *rhs)553{554const struct bhnd_nvram_vardefn *r = rhs;555556return (strcmp((const char *)key, r->name));557}558559/**560* Find and return the variable definition for @p varname, if any.561*562* @param varname variable name563*564* @retval bhnd_nvram_vardefn If a valid definition for @p varname is found.565* @retval NULL If no definition for @p varname is found.566*/567const struct bhnd_nvram_vardefn *568bhnd_nvram_find_vardefn(const char *varname)569{570return (bsearch(varname, bhnd_nvram_vardefns, bhnd_nvram_num_vardefns,571sizeof(bhnd_nvram_vardefns[0]), bhnd_nvram_find_vardefn_compare));572}573574/**575* Return the variable ID for a variable definition.576*577* @param defn Variable definition previously returned by578* bhnd_nvram_find_vardefn() or bhnd_nvram_get_vardefn().579*/580size_t581bhnd_nvram_get_vardefn_id(const struct bhnd_nvram_vardefn *defn)582{583BHND_NV_ASSERT(584defn >= bhnd_nvram_vardefns &&585defn <= &bhnd_nvram_vardefns[bhnd_nvram_num_vardefns-1],586("invalid variable definition pointer %p", defn));587588return (defn - bhnd_nvram_vardefns);589}590591/**592* Return the variable definition with the given @p id, or NULL593* if no such variable ID is defined.594*595* @param id variable ID.596*597* @retval bhnd_nvram_vardefn If a valid definition for @p id is found.598* @retval NULL If no definition for @p id is found.599*/600const struct bhnd_nvram_vardefn *601bhnd_nvram_get_vardefn(size_t id)602{603if (id >= bhnd_nvram_num_vardefns)604return (NULL);605606return (&bhnd_nvram_vardefns[id]);607}608609/**610* Validate an NVRAM variable name.611*612* Scans for special characters (path delimiters, value delimiters, path613* alias prefixes), returning false if the given name cannot be used614* as a relative NVRAM key.615*616* @param name A relative NVRAM variable name to validate.617*618* @retval true If @p name is a valid relative NVRAM key.619* @retval false If @p name should not be used as a relative NVRAM key.620*/621bool622bhnd_nvram_validate_name(const char *name)623{624/* Reject path-prefixed variable names */625if (bhnd_nvram_trim_path_name(name) != name)626return (false);627628/* Reject device path alias declarations (devpath[1-9][0-9]*.*\0) */629if (strncmp(name, "devpath", strlen("devpath")) == 0) {630const char *p;631char *endp;632633/* Check for trailing [1-9][0-9]* */634p = name + strlen("devpath");635strtoul(p, &endp, 10);636if (endp != p)637return (false);638}639640/* Scan for [^A-Za-z_0-9] */641for (const char *p = name; *p != '\0'; p++) {642switch (*p) {643/* [0-9_] */644case '0': case '1': case '2': case '3': case '4':645case '5': case '6': case '7': case '8': case '9':646case '_':647break;648649/* [A-Za-z] */650default:651if (!bhnd_nv_isalpha(*p))652return (false);653break;654}655}656657return (true);658}659660/**661* Parses the string in the optionally NUL-terminated @p str to as an integer662* value of @p otype, accepting any integer format supported by the standard663* strtoul().664*665* - Any leading whitespace in @p str -- as defined by the equivalent of666* calling isspace_l() with an ASCII locale -- will be ignored.667* - A @p str may be prefixed with a single optional '+' or '-' sign denoting668* signedness.669* - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a670* base 16 integer follows.671* - An octal @p str may include a '0' prefix, denoting that an octal integer672* follows.673*674* If a @p base of 0 is specified, the base will be determined according675* to the string's initial prefix, as per strtoul()'s documented behavior.676*677* When parsing a base 16 integer to a signed representation, if no explicit678* sign prefix is given, the string will be parsed as the raw two's complement679* representation of the signed integer value.680*681* @param str The string to be parsed.682* @param maxlen The maximum number of bytes to be read in683* @p str.684* @param base The input string's base (2-36), or 0.685* @param[out] nbytes On success or failure, will be set to the total686* number of parsed bytes. If the total number of687* bytes is not desired, a NULL pointer may be688* provided.689* @param[out] outp On success, the parsed integer value will be690* written to @p outp. This argment may be NULL if691* the value is not desired.692* @param[in,out] olen The capacity of @p outp. On success, will be set693* to the actual size of the requested value.694* @param otype The integer type to be parsed.695*696* @retval 0 success697* @retval EINVAL if an invalid @p base is specified.698* @retval EINVAL if an unsupported (or non-integer) @p otype is699* specified.700* @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too701* small to hold the requested value.702* @retval EFTYPE if @p str cannot be parsed as an integer of @p base.703* @retval ERANGE If the integer parsed from @p str is too large to be704* represented as a value of @p otype.705*/706int707bhnd_nvram_parse_int(const char *str, size_t maxlen, u_int base,708size_t *nbytes, void *outp, size_t *olen, bhnd_nvram_type otype)709{710uint64_t value;711uint64_t carry_max, value_max;712uint64_t type_max;713size_t limit, local_nbytes;714size_t ndigits;715bool negative, sign, twos_compl;716717/* Must be an integer type */718if (!bhnd_nvram_is_int_type(otype))719return (EINVAL);720721/* Determine output byte limit */722if (outp != NULL)723limit = *olen;724else725limit = 0;726727/* We always need a byte count. If the caller provides a NULL nbytes,728* track our position in a stack variable */729if (nbytes == NULL)730nbytes = &local_nbytes;731732value = 0;733ndigits = 0;734*nbytes = 0;735negative = false;736sign = false;737738/* Validate the specified base */739if (base != 0 && !(base >= 2 && base <= 36))740return (EINVAL);741742/* Skip any leading whitespace */743for (; *nbytes < maxlen; (*nbytes)++) {744if (!bhnd_nv_isspace(str[*nbytes]))745break;746}747748/* Empty string? */749if (*nbytes == maxlen)750return (EFTYPE);751752/* Parse and skip sign */753if (str[*nbytes] == '-') {754negative = true;755sign = true;756(*nbytes)++;757} else if (str[*nbytes] == '+') {758sign = true;759(*nbytes)++;760}761762/* Truncated after sign character? */763if (*nbytes == maxlen)764return (EFTYPE);765766/* Identify (or validate) hex base, skipping 0x/0X prefix */767if (base == 16 || base == 0) {768/* Check for (and skip) 0x/0X prefix */769if (maxlen - *nbytes >= 2 && str[*nbytes] == '0' &&770(str[*nbytes+1] == 'x' || str[*nbytes+1] == 'X'))771{772base = 16;773(*nbytes) += 2;774}775}776777/* Truncated after hex prefix? */778if (*nbytes == maxlen)779return (EFTYPE);780781/* Differentiate decimal/octal by looking for a leading 0 */782if (base == 0) {783if (str[*nbytes] == '0') {784base = 8;785} else {786base = 10;787}788}789790/* Only enable twos-compliment signed integer parsing enabled if the791* input is base 16, and no explicit sign prefix was provided */792if (!sign && base == 16)793twos_compl = true;794else795twos_compl = false;796797/* Determine the maximum value representable by the requested type */798switch (otype) {799case BHND_NVRAM_TYPE_CHAR:800case BHND_NVRAM_TYPE_UINT8:801type_max = (uint64_t)UINT8_MAX;802break;803case BHND_NVRAM_TYPE_UINT16:804type_max = (uint64_t)UINT16_MAX;805break;806case BHND_NVRAM_TYPE_UINT32:807type_max = (uint64_t)UINT32_MAX;808break;809case BHND_NVRAM_TYPE_UINT64:810type_max = (uint64_t)UINT64_MAX;811break;812813case BHND_NVRAM_TYPE_INT8:814if (twos_compl)815type_max = (uint64_t)UINT8_MAX;816else if (negative)817type_max = -(uint64_t)INT8_MIN;818else819type_max = (uint64_t)INT8_MAX;820break;821822case BHND_NVRAM_TYPE_INT16:823if (twos_compl)824type_max = (uint64_t)UINT16_MAX;825else if (negative)826type_max = -(uint64_t)INT16_MIN;827else828type_max = (uint64_t)INT16_MAX;829break;830831case BHND_NVRAM_TYPE_INT32:832if (twos_compl)833type_max = (uint64_t)UINT32_MAX;834else if (negative)835type_max = -(uint64_t)INT32_MIN;836else837type_max = (uint64_t)INT32_MAX;838break;839840case BHND_NVRAM_TYPE_INT64:841if (twos_compl)842type_max = (uint64_t)UINT64_MAX;843else if (negative)844type_max = -(uint64_t)INT64_MIN;845else846type_max = (uint64_t)INT64_MAX;847break;848849default:850BHND_NV_LOG("unsupported integer type: %d\n", otype);851return (EINVAL);852}853854/* The maximum value after which an additional carry would overflow */855value_max = type_max / (uint64_t)base;856857/* The maximum carry value given a value equal to value_max */858carry_max = type_max % (uint64_t)base;859860/* Consume input until we hit maxlen or a non-digit character */861for (; *nbytes < maxlen; (*nbytes)++) {862u_long carry;863char c;864865/* Parse carry value */866c = str[*nbytes];867if (bhnd_nv_isdigit(c)) {868carry = c - '0';869} else if (bhnd_nv_isxdigit(c)) {870if (bhnd_nv_isupper(c))871carry = (c - 'A') + 10;872else873carry = (c - 'a') + 10;874} else {875/* Hit first non-digit character */876break;877}878879/* If carry is outside the base, it's not a valid digit880* in the current parse context; consider it a non-digit881* character */882if (carry >= (uint64_t)base)883break;884885/* Increment count of parsed digits */886ndigits++;887888if (value > value_max) {889/* -Any- carry value would overflow */890return (ERANGE);891} else if (value == value_max && carry > carry_max) {892/* -This- carry value would overflow */893return (ERANGE);894}895896value *= (uint64_t)base;897value += carry;898}899900/* If we hit a non-digit character before parsing the first digit,901* we hit an empty integer string. */902if (ndigits == 0)903return (EFTYPE);904905if (negative)906value = -value;907908/* Provide (and verify) required length */909*olen = bhnd_nvram_type_width(otype);910if (outp == NULL)911return (0);912else if (limit < *olen)913return (ENOMEM);914915/* Provide result */916switch (otype) {917case BHND_NVRAM_TYPE_CHAR:918case BHND_NVRAM_TYPE_UINT8:919*(uint8_t *)outp = (uint8_t)value;920break;921case BHND_NVRAM_TYPE_UINT16:922*(uint16_t *)outp = (uint16_t)value;923break;924case BHND_NVRAM_TYPE_UINT32:925*(uint32_t *)outp = (uint32_t)value;926break;927case BHND_NVRAM_TYPE_UINT64:928*(uint64_t *)outp = (uint64_t)value;929break;930931case BHND_NVRAM_TYPE_INT8:932*(int8_t *)outp = (int8_t)(int64_t)value;933break;934case BHND_NVRAM_TYPE_INT16:935*(int16_t *)outp = (int16_t)(int64_t)value;936break;937case BHND_NVRAM_TYPE_INT32:938*(int32_t *)outp = (int32_t)(int64_t)value;939break;940case BHND_NVRAM_TYPE_INT64:941*(int64_t *)outp = (int64_t)value;942break;943default:944/* unreachable */945BHND_NV_PANIC("unhandled type %d\n", otype);946}947948return (0);949}950951/**952* Trim leading path (pci/1/1) or path alias (0:) prefix from @p name, if any,953* returning a pointer to the start of the relative variable name.954*955* @par Examples956*957* - "/foo" -> "foo"958* - "dev/pci/foo" -> "foo"959* - "0:foo" -> "foo"960* - "foo" -> "foo"961*962* @param name The string to be trimmed.963*964* @return A pointer to the start of the relative variable name in @p name.965*/966const char *967bhnd_nvram_trim_path_name(const char *name)968{969char *endp;970971/* path alias prefix? (0:varname) */972if (bhnd_nv_isdigit(*name)) {973/* Parse '0...:' alias prefix, if it exists */974strtoul(name, &endp, 10);975if (endp != name && *endp == ':') {976/* Variable name follows 0: prefix */977return (endp+1);978}979}980981/* device path prefix? (pci/1/1/varname) */982if ((endp = strrchr(name, '/')) != NULL) {983/* Variable name follows the final path separator '/' */984return (endp+1);985}986987/* variable name is not prefixed */988return (name);989}990991/**992* Parse a 'name=value' string.993*994* @param env The string to be parsed.995* @param env_len The length of @p envp.996* @param delim The delimiter used in @p envp. This will generally be '='.997* @param[out] name If not NULL, a pointer to the name string. This argument998* may be NULL.999* @param[out] name_len On success, the length of the name substring. This1000* argument may be NULL.1001* @param[out] value On success, a pointer to the value substring. This argument1002* may be NULL.1003* @param[out] value_len On success, the length of the value substring. This1004* argument may be NULL.1005*1006* @retval 0 success1007* @retval EINVAL if parsing @p envp fails.1008*/1009int1010bhnd_nvram_parse_env(const char *env, size_t env_len, char delim,1011const char **name, size_t *name_len, const char **value, size_t *value_len)1012{1013const char *p;10141015/* Name */1016if ((p = memchr(env, delim, env_len)) == NULL) {1017BHND_NV_LOG("delimiter '%c' not found in '%.*s'\n", delim,1018BHND_NV_PRINT_WIDTH(env_len), env);1019return (EINVAL);1020}10211022/* Name */1023if (name != NULL)1024*name = env;1025if (name_len != NULL)1026*name_len = p - env;10271028/* Skip delim */1029p++;10301031/* Value */1032if (value != NULL)1033*value = p;1034if (value_len != NULL)1035*value_len = env_len - (p - env);10361037return (0);1038}10391040/**1041* Parse a field value, returning the actual pointer to the first1042* non-whitespace character and the total size of the field.1043*1044* @param[in,out] inp The field string to parse. Will be updated to point1045* at the first non-whitespace character found.1046* @param ilen The length of @p inp, in bytes.1047* @param delim The field delimiter to search for.1048*1049* @return Returns the actual size of the field data.1050*/1051size_t1052bhnd_nvram_parse_field(const char **inp, size_t ilen, char delim)1053{1054const char *p, *sp;10551056/* Skip any leading whitespace */1057for (sp = *inp; (size_t)(sp-*inp) < ilen && bhnd_nv_isspace(*sp); sp++)1058continue;10591060*inp = sp;10611062/* Find the last field character */1063for (p = *inp; (size_t)(p - *inp) < ilen; p++) {1064if (*p == delim || *p == '\0')1065break;1066}10671068return (p - *inp);1069}10701071/**1072* Parse a field value, returning the actual pointer to the first1073* non-whitespace character and the total size of the field, minus1074* any trailing whitespace.1075*1076* @param[in,out] inp The field string to parse. Will be updated to point1077* at the first non-whitespace character found.1078* @param ilen The length of the parsed field, in bytes, excluding the1079* field elimiter and any trailing whitespace.1080* @param delim The field delimiter to search for.1081*1082* @return Returns the actual size of the field data.1083*/1084size_t1085bhnd_nvram_trim_field(const char **inp, size_t ilen, char delim)1086{1087const char *sp;1088size_t plen;10891090plen = bhnd_nvram_parse_field(inp, ilen, delim);10911092/* Trim trailing whitespace */1093sp = *inp;1094while (plen > 0) {1095if (!bhnd_nv_isspace(*(sp + plen - 1)))1096break;10971098plen--;1099}11001101return (plen);1102}110311041105