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