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 34 #ifdef _KERNEL 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 39 #include <machine/_inttypes.h> 40 41 #else /* !_KERNEL */ 42 43 #include <errno.h> 44 #include <stdint.h> 45 #include <stdlib.h> 46 #include <string.h> 47 48 #endif /* _KERNEL */ 49 50 #include "bhnd_nvram_private.h" 51 #include "bhnd_nvram_io.h" 52 53 #include "bhnd_nvram_datavar.h" 54 #include "bhnd_nvram_data.h" 55 56 /** 57 * Return a human-readable description for the given NVRAM data class. 58 * 59 * @param cls The NVRAM class. 60 */ 61 const char * 62 bhnd_nvram_data_class_desc(bhnd_nvram_data_class_t *cls) 63 { 64 return (cls->desc); 65 } 66 67 /** 68 * Probe to see if this NVRAM data class class supports the data mapped by the 69 * given I/O context, returning a BHND_NVRAM_DATA_PROBE probe result. 70 * 71 * @param cls The NVRAM class. 72 * @param io An I/O context mapping the NVRAM data. 73 * 74 * @retval 0 if this is the only possible NVRAM data class for @p io. 75 * @retval negative if the probe succeeds, a negative value should be returned; 76 * the class returning the highest negative value should be selected to handle 77 * NVRAM parsing. 78 * @retval ENXIO If the NVRAM format is not handled by @p cls. 79 * @retval positive if an error occurs during probing, a regular unix error 80 * code should be returned. 81 */ 82 int 83 bhnd_nvram_data_probe(bhnd_nvram_data_class_t *cls, struct bhnd_nvram_io *io) 84 { 85 return (cls->op_probe(io)); 86 } 87 88 /** 89 * Probe to see if an NVRAM data class in @p classes supports parsing 90 * of the data mapped by @p io, returning the parsed data in @p data. 91 * 92 * The caller is responsible for deallocating the returned instance via 93 * bhnd_nvram_data_release(). 94 * 95 * @param[out] data On success, the parsed NVRAM data instance. 96 * @param io An I/O context mapping the NVRAM data to be copied and parsed. 97 * @param classes An array of NVRAM data classes to be probed, or NULL to 98 * probe the default supported set. 99 * @param num_classes The number of NVRAM data classes in @p classes. 100 * 101 * @retval 0 success 102 * @retval ENXIO if no class is found capable of parsing @p io. 103 * @retval non-zero if an error otherwise occurs during allocation, 104 * initialization, or parsing of the NVRAM data, a regular unix error code 105 * will be returned. 106 */ 107 int 108 bhnd_nvram_data_probe_classes(struct bhnd_nvram_data **data, 109 struct bhnd_nvram_io *io, bhnd_nvram_data_class_t *classes[], 110 size_t num_classes) 111 { 112 bhnd_nvram_data_class_t *cls; 113 int error, prio, result; 114 115 cls = NULL; 116 prio = 0; 117 *data = NULL; 118 119 /* If class array is NULL, default to our linker set */ 120 if (classes == NULL) { 121 classes = SET_BEGIN(bhnd_nvram_data_class_set); 122 num_classes = SET_COUNT(bhnd_nvram_data_class_set); 123 } 124 125 /* Try to find the best data class capable of parsing io */ 126 for (size_t i = 0; i < num_classes; i++) { 127 bhnd_nvram_data_class_t *next_cls; 128 129 next_cls = classes[i]; 130 131 /* Try to probe */ 132 result = bhnd_nvram_data_probe(next_cls, io); 133 134 /* The parser did not match if an error was returned */ 135 if (result > 0) 136 continue; 137 138 /* Lower priority than previous match; keep 139 * searching */ 140 if (cls != NULL && result <= prio) 141 continue; 142 143 /* Drop any previously parsed data */ 144 if (*data != NULL) { 145 bhnd_nvram_data_release(*data); 146 *data = NULL; 147 } 148 149 /* If this is a 'maybe' match, attempt actual parsing to 150 * verify that this does in fact match */ 151 if (result <= BHND_NVRAM_DATA_PROBE_MAYBE) { 152 /* If parsing fails, keep searching */ 153 error = bhnd_nvram_data_new(next_cls, data, io); 154 if (error) 155 continue; 156 } 157 158 /* Record best new match */ 159 prio = result; 160 cls = next_cls; 161 162 163 /* Terminate search immediately on 164 * BHND_NVRAM_DATA_PROBE_SPECIFIC */ 165 if (result == BHND_NVRAM_DATA_PROBE_SPECIFIC) 166 break; 167 } 168 169 /* If no match, return error */ 170 if (cls == NULL) 171 return (ENXIO); 172 173 /* If the NVRAM data was not parsed above, do so now */ 174 if (*data == NULL) { 175 if ((error = bhnd_nvram_data_new(cls, data, io))) 176 return (error); 177 } 178 179 return (0); 180 } 181 182 /** 183 * Allocate and initialize a new instance of data class @p cls, copying and 184 * parsing NVRAM data from @p io. 185 * 186 * The caller is responsible for releasing the returned parser instance 187 * reference via bhnd_nvram_data_release(). 188 * 189 * @param cls If non-NULL, the data class to be allocated. If NULL, 190 * bhnd_nvram_data_probe_classes() will be used to determine the data format. 191 * @param[out] nv On success, a pointer to the newly allocated NVRAM data instance. 192 * @param io An I/O context mapping the NVRAM data to be copied and parsed. 193 * 194 * @retval 0 success 195 * @retval non-zero if an error occurs during allocation or initialization, a 196 * regular unix error code will be returned. 197 */ 198 int 199 bhnd_nvram_data_new(bhnd_nvram_data_class_t *cls, 200 struct bhnd_nvram_data **nv, struct bhnd_nvram_io *io) 201 { 202 struct bhnd_nvram_data *data; 203 int error; 204 205 /* If NULL, try to identify the appropriate class */ 206 if (cls == NULL) 207 return (bhnd_nvram_data_probe_classes(nv, io, NULL, 0)); 208 209 /* Allocate new instance */ 210 BHND_NV_ASSERT(sizeof(struct bhnd_nvram_data) <= cls->size, 211 ("instance size %zu less than minimum %zu", cls->size, 212 sizeof(struct bhnd_nvram_data))); 213 214 data = bhnd_nv_calloc(1, cls->size); 215 data->cls = cls; 216 refcount_init(&data->refs, 1); 217 218 /* Let the class handle initialization */ 219 if ((error = cls->op_new(data, io))) { 220 bhnd_nv_free(data); 221 return (error); 222 } 223 224 *nv = data; 225 return (0); 226 } 227 228 /** 229 * Retain and return a reference to the given data instance. 230 * 231 * @param nv The reference to be retained. 232 */ 233 struct bhnd_nvram_data * 234 bhnd_nvram_data_retain(struct bhnd_nvram_data *nv) 235 { 236 refcount_acquire(&nv->refs); 237 return (nv); 238 } 239 240 /** 241 * Release a reference to the given data instance. 242 * 243 * If this is the last reference, the data instance and its associated 244 * resources will be freed. 245 * 246 * @param nv The reference to be released. 247 */ 248 void 249 bhnd_nvram_data_release(struct bhnd_nvram_data *nv) 250 { 251 if (!refcount_release(&nv->refs)) 252 return; 253 254 /* Free any internal resources */ 255 nv->cls->op_free(nv); 256 257 /* Free the instance allocation */ 258 bhnd_nv_free(nv); 259 } 260 261 /** 262 * Return a pointer to @p nv's data class. 263 * 264 * @param nv The NVRAM data instance to be queried. 265 */ 266 bhnd_nvram_data_class_t * 267 bhnd_nvram_data_class(struct bhnd_nvram_data *nv) 268 { 269 return (nv->cls); 270 } 271 272 /** 273 * Return the number of variables in @p nv. 274 * 275 * @param nv The NVRAM data to be queried. 276 */ 277 size_t 278 bhnd_nvram_data_count(struct bhnd_nvram_data *nv) 279 { 280 return (nv->cls->op_count(nv)); 281 } 282 283 /** 284 * Compute the size of the serialized form of @p nv. 285 * 286 * Serialization may be performed via bhnd_nvram_data_serialize(). 287 * 288 * @param nv The NVRAM data to be queried. 289 * @param[out] len On success, will be set to the computed size. 290 * 291 * @retval 0 success 292 * @retval non-zero if computing the serialized size otherwise fails, a 293 * regular unix error code will be returned. 294 */ 295 int 296 bhnd_nvram_data_size(struct bhnd_nvram_data *nv, size_t *len) 297 { 298 return (nv->cls->op_size(nv, len)); 299 } 300 301 /** 302 * Serialize the NVRAM data to @p buf, using the NVRAM data class' native 303 * format. 304 * 305 * The resulting serialization may be reparsed with @p nv's BHND NVRAM data 306 * class. 307 * 308 * @param nv The NVRAM data to be serialized. 309 * @param[out] buf On success, the serialed NVRAM data will be 310 * written to this buffer. This argment may be 311 * NULL if the value is not desired. 312 * @param[in,out] len The capacity of @p buf. On success, will be set 313 * to the actual length of the serialized data. 314 * 315 * @retval 0 success 316 * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too 317 * small to hold the serialized data. 318 * @retval non-zero If serialization otherwise fails, a regular unix error 319 * code will be returned. 320 */ 321 int 322 bhnd_nvram_data_serialize(struct bhnd_nvram_data *nv, 323 void *buf, size_t *len) 324 { 325 return (nv->cls->op_serialize(nv, buf, len)); 326 } 327 328 /** 329 * Return the capability flags (@see BHND_NVRAM_DATA_CAP_*) for @p nv. 330 * 331 * @param nv The NVRAM data to be queried. 332 */ 333 uint32_t 334 bhnd_nvram_data_caps(struct bhnd_nvram_data *nv) 335 { 336 return (nv->cls->op_caps(nv)); 337 } 338 339 /** 340 * Iterate over @p nv, returning the names of subsequent variables. 341 * 342 * @param nv The NVRAM data to be iterated. 343 * @param[in,out] cookiep A pointer to a cookiep value previously returned 344 * by bhnd_nvram_data_next(), or a NULL value to 345 * begin iteration. 346 * 347 * @return Returns the next variable name, or NULL if there are no more 348 * variables defined in @p nv. 349 */ 350 const char * 351 bhnd_nvram_data_next(struct bhnd_nvram_data *nv, void **cookiep) 352 { 353 return (nv->cls->op_next(nv, cookiep)); 354 } 355 356 /** 357 * Search @p nv for a named variable, returning the variable's opaque reference 358 * if found, or NULL if unavailable. 359 * 360 * The BHND_NVRAM_DATA_CAP_INDEXED capability flag will be returned by 361 * bhnd_nvram_data_caps() if @p nv supports effecient name-based 362 * lookups. 363 * 364 * @param nv The NVRAM data to search. 365 * @param name The name to search for. 366 * 367 * @retval non-NULL If @p name is found, the opaque cookie value will be 368 * returned. 369 * @retval NULL If @p name is not found. 370 */ 371 void * 372 bhnd_nvram_data_find(struct bhnd_nvram_data *nv, const char *name) 373 { 374 return (nv->cls->op_find(nv, name)); 375 } 376 377 /** 378 * A generic implementation of bhnd_nvram_data_find(). 379 * 380 * This implementation will use bhnd_nvram_data_next() to perform a 381 * simple O(n) case-insensitve search for @p name. 382 */ 383 void * 384 bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv, const char *name) 385 { 386 const char *next; 387 void *cookiep; 388 389 cookiep = NULL; 390 while ((next = bhnd_nvram_data_next(nv, &cookiep))) { 391 if (strcasecmp(name, next) == 0) 392 return (cookiep); 393 } 394 395 /* Not found */ 396 return (NULL); 397 } 398 399 /** 400 * Read a variable and decode as @p type. 401 * 402 * @param nv The NVRAM data. 403 * @param cookiep An NVRAM variable cookie previously returned 404 * via bhnd_nvram_data_next() or 405 * bhnd_nvram_data_find(). 406 * @param[out] buf On success, the requested value will be written 407 * to this buffer. This argment may be NULL if 408 * the value is not desired. 409 * @param[in,out] len The capacity of @p buf. On success, will be set 410 * to the actual size of the requested value. 411 * @param type The data type to be written to @p buf. 412 * 413 * @retval 0 success 414 * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too 415 * small to hold the requested value. 416 * @retval EFTYPE If the variable data cannot be coerced to @p type. 417 * @retval ERANGE If value coercion would overflow @p type. 418 */ 419 int 420 bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, 421 size_t *len, bhnd_nvram_type type) 422 { 423 return (nv->cls->op_getvar(nv, cookiep, buf, len, type)); 424 } 425 426 /** 427 * A generic implementation of bhnd_nvram_data_getvar(). 428 * 429 * This implementation will call bhnd_nvram_data_getvar_ptr() to fetch 430 * a pointer to the variable data and perform data coercion on behalf 431 * of the caller. 432 * 433 * If a variable definition for the requested variable is available via 434 * bhnd_nvram_find_vardefn(), the definition will be used to provide 435 * formatting hints to bhnd_nvram_coerce_value(). 436 */ 437 int 438 bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, void *cookiep, 439 void *outp, size_t *olen, bhnd_nvram_type otype) 440 { 441 bhnd_nvram_val_t val; 442 const struct bhnd_nvram_vardefn *vdefn; 443 const bhnd_nvram_val_fmt_t *fmt; 444 const char *name; 445 const void *vptr; 446 bhnd_nvram_type vtype; 447 size_t vlen; 448 int error; 449 450 BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR, 451 ("instance does not advertise READ_PTR support")); 452 453 /* Fetch pointer to our variable data */ 454 vptr = bhnd_nvram_data_getvar_ptr(nv, cookiep, &vlen, &vtype); 455 if (vptr == NULL) 456 return (EINVAL); 457 458 /* Use the NVRAM string support */ 459 switch (vtype) { 460 case BHND_NVRAM_TYPE_STRING: 461 case BHND_NVRAM_TYPE_STRING_ARRAY: 462 fmt = &bhnd_nvram_val_bcm_string_fmt; 463 break; 464 default: 465 fmt = NULL; 466 } 467 468 /* Check the variable definition table for a matching entry; if 469 * it exists, use it to populate the value format. */ 470 name = bhnd_nvram_data_getvar_name(nv, cookiep); 471 vdefn = bhnd_nvram_find_vardefn(name); 472 if (vdefn != NULL) 473 fmt = vdefn->fmt; 474 475 /* Attempt value coercion */ 476 error = bhnd_nvram_val_init(&val, fmt, vptr, vlen, vtype, 477 BHND_NVRAM_VAL_BORROW_DATA); 478 if (error) 479 return (error); 480 481 error = bhnd_nvram_val_encode(&val, outp, olen, otype); 482 483 /* Clean up */ 484 bhnd_nvram_val_release(&val); 485 return (error); 486 } 487 488 /** 489 * If available and supported by the NVRAM data instance, return a reference 490 * to the internal buffer containing an entry's variable data, 491 * 492 * Note that string values may not be NUL terminated. 493 * 494 * @param nv The NVRAM data. 495 * @param cookiep An NVRAM variable cookie previously returned 496 * via bhnd_nvram_data_next() or 497 * bhnd_nvram_data_find(). 498 * @param[out] len On success, will be set to the actual size of 499 * the requested value. 500 * @param[out] type The data type of the entry data. 501 * 502 * @retval non-NULL success 503 * @retval NULL if direct data access is unsupported by @p nv, or 504 * unavailable for @p cookiep. 505 */ 506 const void * 507 bhnd_nvram_data_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, 508 size_t *len, bhnd_nvram_type *type) 509 { 510 return (nv->cls->op_getvar_ptr(nv, cookiep, len, type)); 511 } 512 513 514 /** 515 * Return the variable name associated with a given @p cookiep. 516 * @param nv The NVRAM data to be iterated. 517 * @param[in,out] cookiep A pointer to a cookiep value previously returned 518 * via bhnd_nvram_data_next() or 519 * bhnd_nvram_data_find(). 520 * 521 * @return Returns the variable's name. 522 */ 523 const char * 524 bhnd_nvram_data_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) 525 { 526 return (nv->cls->op_getvar_name(nv, cookiep)); 527 } 528