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 #include <sys/param.h> 32 33 #ifdef _KERNEL 34 35 #include <sys/systm.h> 36 37 #else /* !_KERNEL */ 38 39 #include <errno.h> 40 #include <string.h> 41 42 #endif /* _KERNEL */ 43 44 #include "bhnd_nvram_private.h" 45 #include "bhnd_nvram_valuevar.h" 46 47 /** 48 * Validate the alignment of a value of @p type. 49 * 50 * @param inp The value data. 51 * @param ilen The value length, in bytes. 52 * @param itype The value type. 53 * 54 * @retval 0 success 55 * @retval EFTYPE if @p type is not an array type, and @p len is not 56 * equal to the size of a single element of @p type. 57 * @retval EFAULT if @p data is not correctly aligned to the required 58 * host alignment. 59 * @retval EFAULT if @p len is not aligned to the @p type width. 60 */ 61 int 62 bhnd_nvram_value_check_aligned(const void *inp, size_t ilen, 63 bhnd_nvram_type itype) 64 { 65 size_t align, width; 66 67 /* As a special case, NULL values have no alignment, but must 68 * always have a length of zero */ 69 if (itype == BHND_NVRAM_TYPE_NULL) { 70 if (ilen != 0) 71 return (EFAULT); 72 73 return (0); 74 } 75 76 /* Check pointer alignment against the required host alignment */ 77 align = bhnd_nvram_type_host_align(itype); 78 BHND_NV_ASSERT(align != 0, ("invalid zero alignment")); 79 if ((uintptr_t)inp % align != 0) 80 return (EFAULT); 81 82 /* If type is not fixed width, nothing else to check */ 83 width = bhnd_nvram_type_width(itype); 84 if (width == 0) 85 return (0); 86 87 /* Length must be aligned to the element width */ 88 if (ilen % width != 0) 89 return (EFAULT); 90 91 /* If the type is not an array type, the length must be equal to the 92 * size of a single element of @p type. */ 93 if (!bhnd_nvram_is_array_type(itype) && ilen != width) 94 return (EFTYPE); 95 96 return (0); 97 } 98 99 /** 100 * Calculate the number of elements represented by a value of @p ilen bytes 101 * with @p itype. 102 * 103 * @param inp The value data. 104 * @param ilen The value length. 105 * @param itype The value type. 106 * @param[out] nelem On success, the number of elements. 107 * 108 * @retval 0 success 109 * @retval EINVAL if @p inp is NULL and the element count of @p itype 110 * cannot be determined without parsing the value data. 111 * @retval EFTYPE if @p itype is not an array type, and @p ilen is not 112 * equal to the size of a single element of @p itype. 113 * @retval EFAULT if @p ilen is not correctly aligned for elements of 114 * @p itype. 115 */ 116 int 117 bhnd_nvram_value_nelem(const void *inp, size_t ilen, bhnd_nvram_type itype, 118 size_t *nelem) 119 { 120 int error; 121 122 BHND_NV_ASSERT(inp != NULL, ("NULL inp")); 123 124 /* Check alignment */ 125 if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype))) 126 return (error); 127 128 switch (itype) { 129 case BHND_NVRAM_TYPE_DATA: 130 /* Always exactly one element */ 131 *nelem = 1; 132 return (0); 133 134 case BHND_NVRAM_TYPE_NULL: 135 /* Must be zero length */ 136 if (ilen != 0) 137 return (EFAULT); 138 139 /* Always exactly one element */ 140 *nelem = 1; 141 return (0); 142 143 case BHND_NVRAM_TYPE_STRING: 144 /* Always exactly one element */ 145 *nelem = 1; 146 return (0); 147 148 case BHND_NVRAM_TYPE_STRING_ARRAY: { 149 const char *p; 150 size_t nleft; 151 152 /* Iterate over the NUL-terminated strings to calculate 153 * total element count */ 154 p = inp; 155 nleft = ilen; 156 *nelem = 0; 157 while (nleft > 0) { 158 size_t slen; 159 160 /* Increment element count */ 161 (*nelem)++; 162 163 /* Determine string length */ 164 slen = strnlen(p, nleft); 165 nleft -= slen; 166 167 /* Advance input */ 168 p += slen; 169 170 /* Account for trailing NUL, if we haven't hit the end 171 * of the input */ 172 if (nleft > 0) { 173 nleft--; 174 p++; 175 } 176 } 177 178 return (0); 179 } 180 181 case BHND_NVRAM_TYPE_UINT8_ARRAY: 182 case BHND_NVRAM_TYPE_UINT16_ARRAY: 183 case BHND_NVRAM_TYPE_UINT32_ARRAY: 184 case BHND_NVRAM_TYPE_UINT64_ARRAY: 185 case BHND_NVRAM_TYPE_INT8_ARRAY: 186 case BHND_NVRAM_TYPE_INT16_ARRAY: 187 case BHND_NVRAM_TYPE_INT32_ARRAY: 188 case BHND_NVRAM_TYPE_INT64_ARRAY: 189 case BHND_NVRAM_TYPE_CHAR_ARRAY: 190 case BHND_NVRAM_TYPE_BOOL_ARRAY: { 191 size_t width = bhnd_nvram_type_width(itype); 192 BHND_NV_ASSERT(width != 0, ("invalid width")); 193 194 *nelem = ilen / width; 195 return (0); 196 } 197 198 case BHND_NVRAM_TYPE_INT8: 199 case BHND_NVRAM_TYPE_UINT8: 200 case BHND_NVRAM_TYPE_CHAR: 201 case BHND_NVRAM_TYPE_INT16: 202 case BHND_NVRAM_TYPE_UINT16: 203 case BHND_NVRAM_TYPE_INT32: 204 case BHND_NVRAM_TYPE_UINT32: 205 case BHND_NVRAM_TYPE_INT64: 206 case BHND_NVRAM_TYPE_UINT64: 207 case BHND_NVRAM_TYPE_BOOL: 208 /* Length must be equal to the size of exactly one 209 * element (arrays can represent zero elements -- non-array 210 * types cannot) */ 211 if (ilen != bhnd_nvram_type_width(itype)) 212 return (EFTYPE); 213 *nelem = 1; 214 return (0); 215 } 216 217 /* Quiesce gcc4.2 */ 218 BHND_NV_PANIC("bhnd nvram type %u unknown", itype); 219 } 220 221 /** 222 * Return the size, in bytes, of a value of @p itype with @p nelem elements. 223 * 224 * @param inp The actual data to be queried, or NULL if unknown. If 225 * NULL and the base type is not a fixed width type 226 * (e.g. BHND_NVRAM_TYPE_STRING), 0 will be returned. 227 * @param ilen The size of @p inp, in bytes, or 0 if @p inp is NULL. 228 * @param itype The value type. 229 * @param nelem The number of elements. If @p itype is not an array 230 * type, this value must be 1. 231 * 232 * @retval 0 If @p itype has a variable width, and @p inp is NULL. 233 * @retval 0 If a @p nelem value greater than 1 is provided for a 234 * non-array @p itype. 235 * @retval 0 If a @p nelem value of 0 is provided. 236 * @retval 0 If the result would exceed the maximum value 237 * representable by size_t. 238 * @retval 0 If @p itype is BHND_NVRAM_TYPE_NULL. 239 * @retval non-zero The size, in bytes, of @p itype with @p nelem elements. 240 */ 241 size_t 242 bhnd_nvram_value_size(const void *inp, size_t ilen, bhnd_nvram_type itype, 243 size_t nelem) 244 { 245 /* If nelem 0, nothing to do */ 246 if (nelem == 0) 247 return (0); 248 249 /* Non-array types must have an nelem value of 1 */ 250 if (!bhnd_nvram_is_array_type(itype) && nelem != 1) 251 return (0); 252 253 switch (itype) { 254 case BHND_NVRAM_TYPE_UINT8_ARRAY: 255 case BHND_NVRAM_TYPE_UINT16_ARRAY: 256 case BHND_NVRAM_TYPE_UINT32_ARRAY: 257 case BHND_NVRAM_TYPE_UINT64_ARRAY: 258 case BHND_NVRAM_TYPE_INT8_ARRAY: 259 case BHND_NVRAM_TYPE_INT16_ARRAY: 260 case BHND_NVRAM_TYPE_INT32_ARRAY: 261 case BHND_NVRAM_TYPE_INT64_ARRAY: 262 case BHND_NVRAM_TYPE_CHAR_ARRAY: 263 case BHND_NVRAM_TYPE_BOOL_ARRAY:{ 264 size_t width; 265 266 width = bhnd_nvram_type_width(itype); 267 268 /* Would nelem * width overflow? */ 269 if (SIZE_MAX / nelem < width) { 270 BHND_NV_LOG("cannot represent size %s[%zu]\n", 271 bhnd_nvram_type_name(bhnd_nvram_base_type(itype)), 272 nelem); 273 return (0); 274 } 275 276 return (nelem * width); 277 } 278 279 case BHND_NVRAM_TYPE_STRING_ARRAY: { 280 const char *p; 281 size_t total_size; 282 283 if (inp == NULL) 284 return (0); 285 286 /* Iterate over the NUL-terminated strings to calculate 287 * total byte length */ 288 p = inp; 289 total_size = 0; 290 for (size_t i = 0; i < nelem; i++) { 291 size_t elem_size; 292 293 elem_size = strnlen(p, ilen - total_size); 294 p += elem_size; 295 296 /* Check for (and skip) terminating NUL */ 297 if (total_size < ilen && *p == '\0') { 298 elem_size++; 299 p++; 300 } 301 302 /* Would total_size + elem_size overflow? 303 * 304 * A memory range larger than SIZE_MAX shouldn't be, 305 * possible, but include the check for completeness */ 306 if (SIZE_MAX - total_size < elem_size) 307 return (0); 308 309 total_size += elem_size; 310 } 311 312 return (total_size); 313 } 314 315 case BHND_NVRAM_TYPE_STRING: { 316 size_t size; 317 318 if (inp == NULL) 319 return (0); 320 321 /* Find length */ 322 size = strnlen(inp, ilen); 323 324 /* Is there a terminating NUL, or did we just hit the 325 * end of the string input */ 326 if (size < ilen) 327 size++; 328 329 return (size); 330 } 331 332 case BHND_NVRAM_TYPE_NULL: 333 return (0); 334 335 case BHND_NVRAM_TYPE_DATA: 336 if (inp == NULL) 337 return (0); 338 339 return (ilen); 340 341 case BHND_NVRAM_TYPE_BOOL: 342 return (sizeof(bhnd_nvram_bool_t)); 343 344 case BHND_NVRAM_TYPE_INT8: 345 case BHND_NVRAM_TYPE_UINT8: 346 case BHND_NVRAM_TYPE_CHAR: 347 return (sizeof(uint8_t)); 348 349 case BHND_NVRAM_TYPE_INT16: 350 case BHND_NVRAM_TYPE_UINT16: 351 return (sizeof(uint16_t)); 352 353 case BHND_NVRAM_TYPE_INT32: 354 case BHND_NVRAM_TYPE_UINT32: 355 return (sizeof(uint32_t)); 356 357 case BHND_NVRAM_TYPE_UINT64: 358 case BHND_NVRAM_TYPE_INT64: 359 return (sizeof(uint64_t)); 360 } 361 362 /* Quiesce gcc4.2 */ 363 BHND_NV_PANIC("bhnd nvram type %u unknown", itype); 364 } 365 366 /** 367 * Format a string representation of @p inp using @p fmt, with, writing the 368 * result to @p outp. 369 * 370 * Refer to bhnd_nvram_val_vprintf() for full format string documentation. 371 * 372 * @param fmt The format string. 373 * @param inp The value to be formatted. 374 * @param ilen The size of @p inp, in bytes. 375 * @param itype The type of @p inp. 376 * @param[out] outp On success, the string value will be written to 377 * this buffer. This argment may be NULL if the 378 * value is not desired. 379 * @param[in,out] olen The capacity of @p outp. On success, will be set 380 * to the actual size of the formatted string. 381 * 382 * @retval 0 success 383 * @retval EINVAL If @p fmt contains unrecognized format string 384 * specifiers. 385 * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen 386 * is too small to hold the encoded value. 387 * @retval EFTYPE If value coercion from @p inp to a string value via 388 * @p fmt is unsupported. 389 * @retval ERANGE If value coercion of @p value would overflow (or 390 * underflow) the representation defined by @p fmt. 391 */ 392 int 393 bhnd_nvram_value_printf(const char *fmt, const void *inp, size_t ilen, 394 bhnd_nvram_type itype, char *outp, size_t *olen, ...) 395 { 396 va_list ap; 397 int error; 398 399 va_start(ap, olen); 400 error = bhnd_nvram_value_vprintf(fmt, inp, ilen, itype, outp, olen, ap); 401 va_end(ap); 402 403 return (error); 404 } 405 406 /** 407 * Format a string representation of @p inp using @p fmt, with, writing the 408 * result to @p outp. 409 * 410 * Refer to bhnd_nvram_val_vprintf() for full format string documentation. 411 * 412 * @param fmt The format string. 413 * @param inp The value to be formatted. 414 * @param ilen The size of @p inp, in bytes. 415 * @param itype The type of @p inp. 416 * @param[out] outp On success, the string value will be written to 417 * this buffer. This argment may be NULL if the 418 * value is not desired. 419 * @param[in,out] olen The capacity of @p outp. On success, will be set 420 * to the actual size of the formatted string. 421 * @param ap Argument list. 422 * 423 * @retval 0 success 424 * @retval EINVAL If @p fmt contains unrecognized format string 425 * specifiers. 426 * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen 427 * is too small to hold the encoded value. 428 * @retval EFTYPE If value coercion from @p inp to a string value via 429 * @p fmt is unsupported. 430 * @retval ERANGE If value coercion of @p value would overflow (or 431 * underflow) the representation defined by @p fmt. 432 */ 433 int 434 bhnd_nvram_value_vprintf(const char *fmt, const void *inp, size_t ilen, 435 bhnd_nvram_type itype, char *outp, size_t *olen, va_list ap) 436 { 437 bhnd_nvram_val val; 438 int error; 439 440 /* Map input buffer as a value instance */ 441 error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype, 442 BHND_NVRAM_VAL_BORROW_DATA); 443 if (error) 444 return (error); 445 446 /* Attempt to format the value */ 447 error = bhnd_nvram_val_vprintf(&val, fmt, outp, olen, ap); 448 449 /* Clean up */ 450 bhnd_nvram_val_release(&val); 451 return (error); 452 } 453 454 /** 455 * Iterate over all elements in @p inp. 456 * 457 * @param inp The value to be iterated. 458 * @param ilen The size, in bytes, of @p inp. 459 * @param itype The data type of @p inp. 460 * @param prev The value previously returned by 461 * bhnd_nvram_value_array_next(), or NULL to begin 462 * iteration. 463 * @param[in,out] olen If @p prev is non-NULL, @p olen must be a 464 * pointer to the length previously returned by 465 * bhnd_nvram_value_array_next(). On success, will 466 * be set to the next element's length, in bytes. 467 * 468 * @retval non-NULL A borrowed reference to the next element of @p inp. 469 * @retval NULL If the end of the array is reached. 470 */ 471 const void * 472 bhnd_nvram_value_array_next(const void *inp, size_t ilen, bhnd_nvram_type itype, 473 const void *prev, size_t *olen) 474 { 475 const u_char *next; 476 size_t offset; 477 478 /* Handle first element */ 479 if (prev == NULL) { 480 /* Zero-length array? */ 481 if (ilen == 0) 482 return (NULL); 483 484 *olen = bhnd_nvram_value_size(inp, ilen, itype, 1); 485 return (inp); 486 } 487 488 /* Advance to next element */ 489 BHND_NV_ASSERT(prev >= (const void *)inp, ("invalid cookiep")); 490 next = (const u_char *)prev + *olen; 491 offset = (size_t)(next - (const u_char *)inp); 492 493 if (offset >= ilen) { 494 /* Hit end of the array */ 495 return (NULL); 496 } 497 498 /* Determine element size */ 499 *olen = bhnd_nvram_value_size(next, ilen - offset, itype, 1); 500 if (ilen - offset < *olen) { 501 BHND_NV_LOG("short element of type %s -- misaligned " 502 "representation", bhnd_nvram_type_name(itype)); 503 return (NULL); 504 } 505 506 return (next); 507 } 508 509 /** 510 * Coerce value @p inp of type @p itype to @p otype, writing the 511 * result to @p outp. 512 * 513 * @param inp The value to be coerced. 514 * @param ilen The size of @p inp, in bytes. 515 * @param itype The base data type of @p inp. 516 * @param[out] outp On success, the value will be written to this 517 * buffer. This argment may be NULL if the value 518 * is not desired. 519 * @param[in,out] olen The capacity of @p outp. On success, will be set 520 * to the actual size of the requested value. 521 * @param otype The data type to be written to @p outp. 522 * 523 * @retval 0 success 524 * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too 525 * small to hold the requested value. 526 * @retval EFTYPE If the variable data cannot be coerced to @p otype. 527 * @retval ERANGE If value coercion would overflow @p otype. 528 */ 529 int 530 bhnd_nvram_value_coerce(const void *inp, size_t ilen, bhnd_nvram_type itype, 531 void *outp, size_t *olen, bhnd_nvram_type otype) 532 { 533 bhnd_nvram_val val; 534 int error; 535 536 /* Wrap input buffer in a value instance */ 537 error = bhnd_nvram_val_init(&val, NULL, inp, ilen, 538 itype, BHND_NVRAM_VAL_BORROW_DATA|BHND_NVRAM_VAL_FIXED); 539 if (error) 540 return (error); 541 542 /* Try to encode as requested type */ 543 error = bhnd_nvram_val_encode(&val, outp, olen, otype); 544 545 /* Clean up and return error */ 546 bhnd_nvram_val_release(&val); 547 return (error); 548 } 549