1 /*- 2 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13 * redistribution must be conditioned upon including a substantially 14 * similar Disclaimer requirement for further binary redistribution. 15 * 16 * NO WARRANTY 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 * THE POSSIBILITY OF SUCH DAMAGES. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 35 #ifdef _KERNEL 36 37 #include <sys/ctype.h> 38 #include <sys/kernel.h> 39 #include <sys/limits.h> 40 #include <sys/malloc.h> 41 #include <sys/systm.h> 42 43 #include <machine/_inttypes.h> 44 45 #else /* !_KERNEL */ 46 47 #include <ctype.h> 48 #include <errno.h> 49 #include <inttypes.h> 50 #include <limits.h> 51 #include <stdbool.h> 52 #include <stdio.h> 53 #include <stdint.h> 54 #include <stdlib.h> 55 #include <string.h> 56 57 #endif /* _KERNEL */ 58 59 #include "bhnd_nvram_io.h" 60 #include "bhnd_nvram_private.h" 61 #include "bhnd_nvram_value.h" 62 63 #include "bhnd_nvram_map_data.h" 64 65 /* 66 * Common NVRAM/SPROM support, including NVRAM variable map 67 * lookup. 68 */ 69 70 #ifdef _KERNEL 71 MALLOC_DEFINE(M_BHND_NVRAM, "bhnd_nvram", "bhnd nvram data"); 72 #endif 73 74 /** signed/unsigned 32-bit integer value storage */ 75 union bhnd_nvram_int_storage { 76 uint32_t u32; 77 int32_t s32; 78 }; 79 80 /* 81 * CRC-8 lookup table used to checksum SPROM and NVRAM data via 82 * bhnd_nvram_crc8(). 83 * 84 * Generated with following parameters: 85 * polynomial: CRC-8 (x^8 + x^7 + x^6 + x^4 + x^2 + 1) 86 * reflected bits: false 87 * reversed: true 88 */ 89 const uint8_t bhnd_nvram_crc8_tab[] = { 90 0x00, 0xf7, 0xb9, 0x4e, 0x25, 0xd2, 0x9c, 0x6b, 0x4a, 0xbd, 0xf3, 91 0x04, 0x6f, 0x98, 0xd6, 0x21, 0x94, 0x63, 0x2d, 0xda, 0xb1, 0x46, 92 0x08, 0xff, 0xde, 0x29, 0x67, 0x90, 0xfb, 0x0c, 0x42, 0xb5, 0x7f, 93 0x88, 0xc6, 0x31, 0x5a, 0xad, 0xe3, 0x14, 0x35, 0xc2, 0x8c, 0x7b, 94 0x10, 0xe7, 0xa9, 0x5e, 0xeb, 0x1c, 0x52, 0xa5, 0xce, 0x39, 0x77, 95 0x80, 0xa1, 0x56, 0x18, 0xef, 0x84, 0x73, 0x3d, 0xca, 0xfe, 0x09, 96 0x47, 0xb0, 0xdb, 0x2c, 0x62, 0x95, 0xb4, 0x43, 0x0d, 0xfa, 0x91, 97 0x66, 0x28, 0xdf, 0x6a, 0x9d, 0xd3, 0x24, 0x4f, 0xb8, 0xf6, 0x01, 98 0x20, 0xd7, 0x99, 0x6e, 0x05, 0xf2, 0xbc, 0x4b, 0x81, 0x76, 0x38, 99 0xcf, 0xa4, 0x53, 0x1d, 0xea, 0xcb, 0x3c, 0x72, 0x85, 0xee, 0x19, 100 0x57, 0xa0, 0x15, 0xe2, 0xac, 0x5b, 0x30, 0xc7, 0x89, 0x7e, 0x5f, 101 0xa8, 0xe6, 0x11, 0x7a, 0x8d, 0xc3, 0x34, 0xab, 0x5c, 0x12, 0xe5, 102 0x8e, 0x79, 0x37, 0xc0, 0xe1, 0x16, 0x58, 0xaf, 0xc4, 0x33, 0x7d, 103 0x8a, 0x3f, 0xc8, 0x86, 0x71, 0x1a, 0xed, 0xa3, 0x54, 0x75, 0x82, 104 0xcc, 0x3b, 0x50, 0xa7, 0xe9, 0x1e, 0xd4, 0x23, 0x6d, 0x9a, 0xf1, 105 0x06, 0x48, 0xbf, 0x9e, 0x69, 0x27, 0xd0, 0xbb, 0x4c, 0x02, 0xf5, 106 0x40, 0xb7, 0xf9, 0x0e, 0x65, 0x92, 0xdc, 0x2b, 0x0a, 0xfd, 0xb3, 107 0x44, 0x2f, 0xd8, 0x96, 0x61, 0x55, 0xa2, 0xec, 0x1b, 0x70, 0x87, 108 0xc9, 0x3e, 0x1f, 0xe8, 0xa6, 0x51, 0x3a, 0xcd, 0x83, 0x74, 0xc1, 109 0x36, 0x78, 0x8f, 0xe4, 0x13, 0x5d, 0xaa, 0x8b, 0x7c, 0x32, 0xc5, 110 0xae, 0x59, 0x17, 0xe0, 0x2a, 0xdd, 0x93, 0x64, 0x0f, 0xf8, 0xb6, 111 0x41, 0x60, 0x97, 0xd9, 0x2e, 0x45, 0xb2, 0xfc, 0x0b, 0xbe, 0x49, 112 0x07, 0xf0, 0x9b, 0x6c, 0x22, 0xd5, 0xf4, 0x03, 0x4d, 0xba, 0xd1, 113 0x26, 0x68, 0x9f 114 }; 115 116 /** 117 * Return a human readable name for @p type. 118 * 119 * @param type The type to query. 120 */ 121 const char * 122 bhnd_nvram_type_name(bhnd_nvram_type type) 123 { 124 switch (type) { 125 case BHND_NVRAM_TYPE_UINT8: 126 return ("uint8"); 127 case BHND_NVRAM_TYPE_UINT16: 128 return ("uint16"); 129 case BHND_NVRAM_TYPE_UINT32: 130 return ("uint32"); 131 case BHND_NVRAM_TYPE_UINT64: 132 return ("uint64"); 133 case BHND_NVRAM_TYPE_CHAR: 134 return ("char"); 135 case BHND_NVRAM_TYPE_INT8: 136 return ("int8"); 137 case BHND_NVRAM_TYPE_INT16: 138 return ("int16"); 139 case BHND_NVRAM_TYPE_INT32: 140 return ("int32"); 141 case BHND_NVRAM_TYPE_INT64: 142 return ("int64"); 143 case BHND_NVRAM_TYPE_STRING: 144 return ("string"); 145 case BHND_NVRAM_TYPE_UINT8_ARRAY: 146 return ("uint8[]"); 147 case BHND_NVRAM_TYPE_UINT16_ARRAY: 148 return ("uint16[]"); 149 case BHND_NVRAM_TYPE_UINT32_ARRAY: 150 return ("uint32[]"); 151 case BHND_NVRAM_TYPE_UINT64_ARRAY: 152 return ("uint64[]"); 153 case BHND_NVRAM_TYPE_INT8_ARRAY: 154 return ("int8[]"); 155 case BHND_NVRAM_TYPE_INT16_ARRAY: 156 return ("int16[]"); 157 case BHND_NVRAM_TYPE_INT32_ARRAY: 158 return ("int32[]"); 159 case BHND_NVRAM_TYPE_INT64_ARRAY: 160 return ("int64[]"); 161 case BHND_NVRAM_TYPE_CHAR_ARRAY: 162 return ("char[]"); 163 case BHND_NVRAM_TYPE_STRING_ARRAY: 164 return ("string[]"); 165 } 166 167 /* Quiesce gcc4.2 */ 168 BHND_NV_PANIC("bhnd nvram type %u unknown", type); 169 } 170 171 /** 172 * Return true if @p type is a signed integer type, false otherwise. 173 * 174 * Will return false for all array types. 175 * 176 * @param type The type to query. 177 */ 178 bool 179 bhnd_nvram_is_signed_type(bhnd_nvram_type type) 180 { 181 switch (type) { 182 case BHND_NVRAM_TYPE_INT8: 183 case BHND_NVRAM_TYPE_INT16: 184 case BHND_NVRAM_TYPE_INT32: 185 case BHND_NVRAM_TYPE_INT64: 186 BHND_NV_ASSERT(bhnd_nvram_is_int_type(type), ("non-int type?")); 187 return (true); 188 189 case BHND_NVRAM_TYPE_CHAR: 190 case BHND_NVRAM_TYPE_UINT8: 191 case BHND_NVRAM_TYPE_UINT16: 192 case BHND_NVRAM_TYPE_UINT32: 193 case BHND_NVRAM_TYPE_UINT64: 194 case BHND_NVRAM_TYPE_STRING: 195 case BHND_NVRAM_TYPE_UINT8_ARRAY: 196 case BHND_NVRAM_TYPE_UINT16_ARRAY: 197 case BHND_NVRAM_TYPE_UINT32_ARRAY: 198 case BHND_NVRAM_TYPE_UINT64_ARRAY: 199 case BHND_NVRAM_TYPE_INT8_ARRAY: 200 case BHND_NVRAM_TYPE_INT16_ARRAY: 201 case BHND_NVRAM_TYPE_INT32_ARRAY: 202 case BHND_NVRAM_TYPE_INT64_ARRAY: 203 case BHND_NVRAM_TYPE_CHAR_ARRAY: 204 case BHND_NVRAM_TYPE_STRING_ARRAY: 205 return (false); 206 } 207 208 /* Quiesce gcc4.2 */ 209 BHND_NV_PANIC("bhnd nvram type %u unknown", type); 210 } 211 212 /** 213 * Return true if @p type is an unsigned integer type, false otherwise. 214 * 215 * @param type The type to query. 216 * 217 * @return Will return false for all array types. 218 * @return Will return true for BHND_NVRAM_TYPE_CHAR. 219 */ 220 bool 221 bhnd_nvram_is_unsigned_type(bhnd_nvram_type type) 222 { 223 /* If an integer type, must be either signed or unsigned */ 224 if (!bhnd_nvram_is_int_type(type)) 225 return (false); 226 227 return (!bhnd_nvram_is_signed_type(type)); 228 } 229 230 /** 231 * Return true if bhnd_nvram_is_signed_type() or bhnd_nvram_is_unsigned_type() 232 * returns true for @p type. 233 * 234 * @param type The type to query. 235 */ 236 bool 237 bhnd_nvram_is_int_type(bhnd_nvram_type type) 238 { 239 switch (type) { 240 case BHND_NVRAM_TYPE_UINT8: 241 case BHND_NVRAM_TYPE_UINT16: 242 case BHND_NVRAM_TYPE_UINT32: 243 case BHND_NVRAM_TYPE_UINT64: 244 case BHND_NVRAM_TYPE_INT8: 245 case BHND_NVRAM_TYPE_INT16: 246 case BHND_NVRAM_TYPE_INT32: 247 case BHND_NVRAM_TYPE_INT64: 248 return (true); 249 250 case BHND_NVRAM_TYPE_CHAR: 251 case BHND_NVRAM_TYPE_STRING: 252 case BHND_NVRAM_TYPE_UINT8_ARRAY: 253 case BHND_NVRAM_TYPE_UINT16_ARRAY: 254 case BHND_NVRAM_TYPE_UINT32_ARRAY: 255 case BHND_NVRAM_TYPE_UINT64_ARRAY: 256 case BHND_NVRAM_TYPE_INT8_ARRAY: 257 case BHND_NVRAM_TYPE_INT16_ARRAY: 258 case BHND_NVRAM_TYPE_INT32_ARRAY: 259 case BHND_NVRAM_TYPE_INT64_ARRAY: 260 case BHND_NVRAM_TYPE_CHAR_ARRAY: 261 case BHND_NVRAM_TYPE_STRING_ARRAY: 262 return (false); 263 } 264 265 /* Quiesce gcc4.2 */ 266 BHND_NV_PANIC("bhnd nvram type %u unknown", type); 267 } 268 269 /** 270 * Return true if @p type is an array type, false otherwise. 271 * 272 * @param type The type to query. 273 */ 274 bool 275 bhnd_nvram_is_array_type(bhnd_nvram_type type) 276 { 277 switch (type) { 278 case BHND_NVRAM_TYPE_UINT8: 279 case BHND_NVRAM_TYPE_UINT16: 280 case BHND_NVRAM_TYPE_UINT32: 281 case BHND_NVRAM_TYPE_UINT64: 282 case BHND_NVRAM_TYPE_INT8: 283 case BHND_NVRAM_TYPE_INT16: 284 case BHND_NVRAM_TYPE_INT32: 285 case BHND_NVRAM_TYPE_INT64: 286 case BHND_NVRAM_TYPE_CHAR: 287 case BHND_NVRAM_TYPE_STRING: 288 return (false); 289 290 case BHND_NVRAM_TYPE_UINT8_ARRAY: 291 case BHND_NVRAM_TYPE_UINT16_ARRAY: 292 case BHND_NVRAM_TYPE_UINT32_ARRAY: 293 case BHND_NVRAM_TYPE_UINT64_ARRAY: 294 case BHND_NVRAM_TYPE_INT8_ARRAY: 295 case BHND_NVRAM_TYPE_INT16_ARRAY: 296 case BHND_NVRAM_TYPE_INT32_ARRAY: 297 case BHND_NVRAM_TYPE_INT64_ARRAY: 298 case BHND_NVRAM_TYPE_CHAR_ARRAY: 299 case BHND_NVRAM_TYPE_STRING_ARRAY: 300 return (true); 301 } 302 303 /* Quiesce gcc4.2 */ 304 BHND_NV_PANIC("bhnd nvram type %u unknown", type); 305 } 306 307 /** 308 * If @p type is an array type, return the base element type. Otherwise, 309 * returns @p type. 310 * 311 * @param type The type to query. 312 */ 313 bhnd_nvram_type 314 bhnd_nvram_base_type(bhnd_nvram_type type) 315 { 316 switch (type) { 317 case BHND_NVRAM_TYPE_UINT8: 318 case BHND_NVRAM_TYPE_UINT16: 319 case BHND_NVRAM_TYPE_UINT32: 320 case BHND_NVRAM_TYPE_UINT64: 321 case BHND_NVRAM_TYPE_INT8: 322 case BHND_NVRAM_TYPE_INT16: 323 case BHND_NVRAM_TYPE_INT32: 324 case BHND_NVRAM_TYPE_INT64: 325 case BHND_NVRAM_TYPE_CHAR: 326 case BHND_NVRAM_TYPE_STRING: 327 return (type); 328 329 case BHND_NVRAM_TYPE_UINT8_ARRAY: return (BHND_NVRAM_TYPE_UINT8); 330 case BHND_NVRAM_TYPE_UINT16_ARRAY: return (BHND_NVRAM_TYPE_UINT16); 331 case BHND_NVRAM_TYPE_UINT32_ARRAY: return (BHND_NVRAM_TYPE_UINT32); 332 case BHND_NVRAM_TYPE_UINT64_ARRAY: return (BHND_NVRAM_TYPE_UINT64); 333 case BHND_NVRAM_TYPE_INT8_ARRAY: return (BHND_NVRAM_TYPE_INT8); 334 case BHND_NVRAM_TYPE_INT16_ARRAY: return (BHND_NVRAM_TYPE_INT16); 335 case BHND_NVRAM_TYPE_INT32_ARRAY: return (BHND_NVRAM_TYPE_INT32); 336 case BHND_NVRAM_TYPE_INT64_ARRAY: return (BHND_NVRAM_TYPE_INT64); 337 case BHND_NVRAM_TYPE_CHAR_ARRAY: return (BHND_NVRAM_TYPE_CHAR); 338 case BHND_NVRAM_TYPE_STRING_ARRAY: return (BHND_NVRAM_TYPE_STRING); 339 } 340 341 /* Quiesce gcc4.2 */ 342 BHND_NV_PANIC("bhnd nvram type %u unknown", type); 343 } 344 345 /** 346 * Calculate the number of elements represented by a value of @p len bytes 347 * with @p type. 348 * 349 * @param type The value type. 350 * @param data The actual data to be queried, or NULL if unknown. 351 * @param len The length in bytes of @p data, or if @p data is NULL, 352 * the expected length in bytes. 353 * @param[out] nelem On success, the number of elements. If @p type is not 354 * a fixed width type (e.g. BHND_NVRAM_TYPE_STRING_ARRAY), 355 * and @p data is NULL, an @p nelem value of 0 will be 356 * returned. 357 * 358 * @retval 0 success 359 * @retval EFTYPE if @p type is not an array type, and @p len is not 360 * equal to the size of a single element of @p type. 361 * @retval EFAULT if @p len is not correctly aligned for elements of 362 * @p type. 363 */ 364 int 365 bhnd_nvram_value_nelem(bhnd_nvram_type type, const void *data, size_t len, 366 size_t *nelem) 367 { 368 bhnd_nvram_type base_type; 369 size_t base_size; 370 371 /* Length must be aligned to the element size */ 372 base_type = bhnd_nvram_base_type(type); 373 base_size = bhnd_nvram_value_size(base_type, NULL, 0, 1); 374 if (base_size != 0 && len % base_size != 0) 375 return (EFAULT); 376 377 switch (type) { 378 case BHND_NVRAM_TYPE_STRING: 379 case BHND_NVRAM_TYPE_STRING_ARRAY: { 380 const char *p; 381 size_t nleft; 382 383 /* Cannot determine the element count without parsing 384 * the actual data */ 385 if (data == NULL) { 386 *nelem = 0; 387 return (0); 388 } 389 390 /* Iterate over the NUL-terminated strings to calculate 391 * total element count */ 392 p = data; 393 nleft = len; 394 *nelem = 0; 395 while (nleft > 0) { 396 size_t slen; 397 398 /* Increment element count */ 399 (*nelem)++; 400 401 /* If not a string array, data must not contain more 402 * than one entry. */ 403 if (!bhnd_nvram_is_array_type(type) && *nelem > 1) 404 return (EFTYPE); 405 406 /* Determine string length */ 407 slen = strnlen(p, nleft); 408 nleft -= slen; 409 410 /* Advance input */ 411 p += slen; 412 413 /* Account for trailing NUL, if we haven't hit the end 414 * of the input */ 415 if (nleft > 0) { 416 nleft--; 417 p++; 418 } 419 } 420 421 return (0); 422 } 423 case BHND_NVRAM_TYPE_INT8: 424 case BHND_NVRAM_TYPE_UINT8: 425 case BHND_NVRAM_TYPE_CHAR: 426 case BHND_NVRAM_TYPE_INT16: 427 case BHND_NVRAM_TYPE_UINT16: 428 case BHND_NVRAM_TYPE_INT32: 429 case BHND_NVRAM_TYPE_UINT32: 430 case BHND_NVRAM_TYPE_INT64: 431 case BHND_NVRAM_TYPE_UINT64: 432 /* Length must be equal to the size of exactly one 433 * element (arrays can represent zero elements -- non-array 434 * types cannot) */ 435 if (len != base_size) 436 return (EFTYPE); 437 *nelem = 1; 438 return (0); 439 440 case BHND_NVRAM_TYPE_UINT8_ARRAY: 441 case BHND_NVRAM_TYPE_UINT16_ARRAY: 442 case BHND_NVRAM_TYPE_UINT32_ARRAY: 443 case BHND_NVRAM_TYPE_UINT64_ARRAY: 444 case BHND_NVRAM_TYPE_INT8_ARRAY: 445 case BHND_NVRAM_TYPE_INT16_ARRAY: 446 case BHND_NVRAM_TYPE_INT32_ARRAY: 447 case BHND_NVRAM_TYPE_INT64_ARRAY: 448 case BHND_NVRAM_TYPE_CHAR_ARRAY: 449 BHND_NV_ASSERT(base_size != 0, ("invalid base size")); 450 *nelem = len / base_size; 451 return (0); 452 } 453 454 /* Quiesce gcc4.2 */ 455 BHND_NV_PANIC("bhnd nvram type %u unknown", type); 456 } 457 458 /** 459 * Return the size, in bytes, of a value of @p type with @p nelem elements. 460 * 461 * @param type The value type. 462 * @param data The actual data to be queried, or NULL if unknown. If 463 * NULL and the base type is not a fixed width type 464 * (e.g. BHND_NVRAM_TYPE_STRING), 0 will be returned. 465 * @param nbytes The size of @p data, in bytes, or 0 if @p data is NULL. 466 * @param nelem The number of elements. If @p type is not an array type, 467 * this value must be 1. 468 * 469 * @retval 0 If @p type has a variable width, and @p data is NULL. 470 * @retval 0 If a @p nelem value greater than 1 is provided for a 471 * non-array @p type. 472 * @retval 0 If a @p nelem value of 0 is provided. 473 * @retval 0 If the result would exceed the maximum value 474 * representable by size_t. 475 * @retval non-zero The size, in bytes, of @p type with @p nelem elements. 476 */ 477 size_t 478 bhnd_nvram_value_size(bhnd_nvram_type type, const void *data, size_t nbytes, 479 size_t nelem) 480 { 481 /* If nelem 0, nothing to do */ 482 if (nelem == 0) 483 return (0); 484 485 /* Non-array types must have an nelem value of 1 */ 486 if (!bhnd_nvram_is_array_type(type) && nelem != 1) 487 return (0); 488 489 switch (type) { 490 case BHND_NVRAM_TYPE_UINT8_ARRAY: 491 case BHND_NVRAM_TYPE_UINT16_ARRAY: 492 case BHND_NVRAM_TYPE_UINT32_ARRAY: 493 case BHND_NVRAM_TYPE_UINT64_ARRAY: 494 case BHND_NVRAM_TYPE_INT8_ARRAY: 495 case BHND_NVRAM_TYPE_INT16_ARRAY: 496 case BHND_NVRAM_TYPE_INT32_ARRAY: 497 case BHND_NVRAM_TYPE_INT64_ARRAY: 498 case BHND_NVRAM_TYPE_CHAR_ARRAY: { 499 bhnd_nvram_type base_type; 500 size_t base_size; 501 502 base_type = bhnd_nvram_base_type(type); 503 base_size = bhnd_nvram_value_size(base_type, NULL, 0, 1); 504 505 /* Would nelem * base_size overflow? */ 506 if (SIZE_MAX / nelem < base_size) { 507 BHND_NV_LOG("cannot represent size %s * %zu\n", 508 bhnd_nvram_type_name(base_type), nelem); 509 return (0); 510 } 511 512 return (nelem * base_size); 513 } 514 515 case BHND_NVRAM_TYPE_STRING_ARRAY: { 516 const char *p; 517 size_t total_size; 518 519 if (data == NULL) 520 return (0); 521 522 /* Iterate over the NUL-terminated strings to calculate 523 * total byte length */ 524 p = data; 525 total_size = 0; 526 for (size_t i = 0; i < nelem; i++) { 527 size_t elem_size; 528 529 elem_size = strnlen(p, nbytes - total_size); 530 p += elem_size; 531 532 /* Check for (and skip) terminating NUL */ 533 if (total_size < nbytes && *p == '\0') { 534 elem_size++; 535 p++; 536 } 537 538 /* Would total_size + elem_size overflow? 539 * 540 * A memory range larger than SIZE_MAX shouldn't be, 541 * possible, but include the check for completeness */ 542 if (SIZE_MAX - total_size < elem_size) 543 return (0); 544 545 total_size += elem_size; 546 } 547 548 return (total_size); 549 } 550 551 case BHND_NVRAM_TYPE_STRING: { 552 size_t size; 553 554 if (data == NULL) 555 return (0); 556 557 /* Find length */ 558 size = strnlen(data, nbytes); 559 560 /* Is there a terminating NUL, or did we just hit the 561 * end of the string input */ 562 if (size < nbytes) 563 size++; 564 565 return (size); 566 } 567 case BHND_NVRAM_TYPE_INT8: 568 case BHND_NVRAM_TYPE_UINT8: 569 case BHND_NVRAM_TYPE_CHAR: 570 return (sizeof(uint8_t)); 571 572 case BHND_NVRAM_TYPE_INT16: 573 case BHND_NVRAM_TYPE_UINT16: 574 return (sizeof(uint16_t)); 575 576 case BHND_NVRAM_TYPE_INT32: 577 case BHND_NVRAM_TYPE_UINT32: 578 return (sizeof(uint32_t)); 579 580 case BHND_NVRAM_TYPE_UINT64: 581 case BHND_NVRAM_TYPE_INT64: 582 return (sizeof(uint64_t)); 583 } 584 585 /* Quiesce gcc4.2 */ 586 BHND_NV_PANIC("bhnd nvram type %u unknown", type); 587 } 588 589 /** 590 * Iterate over all strings in the @p inp string array. 591 * 592 * @param inp The string array to be iterated. This must be a buffer 593 * of one or more NUL-terminated strings -- 594 * @see BHND_NVRAM_TYPE_STRING_ARRAY. 595 * @param ilen The size, in bytes, of @p inp, including any 596 * terminating NUL character(s). 597 * @param prev The value previously returned by 598 * bhnd_nvram_string_array_next(), or NULL to begin 599 * iteration. 600 * 601 * @retval non-NULL A reference to the next NUL-terminated string 602 * @retval NULL If the end of the string array is reached. 603 */ 604 const char * 605 bhnd_nvram_string_array_next(const char *inp, size_t ilen, const char *prev) 606 { 607 size_t nremain, plen; 608 609 if (ilen == 0) 610 return (NULL); 611 612 if (prev == NULL) 613 return (inp); 614 615 /* Advance to next value */ 616 BHND_NV_ASSERT(prev >= inp, ("invalid prev pointer")); 617 BHND_NV_ASSERT(prev < (inp+ilen), ("invalid prev pointer")); 618 619 nremain = ilen - (size_t)(prev - inp); 620 plen = strnlen(prev, nremain); 621 nremain -= plen; 622 623 /* Only a trailing NUL remains? */ 624 if (nremain <= 1) 625 return (NULL); 626 627 return (prev + plen + 1); 628 } 629 630 /** 631 * Format a string representation of @p inp using @p fmt, with, writing the 632 * result to @p outp. 633 * 634 * Refer to bhnd_nvram_val_vprintf() for full format string documentation. 635 * 636 * @param fmt The format string. 637 * @param inp The value to be formatted. 638 * @param ilen The size of @p inp, in bytes. 639 * @param itype The type of @p inp. 640 * @param[out] outp On success, the string value will be written to 641 * this buffer. This argment may be NULL if the 642 * value is not desired. 643 * @param[in,out] olen The capacity of @p outp. On success, will be set 644 * to the actual size of the formatted string. 645 * 646 * @retval 0 success 647 * @retval EINVAL If @p fmt contains unrecognized format string 648 * specifiers. 649 * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen 650 * is too small to hold the encoded value. 651 * @retval EFTYPE If value coercion from @p inp to a string value via 652 * @p fmt is unsupported. 653 * @retval ERANGE If value coercion of @p value would overflow (or 654 * underflow) the representation defined by @p fmt. 655 */ 656 int 657 bhnd_nvram_value_printf(const char *fmt, const void *inp, size_t ilen, 658 bhnd_nvram_type itype, char *outp, size_t *olen, ...) 659 { 660 va_list ap; 661 int error; 662 663 va_start(ap, olen); 664 error = bhnd_nvram_value_vprintf(fmt, inp, ilen, itype, outp, olen, ap); 665 va_end(ap); 666 667 return (error); 668 } 669 670 /** 671 * Format a string representation of @p inp using @p fmt, with, writing the 672 * result to @p outp. 673 * 674 * Refer to bhnd_nvram_val_vprintf() for full format string documentation. 675 * 676 * @param fmt The format string. 677 * @param inp The value to be formatted. 678 * @param ilen The size of @p inp, in bytes. 679 * @param itype The type of @p inp. 680 * @param[out] outp On success, the string value will be written to 681 * this buffer. This argment may be NULL if the 682 * value is not desired. 683 * @param[in,out] olen The capacity of @p outp. On success, will be set 684 * to the actual size of the formatted string. 685 * @param ap Argument list. 686 * 687 * @retval 0 success 688 * @retval EINVAL If @p fmt contains unrecognized format string 689 * specifiers. 690 * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen 691 * is too small to hold the encoded value. 692 * @retval EFTYPE If value coercion from @p inp to a string value via 693 * @p fmt is unsupported. 694 * @retval ERANGE If value coercion of @p value would overflow (or 695 * underflow) the representation defined by @p fmt. 696 */ 697 int 698 bhnd_nvram_value_vprintf(const char *fmt, const void *inp, size_t ilen, 699 bhnd_nvram_type itype, char *outp, size_t *olen, va_list ap) 700 { 701 bhnd_nvram_val_t val; 702 int error; 703 704 /* Map input buffer as a value instance */ 705 error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype, 706 BHND_NVRAM_VAL_BORROW_DATA); 707 if (error) 708 return (error); 709 710 /* Attempt to format the value */ 711 error = bhnd_nvram_val_vprintf(&val, fmt, outp, olen, ap); 712 713 /* Clean up */ 714 bhnd_nvram_val_release(&val); 715 return (error); 716 } 717 718 /* used by bhnd_nvram_find_vardefn() */ 719 static int 720 bhnd_nvram_find_vardefn_compare(const void *key, const void *rhs) 721 { 722 const struct bhnd_nvram_vardefn *r = rhs; 723 724 return (strcmp((const char *)key, r->name)); 725 } 726 727 /** 728 * Find and return the variable definition for @p varname, if any. 729 * 730 * @param varname variable name 731 * 732 * @retval bhnd_nvram_vardefn If a valid definition for @p varname is found. 733 * @retval NULL If no definition for @p varname is found. 734 */ 735 const struct bhnd_nvram_vardefn * 736 bhnd_nvram_find_vardefn(const char *varname) 737 { 738 return (bsearch(varname, bhnd_nvram_vardefns, bhnd_nvram_num_vardefns, 739 sizeof(bhnd_nvram_vardefns[0]), bhnd_nvram_find_vardefn_compare)); 740 } 741 742 /** 743 * Return the variable ID for a variable definition. 744 * 745 * @param defn Variable definition previously returned by 746 * bhnd_nvram_find_vardefn() or bhnd_nvram_get_vardefn(). 747 */ 748 size_t 749 bhnd_nvram_get_vardefn_id(const struct bhnd_nvram_vardefn *defn) 750 { 751 BHND_NV_ASSERT( 752 defn >= bhnd_nvram_vardefns && 753 defn <= &bhnd_nvram_vardefns[bhnd_nvram_num_vardefns-1], 754 ("invalid variable definition pointer %p", defn)); 755 756 return (defn - bhnd_nvram_vardefns); 757 } 758 759 /** 760 * Return the variable definition with the given @p id, or NULL 761 * if no such variable ID is defined. 762 * 763 * @param id variable ID. 764 * 765 * @retval bhnd_nvram_vardefn If a valid definition for @p id is found. 766 * @retval NULL If no definition for @p id is found. 767 */ 768 const struct bhnd_nvram_vardefn * 769 bhnd_nvram_get_vardefn(size_t id) 770 { 771 if (id >= bhnd_nvram_num_vardefns) 772 return (NULL); 773 774 return (&bhnd_nvram_vardefns[id]); 775 } 776 777 /** 778 * Validate an NVRAM variable name. 779 * 780 * Scans for special characters (path delimiters, value delimiters, path 781 * alias prefixes), returning false if the given name cannot be used 782 * as a relative NVRAM key. 783 * 784 * @param name A relative NVRAM variable name to validate. 785 * @param name_len The length of @p name, in bytes. 786 * 787 * @retval true If @p name is a valid relative NVRAM key. 788 * @retval false If @p name should not be used as a relative NVRAM key. 789 */ 790 bool 791 bhnd_nvram_validate_name(const char *name, size_t name_len) 792 { 793 size_t limit; 794 795 limit = strnlen(name, name_len); 796 if (limit == 0) 797 return (false); 798 799 /* Disallow path alias prefixes ([0-9]+:.*) */ 800 if (limit >= 2 && bhnd_nv_isdigit(*name)) { 801 for (const char *p = name; (size_t)(p - name) < limit; p++) { 802 if (bhnd_nv_isdigit(*p)) 803 continue; 804 else if (*p == ':') 805 return (false); 806 else 807 break; 808 } 809 } 810 811 /* Scan for special characters */ 812 for (const char *p = name; (size_t)(p - name) < limit; p++) { 813 switch (*p) { 814 case '/': /* path delimiter */ 815 case '=': /* key=value delimiter */ 816 return (false); 817 818 default: 819 if (!isascii(*p) || bhnd_nv_isspace(*p)) 820 return (false); 821 } 822 } 823 824 return (true); 825 } 826 827 /** 828 * Coerce value @p inp of type @p itype to @p otype, writing the 829 * result to @p outp. 830 * 831 * @param inp The value to be coerced. 832 * @param ilen The size of @p inp, in bytes. 833 * @param itype The base data type of @p inp. 834 * @param[out] outp On success, the value will be written to this 835 * buffer. This argment may be NULL if the value 836 * is not desired. 837 * @param[in,out] olen The capacity of @p outp. On success, will be set 838 * to the actual size of the requested value. 839 * @param otype The data type to be written to @p outp. 840 * 841 * @retval 0 success 842 * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too 843 * small to hold the requested value. 844 * @retval EFTYPE If the variable data cannot be coerced to @p otype. 845 * @retval ERANGE If value coercion would overflow @p otype. 846 */ 847 int 848 bhnd_nvram_value_coerce(const void *inp, size_t ilen, bhnd_nvram_type itype, 849 void *outp, size_t *olen, bhnd_nvram_type otype) 850 { 851 bhnd_nvram_val_t val; 852 int error; 853 854 /* Wrap input buffer in a value instance */ 855 error = bhnd_nvram_val_init(&val, NULL, inp, ilen, 856 itype, BHND_NVRAM_VAL_BORROW_DATA|BHND_NVRAM_VAL_FIXED); 857 if (error) 858 return (error); 859 860 /* Try to encode as requested type */ 861 error = bhnd_nvram_val_encode(&val, outp, olen, otype); 862 863 /* Clean up and return error */ 864 bhnd_nvram_val_release(&val); 865 return (error); 866 } 867 868 /** 869 * Parses the string in the optionally NUL-terminated @p str to as an integer 870 * value of @p otype, accepting any integer format supported by the standard 871 * strtoul(). 872 * 873 * - Any leading whitespace in @p str -- as defined by the equivalent of 874 * calling isspace_l() with an ASCII locale -- will be ignored. 875 * - A @p str may be prefixed with a single optional '+' or '-' sign denoting 876 * signedness. 877 * - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a 878 * base 16 integer follows. 879 * - An octal @p str may include a '0' prefix, denoting that an octal integer 880 * follows. 881 * 882 * If a @p base of 0 is specified, the base will be determined according 883 * to the string's initial prefix, as per strtoul()'s documented behavior. 884 * 885 * When parsing a base 16 integer to a signed representation, if no explicit 886 * sign prefix is given, the string will be parsed as the raw two's complement 887 * representation of the signed integer value. 888 * 889 * @param str The string to be parsed. 890 * @param maxlen The maximum number of bytes to be read in 891 * @p str. 892 * @param base The input string's base (2-36), or 0. 893 * @param[out] nbytes On success or failure, will be set to the total 894 * number of parsed bytes. If the total number of 895 * bytes is not desired, a NULL pointer may be 896 * provided. 897 * @param[out] outp On success, the parsed integer value will be 898 * written to @p outp. This argment may be NULL if 899 * the value is not desired. 900 * @param[in,out] olen The capacity of @p outp. On success, will be set 901 * to the actual size of the requested value. 902 * @param otype The integer type to be parsed. 903 * 904 * @retval 0 success 905 * @retval EINVAL if an invalid @p base is specified. 906 * @retval EINVAL if an unsupported (or non-integer) @p otype is 907 * specified. 908 * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too 909 * small to hold the requested value. 910 * @retval EFTYPE if @p str cannot be parsed as an integer of @p base. 911 * @retval ERANGE If the integer parsed from @p str is too large to be 912 * represented as a value of @p otype. 913 */ 914 int 915 bhnd_nvram_parse_int(const char *str, size_t maxlen, u_int base, 916 size_t *nbytes, void *outp, size_t *olen, bhnd_nvram_type otype) 917 { 918 uint64_t value; 919 uint64_t carry_max, value_max; 920 uint64_t type_max; 921 size_t limit, local_nbytes; 922 size_t ndigits; 923 bool negative, sign, twos_compl; 924 925 /* Must be an integer type */ 926 if (!bhnd_nvram_is_int_type(otype)) 927 return (EINVAL); 928 929 /* Determine output byte limit */ 930 if (outp != NULL) 931 limit = *olen; 932 else 933 limit = 0; 934 935 /* We always need a byte count. If the caller provides a NULL nbytes, 936 * track our position in a stack variable */ 937 if (nbytes == NULL) 938 nbytes = &local_nbytes; 939 940 value = 0; 941 ndigits = 0; 942 *nbytes = 0; 943 negative = false; 944 sign = false; 945 946 /* Validate the specified base */ 947 if (base != 0 && !(base >= 2 && base <= 36)) 948 return (EINVAL); 949 950 /* Skip any leading whitespace */ 951 for (; *nbytes < maxlen; (*nbytes)++) { 952 if (!bhnd_nv_isspace(str[*nbytes])) 953 break; 954 } 955 956 /* Empty string? */ 957 if (*nbytes == maxlen) 958 return (EFTYPE); 959 960 /* Parse and skip sign */ 961 if (str[*nbytes] == '-') { 962 negative = true; 963 sign = true; 964 (*nbytes)++; 965 } else if (str[*nbytes] == '+') { 966 sign = true; 967 (*nbytes)++; 968 } 969 970 /* Truncated after sign character? */ 971 if (*nbytes == maxlen) 972 return (EFTYPE); 973 974 /* Identify (or validate) hex base, skipping 0x/0X prefix */ 975 if (base == 16 || base == 0) { 976 /* Check for (and skip) 0x/0X prefix */ 977 if (maxlen - *nbytes >= 2 && str[*nbytes] == '0' && 978 (str[*nbytes+1] == 'x' || str[*nbytes+1] == 'X')) 979 { 980 base = 16; 981 (*nbytes) += 2; 982 } 983 } 984 985 /* Truncated after hex prefix? */ 986 if (*nbytes == maxlen) 987 return (EFTYPE); 988 989 /* Differentiate decimal/octal by looking for a leading 0 */ 990 if (base == 0) { 991 if (str[*nbytes] == '0') { 992 base = 8; 993 } else { 994 base = 10; 995 } 996 } 997 998 /* Only enable twos-compliment signed integer parsing enabled if the 999 * input is base 16, and no explicit sign prefix was provided */ 1000 if (!sign && base == 16) 1001 twos_compl = true; 1002 else 1003 twos_compl = false; 1004 1005 /* Determine the maximum value representable by the requested type */ 1006 switch (otype) { 1007 case BHND_NVRAM_TYPE_CHAR: 1008 case BHND_NVRAM_TYPE_UINT8: 1009 type_max = (uint64_t)UINT8_MAX; 1010 break; 1011 case BHND_NVRAM_TYPE_UINT16: 1012 type_max = (uint64_t)UINT16_MAX; 1013 break; 1014 case BHND_NVRAM_TYPE_UINT32: 1015 type_max = (uint64_t)UINT32_MAX; 1016 break; 1017 case BHND_NVRAM_TYPE_UINT64: 1018 type_max = (uint64_t)UINT64_MAX; 1019 break; 1020 1021 case BHND_NVRAM_TYPE_INT8: 1022 if (twos_compl) 1023 type_max = (uint64_t)UINT8_MAX; 1024 else if (negative) 1025 type_max = -(uint64_t)INT8_MIN; 1026 else 1027 type_max = (uint64_t)INT8_MAX; 1028 break; 1029 1030 case BHND_NVRAM_TYPE_INT16: 1031 if (twos_compl) 1032 type_max = (uint64_t)UINT16_MAX; 1033 else if (negative) 1034 type_max = -(uint64_t)INT16_MIN; 1035 else 1036 type_max = (uint64_t)INT16_MAX; 1037 break; 1038 1039 case BHND_NVRAM_TYPE_INT32: 1040 if (twos_compl) 1041 type_max = (uint64_t)UINT32_MAX; 1042 else if (negative) 1043 type_max = -(uint64_t)INT32_MIN; 1044 else 1045 type_max = (uint64_t)INT32_MAX; 1046 break; 1047 1048 case BHND_NVRAM_TYPE_INT64: 1049 if (twos_compl) 1050 type_max = (uint64_t)UINT64_MAX; 1051 else if (negative) 1052 type_max = -(uint64_t)INT64_MIN; 1053 else 1054 type_max = (uint64_t)INT64_MAX; 1055 break; 1056 1057 default: 1058 BHND_NV_LOG("unsupported integer type: %d\n", otype); 1059 return (EINVAL); 1060 } 1061 1062 /* The maximum value after which an additional carry would overflow */ 1063 value_max = type_max / (uint64_t)base; 1064 1065 /* The maximum carry value given a value equal to value_max */ 1066 carry_max = type_max % (uint64_t)base; 1067 1068 /* Consume input until we hit maxlen or a non-digit character */ 1069 for (; *nbytes < maxlen; (*nbytes)++) { 1070 u_long carry; 1071 char c; 1072 1073 /* Parse carry value */ 1074 c = str[*nbytes]; 1075 if (bhnd_nv_isdigit(c)) { 1076 carry = c - '0'; 1077 } else if (bhnd_nv_isxdigit(c)) { 1078 if (bhnd_nv_isupper(c)) 1079 carry = (c - 'A') + 10; 1080 else 1081 carry = (c - 'a') + 10; 1082 } else { 1083 /* Hit first non-digit character */ 1084 break; 1085 } 1086 1087 /* If carry is outside the base, it's not a valid digit 1088 * in the current parse context; consider it a non-digit 1089 * character */ 1090 if (carry >= (uint64_t)base) 1091 break; 1092 1093 /* Increment count of parsed digits */ 1094 ndigits++; 1095 1096 if (value > value_max) { 1097 /* -Any- carry value would overflow */ 1098 return (ERANGE); 1099 } else if (value == value_max && carry > carry_max) { 1100 /* -This- carry value would overflow */ 1101 return (ERANGE); 1102 } 1103 1104 value *= (uint64_t)base; 1105 value += carry; 1106 } 1107 1108 /* If we hit a non-digit character before parsing the first digit, 1109 * we hit an empty integer string. */ 1110 if (ndigits == 0) 1111 return (EFTYPE); 1112 1113 if (negative) 1114 value = -value; 1115 1116 /* Provide (and verify) required length */ 1117 *olen = bhnd_nvram_value_size(otype, NULL, 0, 1); 1118 if (outp == NULL) 1119 return (0); 1120 else if (limit < *olen) 1121 return (ENOMEM); 1122 1123 /* Provide result */ 1124 switch (otype) { 1125 case BHND_NVRAM_TYPE_CHAR: 1126 case BHND_NVRAM_TYPE_UINT8: 1127 *(uint8_t *)outp = (uint8_t)value; 1128 break; 1129 case BHND_NVRAM_TYPE_UINT16: 1130 *(uint16_t *)outp = (uint16_t)value; 1131 break; 1132 case BHND_NVRAM_TYPE_UINT32: 1133 *(uint32_t *)outp = (uint32_t)value; 1134 break; 1135 case BHND_NVRAM_TYPE_UINT64: 1136 *(uint64_t *)outp = (uint64_t)value; 1137 break; 1138 1139 case BHND_NVRAM_TYPE_INT8: 1140 *(int8_t *)outp = (int8_t)(int64_t)value; 1141 break; 1142 case BHND_NVRAM_TYPE_INT16: 1143 *(int16_t *)outp = (int16_t)(int64_t)value; 1144 break; 1145 case BHND_NVRAM_TYPE_INT32: 1146 *(int32_t *)outp = (int32_t)(int64_t)value; 1147 break; 1148 case BHND_NVRAM_TYPE_INT64: 1149 *(int64_t *)outp = (int64_t)value; 1150 break; 1151 default: 1152 /* unreachable */ 1153 BHND_NV_PANIC("unhandled type %d\n", otype); 1154 } 1155 1156 return (0); 1157 } 1158 1159 /** 1160 * Parse a 'name=value' string. 1161 * 1162 * @param env The string to be parsed. 1163 * @param env_len The length of @p envp. 1164 * @param delim The delimiter used in @p envp. This will generally be '='. 1165 * @param[out] name If not NULL, a pointer to the name string. This argument 1166 * may be NULL. 1167 * @param[out] name_len On success, the length of the name substring. This 1168 * argument may be NULL. 1169 * @param[out] value On success, a pointer to the value substring. This argument 1170 * may be NULL. 1171 * @param[out] value_len On success, the length of the value substring. This 1172 * argument may be NULL. 1173 * 1174 * @retval 0 success 1175 * @retval EINVAL if parsing @p envp fails. 1176 */ 1177 int 1178 bhnd_nvram_parse_env(const char *env, size_t env_len, char delim, 1179 const char **name, size_t *name_len, const char **value, size_t *value_len) 1180 { 1181 const char *p; 1182 1183 /* Name */ 1184 if ((p = memchr(env, delim, env_len)) == NULL) { 1185 BHND_NV_LOG("delimiter '%c' not found in '%.*s'\n", delim, 1186 BHND_NV_PRINT_WIDTH(env_len), env); 1187 return (EINVAL); 1188 } 1189 1190 /* Name */ 1191 if (name != NULL) 1192 *name = env; 1193 if (name_len != NULL) 1194 *name_len = p - env; 1195 1196 /* Skip delim */ 1197 p++; 1198 1199 /* Value */ 1200 if (value != NULL) 1201 *value = p; 1202 if (value_len != NULL) 1203 *value_len = env_len - (p - env); 1204 1205 return (0); 1206 } 1207 1208 1209 /** 1210 * Parse a field value, returning the actual pointer to the first 1211 * non-whitespace character and the total size of the field. 1212 * 1213 * @param[in,out] inp The field string to parse. Will be updated to point 1214 * at the first non-whitespace character found. 1215 * @param ilen The length of @p inp, in bytes. 1216 * @param delim The field delimiter to search for. 1217 * 1218 * @return Returns the actual size of the field data. 1219 */ 1220 size_t 1221 bhnd_nvram_parse_field(const char **inp, size_t ilen, char delim) 1222 { 1223 const char *p, *sp; 1224 1225 /* Skip any leading whitespace */ 1226 for (sp = *inp; (size_t)(sp-*inp) < ilen && bhnd_nv_isspace(*sp); sp++) 1227 continue; 1228 1229 *inp = sp; 1230 1231 /* Find the last field character */ 1232 for (p = *inp; (size_t)(p - *inp) < ilen; p++) { 1233 if (*p == delim || *p == '\0') 1234 break; 1235 } 1236 1237 return (p - *inp); 1238 } 1239 1240 /** 1241 * Parse a field value, returning the actual pointer to the first 1242 * non-whitespace character and the total size of the field, minus 1243 * any trailing whitespace. 1244 * 1245 * @param[in,out] inp The field string to parse. Will be updated to point 1246 * at the first non-whitespace character found. 1247 * @param ilen The length of the parsed field, in bytes, excluding the 1248 * field elimiter and any trailing whitespace. 1249 * @param delim The field delimiter to search for. 1250 * 1251 * @return Returns the actual size of the field data. 1252 */ 1253 size_t 1254 bhnd_nvram_trim_field(const char **inp, size_t ilen, char delim) 1255 { 1256 const char *sp; 1257 size_t plen; 1258 1259 plen = bhnd_nvram_parse_field(inp, ilen, delim); 1260 1261 /* Trim trailing whitespace */ 1262 sp = *inp; 1263 while (plen > 0) { 1264 if (!bhnd_nv_isspace(*(sp + plen - 1))) 1265 break; 1266 1267 plen--; 1268 } 1269 1270 return (plen); 1271 } 1272