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