xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_plist.c (revision 5e3190f700637fcfc1a52daeaa4a031fdd2557c7)
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 #include <sys/hash.h>
33 
34 #ifdef _KERNEL
35 
36 #include <sys/systm.h>
37 
38 #else /* !_KERNEL */
39 
40 #include <errno.h>
41 #include <stdint.h>
42 #include <stdlib.h>
43 #include <string.h>
44 
45 #endif /* _KERNEL */
46 
47 #include "bhnd_nvram_plistvar.h"
48 #include "bhnd_nvram_private.h"
49 
50 static bhnd_nvram_plist_entry	*bhnd_nvram_plist_get_entry(
51 				     bhnd_nvram_plist *plist, const char *name);
52 
53 /**
54  * Allocate and initialize a new, empty property list.
55  *
56  * The caller is responsible for releasing the returned property value
57  * via bhnd_nvram_plist_release().
58  *
59  * @retval non-NULL	success
60  * @retval NULL		if allocation fails.
61  */
62 bhnd_nvram_plist *
63 bhnd_nvram_plist_new(void)
64 {
65 	bhnd_nvram_plist *plist;
66 
67 	plist = bhnd_nv_calloc(1, sizeof(*plist));
68 	if (plist == NULL)
69 		return NULL;
70 
71 	/* Implicit caller-owned reference */
72 	plist->refs = 1;
73 
74 	/* Initialize entry list */
75 	plist->num_entries = 0;
76 	TAILQ_INIT(&plist->entries);
77 
78 	/* Initialize entry hash table */
79 	for (size_t i = 0; i < nitems(plist->names); i++)
80 		LIST_INIT(&plist->names[i]);
81 
82 	return (plist);
83 }
84 
85 /**
86  * Retain a reference and return @p plist to the caller.
87  *
88  * The caller is responsible for releasing their reference ownership via
89  * bhnd_nvram_plist_release().
90  *
91  * @param	plist	The property list to be retained.
92  */
93 bhnd_nvram_plist *
94 bhnd_nvram_plist_retain(bhnd_nvram_plist *plist)
95 {
96 	BHND_NV_ASSERT(plist->refs >= 1, ("plist over-released"));
97 
98 	refcount_acquire(&plist->refs);
99 	return (plist);
100 }
101 
102 /**
103  * Release a reference to @p plist.
104  *
105  * If this is the last reference, all associated resources will be freed.
106  *
107  * @param	plist	The property list to be released.
108  */
109 void
110 bhnd_nvram_plist_release(bhnd_nvram_plist *plist)
111 {
112 	bhnd_nvram_plist_entry *ple, *ple_next;
113 
114 	BHND_NV_ASSERT(plist->refs >= 1, ("plist over-released"));
115 
116 	/* Drop reference */
117 	if (!refcount_release(&plist->refs))
118 		return;
119 
120 	/* Free all property entries */
121 	TAILQ_FOREACH_SAFE(ple, &plist->entries, pl_link, ple_next) {
122 		bhnd_nvram_prop_release(ple->prop);
123 		bhnd_nv_free(ple);
124 	}
125 
126 	/* Free plist instance */
127 	bhnd_nv_free(plist);
128 }
129 
130 /**
131  * Return a shallow copy of @p plist.
132  *
133  * The caller is responsible for releasing the returned property value
134  * via bhnd_nvram_plist_release().
135  *
136  * @retval non-NULL	success
137  * @retval NULL		if allocation fails.
138  */
139 bhnd_nvram_plist *
140 bhnd_nvram_plist_copy(bhnd_nvram_plist *plist)
141 {
142 	bhnd_nvram_plist	*copy;
143 	bhnd_nvram_prop		*prop;
144 	int			 error;
145 
146 	/* Allocate new, empty plist */
147 	if ((copy = bhnd_nvram_plist_new()) == NULL)
148 		return (NULL);
149 
150 	/* Append all properties */
151 	prop = NULL;
152 	while ((prop = bhnd_nvram_plist_next(plist, prop)) != NULL) {
153 		error = bhnd_nvram_plist_append(copy, prop);
154 		if (error) {
155 			if (error != ENOMEM) {
156 				BHND_NV_LOG("error copying property: %d\n",
157 				    error);
158 			}
159 
160 			bhnd_nvram_plist_release(copy);
161 			return (NULL);
162 		}
163 	}
164 
165 	/* Return ownership of the copy to our caller */
166 	return (copy);
167 }
168 
169 /**
170  * Return the number of properties in @p plist.
171  */
172 size_t
173 bhnd_nvram_plist_count(bhnd_nvram_plist *plist)
174 {
175 	return (plist->num_entries);
176 }
177 
178 /**
179  * Return true if @p plist contains a property name @p name, false otherwise.
180  *
181  * @param	plist	The property list to be queried.
182  * @param	name	The property name to be queried.
183  */
184 bool
185 bhnd_nvram_plist_contains(bhnd_nvram_plist *plist, const char *name)
186 {
187 	if (bhnd_nvram_plist_get_entry(plist, name) != NULL)
188 		return (true);
189 
190 	return (false);
191 }
192 
193 /**
194  * Replace the current property value for a property matching the name
195  * of @p prop, maintaining the property's current order in @p plist.
196  *
197  * If a matching property is not found in @p plist, @p prop will instead be
198  * appended.
199  *
200  * @param	plist	The property list to be modified.
201  * @param	prop	The replacement property.
202  *
203  * @retval 0		success
204  * @retval ENOMEM	if allocation fails.
205  * @retval non-zero	if modifying @p plist otherwise fails, a regular unix
206  *			error code will be returned.
207  */
208 int
209 bhnd_nvram_plist_replace(bhnd_nvram_plist *plist, bhnd_nvram_prop *prop)
210 {
211 	bhnd_nvram_plist_entry	*entry;
212 
213 	/* Fetch current entry */
214 	entry = bhnd_nvram_plist_get_entry(plist, prop->name);
215 	if (entry == NULL) {
216 		/* Not found -- append property instead */
217 		return (bhnd_nvram_plist_append(plist, prop));
218 	}
219 
220 	/* Replace the current entry's property reference */
221 	bhnd_nvram_prop_release(entry->prop);
222 	entry->prop = bhnd_nvram_prop_retain(prop);
223 
224 	return (0);
225 }
226 
227 /**
228  * Replace the current property value for a property matching @p name,
229  * maintaining the property's order in @p plist.
230  *
231  * If @p name is not found in @p plist, a new property will be appended.
232  *
233  * @param	plist	The property list to be modified.
234  * @param	name	The name of the property to be replaced.
235  * @param	val	The replacement value for @p name.
236  *
237  * @retval 0		success
238  * @retval ENOMEM	if allocation fails.
239  * @retval non-zero	if modifying @p plist otherwise fails, a regular unix
240  *			error code will be returned.
241  */
242 int
243 bhnd_nvram_plist_replace_val(bhnd_nvram_plist *plist, const char *name,
244     bhnd_nvram_val *val)
245 {
246 	bhnd_nvram_prop		*prop;
247 	int			 error;
248 
249 	/* Construct a new property instance for the name and value */
250 	if ((prop = bhnd_nvram_prop_new(name, val)) == NULL)
251 		return (ENOMEM);
252 
253 	/* Attempt replace */
254 	error = bhnd_nvram_plist_replace(plist, prop);
255 	bhnd_nvram_prop_release(prop);
256 
257 	return (error);
258 }
259 
260 /**
261  * Replace the current property value for a property matching @p name, copying
262  * the new property value from the given @p inp buffer of @p itype and @p ilen.
263  *
264  * The current property order of @p name in @p plist will be maintained.
265  *
266  * If @p name is not found in @p plist, a new property will be appended.
267  *
268  * @param	plist	The property list to be modified.
269  * @param	name	The name of the property to be replaced.
270  * @param	inp	Input buffer.
271  * @param	ilen	Input buffer length.
272  * @param	itype	Input buffer type.
273  *
274  * @retval 0		success
275  * @retval ENOMEM	if allocation fails.
276  * @retval non-zero	if modifying @p plist otherwise fails, a regular unix
277  *			error code will be returned.
278  */
279 int
280 bhnd_nvram_plist_replace_bytes(bhnd_nvram_plist *plist, const char *name,
281     const void *inp, size_t ilen, bhnd_nvram_type itype)
282 {
283 	bhnd_nvram_prop	*prop;
284 	int		 error;
285 
286 	if ((prop = bhnd_nvram_prop_bytes_new(name, inp, ilen, itype)) == NULL)
287 		return (ENOMEM);
288 
289 	error = bhnd_nvram_plist_replace(plist, prop);
290 	bhnd_nvram_prop_release(prop);
291 
292 	return (error);
293 }
294 
295 /**
296  * Replace the current property value for a property matching @p name, copying
297  * the new property value from @p val.
298  *
299  * The current property order of @p name in @p plist will be maintained.
300  *
301  * If @p name is not found in @p plist, a new property will be appended.
302  *
303  * @param	plist	The property list to be modified.
304  * @param	name	The name of the property to be replaced.
305  * @param	val	The property's replacement string value.
306  *
307  * @retval 0		success
308  * @retval ENOMEM	if allocation fails.
309  * @retval non-zero	if modifying @p plist otherwise fails, a regular unix
310  *			error code will be returned.
311  */
312 int
313 bhnd_nvram_plist_replace_string(bhnd_nvram_plist *plist, const char *name,
314     const char *val)
315 {
316 	return (bhnd_nvram_plist_replace_bytes(plist, name, val, strlen(val)+1,
317 	    BHND_NVRAM_TYPE_STRING));
318 }
319 
320 /**
321  * Remove the property entry for the property @p name, if any.
322  *
323  * @param	plist	The property list to be modified.
324  * @param	name	The name of the property to be removed.
325  */
326 void
327 bhnd_nvram_plist_remove(bhnd_nvram_plist *plist, const char *name)
328 {
329 	bhnd_nvram_plist_entry *entry;
330 
331 	/* Fetch entry */
332 	entry = bhnd_nvram_plist_get_entry(plist, name);
333 	if (entry == NULL)
334 		return;
335 
336 	/* Remove from entry list and hash table */
337 	TAILQ_REMOVE(&plist->entries, entry, pl_link);
338 	LIST_REMOVE(entry, pl_hash_link);
339 
340 	/* Free plist entry */
341 	bhnd_nvram_prop_release(entry->prop);
342 	bhnd_nv_free(entry);
343 
344 	/* Decrement entry count */
345 	BHND_NV_ASSERT(plist->num_entries > 0, ("entry count over-release"));
346 	plist->num_entries--;
347 }
348 
349 /**
350  * Fetch the property list entry for @p name, if any.
351  *
352  * @param	plist	The property list to be queried.
353  * @param	name	The property name to be queried.
354  *
355  * @retval non-NULL	if @p name is found.
356  * @retval NULL		if @p name is not found.
357  */
358 static bhnd_nvram_plist_entry *
359 bhnd_nvram_plist_get_entry(bhnd_nvram_plist *plist, const char *name)
360 {
361 	bhnd_nvram_plist_entry_list	*hash_list;
362 	bhnd_nvram_plist_entry		*entry;
363 	uint32_t			 h;
364 
365 	h = hash32_str(name, HASHINIT);
366 	hash_list = &plist->names[h % nitems(plist->names)];
367 
368 	LIST_FOREACH(entry, hash_list, pl_hash_link) {
369 		if (strcmp(entry->prop->name, name) == 0)
370 			return (entry);
371 	};
372 
373 	/* Not found */
374 	return (NULL);
375 }
376 
377 /**
378  * Append all properties from @p tail to @p plist.
379   *
380  * @param	plist	The property list to be modified.
381  * @param	tail	The property list to append.
382  *
383  * @retval 0		success
384  * @retval ENOMEM	if allocation fails.
385  * @retval EEXIST	an existing property from @p tail was found in @p plist.
386  */
387 int
388 bhnd_nvram_plist_append_list(bhnd_nvram_plist *plist, bhnd_nvram_plist *tail)
389 {
390 	bhnd_nvram_prop	*p;
391 	int		 error;
392 
393 	p = NULL;
394 	while ((p = bhnd_nvram_plist_next(tail, p)) != NULL) {
395 		if ((error = bhnd_nvram_plist_append(plist, p)))
396 			return (error);
397 	}
398 
399 	return (0);
400 }
401 
402 /**
403  * Append @p prop to @p plist.
404  *
405  * @param	plist	The property list to be modified.
406  * @param	prop	The property to append.
407  *
408  * @retval 0		success
409  * @retval ENOMEM	if allocation fails.
410  * @retval EEXIST	an existing property with @p name was found in @p plist.
411  */
412 int
413 bhnd_nvram_plist_append(bhnd_nvram_plist *plist, bhnd_nvram_prop *prop)
414 {
415 	bhnd_nvram_plist_entry_list	*hash_list;
416 	bhnd_nvram_plist_entry 		*entry;
417 	uint32_t			 h;
418 
419 	if (bhnd_nvram_plist_contains(plist, prop->name))
420 		return (EEXIST);
421 
422 	/* Have we hit the maximum representable entry count? */
423 	if (plist->num_entries == SIZE_MAX)
424 		return (ENOMEM);
425 
426 	/* Allocate new entry */
427 	entry = bhnd_nv_malloc(sizeof(*entry));
428 	if (entry == NULL)
429 		return (ENOMEM);
430 
431 	entry->prop = bhnd_nvram_prop_retain(prop);
432 
433 	/* Append to entry list */
434 	TAILQ_INSERT_TAIL(&plist->entries, entry, pl_link);
435 
436 	/* Add to name-based hash table */
437 	h = hash32_str(prop->name, HASHINIT);
438 	hash_list = &plist->names[h % nitems(plist->names)];
439 	LIST_INSERT_HEAD(hash_list, entry, pl_hash_link);
440 
441 	/* Increment entry count */
442 	plist->num_entries++;
443 
444 	return (0);
445 }
446 
447 /**
448  * Append a new property to @p plist with @p name and @p val.
449  *
450  * @param	plist	The property list to be modified.
451  * @param	name	The name of the property to be appended.
452  * @param	val	The value of the property to be appended.
453  *
454  * @retval 0		success
455  * @retval ENOMEM	if allocation fails.
456  * @retval EEXIST	an existing property with @p name was found in @p plist.
457  */
458 int
459 bhnd_nvram_plist_append_val(bhnd_nvram_plist *plist, const char *name,
460     bhnd_nvram_val *val)
461 {
462 	bhnd_nvram_prop	*prop;
463 	int		 error;
464 
465 	if ((prop = bhnd_nvram_prop_new(name, val)) == NULL)
466 		return (ENOMEM);
467 
468 	error = bhnd_nvram_plist_append(plist, prop);
469 	bhnd_nvram_prop_release(prop);
470 
471 	return (error);
472 }
473 
474 /**
475  * Append a new property to @p plist, copying the property value from the
476  * given @p inp buffer of @p itype and @p ilen.
477  *
478  * @param	plist	The property list to be modified.
479  * @param	name	The name of the property to be appended.
480  * @param	inp	Input buffer.
481  * @param	ilen	Input buffer length.
482  * @param	itype	Input buffer type.
483  *
484  * @retval 0		success
485  * @retval ENOMEM	if allocation fails.
486  * @retval EEXIST	an existing property with @p name was found in @p plist.
487  */
488 int
489 bhnd_nvram_plist_append_bytes(bhnd_nvram_plist *plist, const char *name,
490     const void *inp, size_t ilen, bhnd_nvram_type itype)
491 {
492 	bhnd_nvram_prop	*prop;
493 	int		 error;
494 
495 	if ((prop = bhnd_nvram_prop_bytes_new(name, inp, ilen, itype)) == NULL)
496 		return (ENOMEM);
497 
498 	error = bhnd_nvram_plist_append(plist, prop);
499 	bhnd_nvram_prop_release(prop);
500 
501 	return (error);
502 }
503 
504 /**
505  * Append a new string property to @p plist, copying the property value from
506  * @p val.
507  *
508  * @param	plist	The property list to be modified.
509  * @param	name	The name of the property to be appended.
510  * @param	val	The new property's string value.
511  *
512  * @retval 0		success
513  * @retval ENOMEM	if allocation fails.
514  * @retval EEXIST	an existing property with @p name was found in @p plist.
515  */
516 int
517 bhnd_nvram_plist_append_string(bhnd_nvram_plist *plist, const char *name,
518     const char *val)
519 {
520 	return (bhnd_nvram_plist_append_bytes(plist, name, val, strlen(val)+1,
521 	    BHND_NVRAM_TYPE_STRING));
522 }
523 
524 /**
525  * Iterate over all properties in @p plist.
526  *
527  * @param	plist	The property list to be iterated.
528  * @param	prop	A property in @p plist, or NULL to return the first
529  *			property in @p plist.
530  *
531  * @retval non-NULL	A borrowed reference to the next property in @p plist.
532  * @retval NULL		If the end of the property list is reached or @p prop
533  *			is not found in @p plist.
534  */
535 bhnd_nvram_prop *
536 bhnd_nvram_plist_next(bhnd_nvram_plist *plist, bhnd_nvram_prop *prop)
537 {
538 	bhnd_nvram_plist_entry *entry;
539 
540 	if (prop == NULL) {
541 		if ((entry = TAILQ_FIRST(&plist->entries)) == NULL)
542 			return (NULL);
543 
544 		return (entry->prop);
545 	}
546 
547 	/* Look up previous property entry by name */
548 	if ((entry = bhnd_nvram_plist_get_entry(plist, prop->name)) == NULL)
549 		return (NULL);
550 
551 	/* The property instance must be identical */
552 	if (entry->prop != prop)
553 		return (NULL);
554 
555 	/* Fetch next entry */
556 	if ((entry = TAILQ_NEXT(entry, pl_link)) == NULL)
557 		return (NULL);
558 
559 	return (entry->prop);
560 }
561 
562 /**
563  * Return a borrowed reference to a named property, or NULL if @p name is
564  * not found in @p plist.
565  *
566  * @param	plist	The property list to be queried.
567  * @param	name	The name of the property to be returned.
568  *
569  * @retval non-NULL	if @p name is found.
570  * @retval NULL		if @p name is not found.
571  */
572 bhnd_nvram_prop *
573 bhnd_nvram_plist_get_prop(bhnd_nvram_plist *plist, const char *name)
574 {
575 	bhnd_nvram_plist_entry *entry;
576 
577 	if ((entry = bhnd_nvram_plist_get_entry(plist, name)) == NULL)
578 		return (NULL);
579 
580 	return (entry->prop);
581 }
582 
583 /**
584  * Return a borrowed reference to the named property's value, or NULL if
585  * @p name is not found in @p plist.
586  *
587  * @param	plist	The property list to be queried.
588  * @param	name	The name of the property to be returned.
589  *
590  * @retval non-NULL	if @p name is found.
591  * @retval NULL		if @p name is not found.
592  */
593 bhnd_nvram_val *
594 bhnd_nvram_plist_get_val(bhnd_nvram_plist *plist, const char *name)
595 {
596 	bhnd_nvram_prop *prop;
597 
598 	if ((prop = bhnd_nvram_plist_get_prop(plist, name)) == NULL)
599 		return (NULL);
600 
601 	return (bhnd_nvram_prop_val(prop));
602 }
603 
604 /**
605  * Attempt to encode a named property's value as @p otype, writing the result
606  * to @p outp.
607  *
608  * @param		plist	The property list to be queried.
609  * @param		name	The name of the property value to be returned.
610  * @param[out]		outp	On success, the value will be written to this
611  *				buffer. This argment may be NULL if the value is
612  *				not desired.
613  * @param[in,out]	olen	The capacity of @p outp. On success, will be set
614  *				to the actual size of the requested value.
615  * @param		otype	The data type to be written to @p outp.
616  *
617  * @retval 0		success
618  * @retval ENOENT	If @p name is not found in @p plist.
619  * @retval ENOMEM	If the @p outp is non-NULL, and the provided @p olen
620  *			is too small to hold the encoded value.
621  * @retval EFTYPE	If value coercion from @p prop to @p otype is
622  *			impossible.
623  * @retval ERANGE	If value coercion would overflow (or underflow) the
624  *			a @p otype representation.
625  */
626 int
627 bhnd_nvram_plist_get_encoded(bhnd_nvram_plist *plist, const char *name,
628     void *outp, size_t olen, bhnd_nvram_type otype)
629 {
630 	bhnd_nvram_prop *prop;
631 
632 	if ((prop = bhnd_nvram_plist_get_prop(plist, name)) == NULL)
633 		return (ENOENT);
634 
635 	return (bhnd_nvram_prop_encode(prop, outp, &olen, otype));
636 }
637 
638 /**
639  * Return the character representation of a named property's value.
640  *
641  * @param	plist	The property list to be queried.
642  * @param	name	The name of the property value to be returned.
643  * @param[out]	val	On success, the character value of @p name.
644  *
645  * @retval 0		success
646  * @retval ENOENT	If @p name is not found in @p plist.
647  * @retval EFTYPE	If coercion of the property's value to @p val.
648  * @retval ERANGE	If coercion of the property's value would overflow
649  *			(or underflow) @p val.
650  */
651 int
652 bhnd_nvram_plist_get_char(bhnd_nvram_plist *plist, const char *name,
653     u_char *val)
654 {
655 	return (bhnd_nvram_plist_get_encoded(plist, name, val, sizeof(*val),
656 	    BHND_NVRAM_TYPE_CHAR));
657 }
658 
659 /**
660  * Return the uint8 representation of a named property's value.
661  *
662  * @param	plist	The property list to be queried.
663  * @param	name	The name of the property value to be returned.
664  * @param[out]	val	On success, the uint8 value of @p name.
665  *
666  * @retval 0		success
667  * @retval ENOENT	If @p name is not found in @p plist.
668  * @retval EFTYPE	If coercion of the property's value to @p val.
669  * @retval ERANGE	If coercion of the property's value would overflow
670  *			(or underflow) @p val.
671  */
672 int
673 bhnd_nvram_plist_get_uint8(bhnd_nvram_plist *plist, const char *name,
674     uint8_t *val)
675 {
676 	return (bhnd_nvram_plist_get_encoded(plist, name, val, sizeof(*val),
677 	    BHND_NVRAM_TYPE_UINT8));
678 }
679 
680 /**
681  * Return the uint16 representation of a named property's value.
682  *
683  * @param	plist	The property list to be queried.
684  * @param	name	The name of the property value to be returned.
685  * @param[out]	val	On success, the uint16 value of @p name.
686  *
687  * @retval 0		success
688  * @retval ENOENT	If @p name is not found in @p plist.
689  * @retval EFTYPE	If coercion of the property's value to @p val.
690  * @retval ERANGE	If coercion of the property's value would overflow
691  *			(or underflow) @p val.
692  */
693 int
694 bhnd_nvram_plist_get_uint16(bhnd_nvram_plist *plist, const char *name,
695     uint16_t *val)
696 {
697 	return (bhnd_nvram_plist_get_encoded(plist, name, val, sizeof(*val),
698 	    BHND_NVRAM_TYPE_UINT16));
699 }
700 
701 /**
702  * Return the uint32 representation of a named property's value.
703  *
704  * @param	plist	The property list to be queried.
705  * @param	name	The name of the property value to be returned.
706  * @param[out]	val	On success, the uint32 value of @p name.
707  *
708  * @retval 0		success
709  * @retval ENOENT	If @p name is not found in @p plist.
710  * @retval EFTYPE	If coercion of the property's value to @p val.
711  * @retval ERANGE	If coercion of the property's value would overflow
712  *			(or underflow) @p val.
713  */
714 int
715 bhnd_nvram_plist_get_uint32(bhnd_nvram_plist *plist, const char *name,
716     uint32_t *val)
717 {
718 	return (bhnd_nvram_plist_get_encoded(plist, name, val, sizeof(*val),
719 	    BHND_NVRAM_TYPE_UINT32));
720 }
721 
722 /**
723  * Return the uint64 representation of a named property's value.
724  *
725  * @param	plist	The property list to be queried.
726  * @param	name	The name of the property value to be returned.
727  * @param[out]	val	On success, the uint64 value of @p name.
728  *
729  * @retval 0		success
730  * @retval ENOENT	If @p name is not found in @p plist.
731  * @retval EFTYPE	If coercion of the property's value to @p val.
732  * @retval ERANGE	If coercion of the property's value would overflow
733  *			(or underflow) @p val.
734  */
735 int
736 bhnd_nvram_plist_get_uint64(bhnd_nvram_plist *plist, const char *name,
737     uint64_t *val)
738 {
739 	return (bhnd_nvram_plist_get_encoded(plist, name, val, sizeof(*val),
740 	    BHND_NVRAM_TYPE_UINT64));
741 }
742 
743 /**
744  * Return the boolean representation of a named property's value.
745  *
746  * @param	plist	The property list to be queried.
747  * @param	name	The name of the property value to be returned.
748  * @param[out]	val	On success, the boolean value of @p name.
749  *
750  * @retval 0		success
751  * @retval ENOENT	If @p name is not found in @p plist.
752  * @retval EFTYPE	If coercion of the property's value to @p val.
753  * @retval ERANGE	If coercion of the property's value would overflow
754  *			(or underflow) @p val.
755  */
756 int
757 bhnd_nvram_plist_get_bool(bhnd_nvram_plist *plist, const char *name,
758     bool *val)
759 {
760 	return (bhnd_nvram_plist_get_encoded(plist, name, val, sizeof(*val),
761 	    BHND_NVRAM_TYPE_BOOL));
762 }
763 
764 /**
765  * Allocate and initialize a new property value.
766  *
767  * The caller is responsible for releasing the returned property value
768  * via bhnd_nvram_prop_release().
769  *
770  * @param	name	Property name.
771  * @param	val	Property value.
772  *
773  * @retval non-NULL	success
774  * @retval NULL		if allocation fails.
775  */
776 struct bhnd_nvram_prop *
777 bhnd_nvram_prop_new(const char *name, bhnd_nvram_val *val)
778 {
779 	struct bhnd_nvram_prop *prop;
780 
781 	prop = bhnd_nv_calloc(1, sizeof(*prop));
782 	if (prop == NULL)
783 		return NULL;
784 
785 	/* Implicit caller-owned reference */
786 	prop->refs = 1;
787 
788 	if ((prop->name = bhnd_nv_strdup(name)) == NULL)
789 		goto failed;
790 
791 	if ((prop->val = bhnd_nvram_val_copy(val)) == NULL)
792 		goto failed;
793 
794 	return (prop);
795 
796 failed:
797 	if (prop->name != NULL)
798 		bhnd_nv_free(prop->name);
799 
800 	if (prop->val != NULL)
801 		bhnd_nvram_val_release(prop->val);
802 
803 	bhnd_nv_free(prop);
804 	return (NULL);
805 }
806 
807 /**
808  * Allocate a new property value and attempt to initialize its value from
809  * the given @p inp buffer of @p itype and @p ilen.
810  *
811  * The caller is responsible for releasing the returned property value
812  * via bhnd_nvram_prop_release().
813  *
814  * @param	name	Property name.
815  * @param	inp	Input buffer.
816  * @param	ilen	Input buffer length.
817  * @param	itype	Input buffer type.
818  *
819  * @retval non-NULL	success
820  * @retval NULL		if allocation or initialization fails.
821  */
822 bhnd_nvram_prop *
823 bhnd_nvram_prop_bytes_new(const char *name, const void *inp, size_t ilen,
824     bhnd_nvram_type itype)
825 {
826 	bhnd_nvram_prop	*prop;
827 	bhnd_nvram_val	*val;
828 	int		 error;
829 
830 	/* Construct new value instance */
831 	error = bhnd_nvram_val_new(&val, NULL, inp, ilen, itype,
832 	    BHND_NVRAM_VAL_DYNAMIC);
833 	if (error) {
834 		if (error != ENOMEM) {
835 			BHND_NV_LOG("invalid input data; initialization "
836 			    "failed: %d\n", error);
837 		}
838 
839 		return (NULL);
840 	}
841 
842 	/* Delegate to default implementation */
843 	prop = bhnd_nvram_prop_new(name, val);
844 
845 	/* Clean up */
846 	bhnd_nvram_val_release(val);
847 	return (prop);
848 }
849 
850 /**
851  * Retain a reference and return @p prop to the caller.
852  *
853  * The caller is responsible for releasing their reference ownership via
854  * bhnd_nvram_prop_release().
855  *
856  * @param	prop	The property to be retained.
857  */
858 bhnd_nvram_prop *
859 bhnd_nvram_prop_retain(bhnd_nvram_prop *prop)
860 {
861 	BHND_NV_ASSERT(prop->refs >= 1, ("prop over-released"));
862 
863 	refcount_acquire(&prop->refs);
864 	return (prop);
865 }
866 
867 /**
868  * Release a reference to @p prop.
869  *
870  * If this is the last reference, all associated resources will be freed.
871  *
872  * @param	prop	The property to be released.
873  */
874 void
875 bhnd_nvram_prop_release(bhnd_nvram_prop *prop)
876 {
877 	BHND_NV_ASSERT(prop->refs >= 1, ("prop over-released"));
878 
879 	/* Drop reference */
880 	if (!refcount_release(&prop->refs))
881 		return;
882 
883 	/* Free property data */
884 	bhnd_nvram_val_release(prop->val);
885 	bhnd_nv_free(prop->name);
886 	bhnd_nv_free(prop);
887 }
888 
889 /**
890  * Return a borrowed reference to the property's name.
891  *
892  * @param	prop	The property to query.
893  */
894 const char *
895 bhnd_nvram_prop_name(bhnd_nvram_prop *prop)
896 {
897 	return (prop->name);
898 }
899 
900 /**
901  * Return a borrowed reference to the property's value.
902  *
903  * @param	prop	The property to query.
904  */
905 bhnd_nvram_val *
906 bhnd_nvram_prop_val(bhnd_nvram_prop *prop)
907 {
908 	return (prop->val);
909 }
910 
911 /**
912  * Return the property's value type.
913  *
914  * @param	prop	The property to query.
915  */
916 bhnd_nvram_type
917 bhnd_nvram_prop_type(bhnd_nvram_prop *prop)
918 {
919 	return (bhnd_nvram_val_type(prop->val));
920 }
921 
922 /**
923  * Return true if @p prop has a NULL value type (BHND_NVRAM_TYPE_NULL), false
924  * otherwise.
925  *
926  * @param      prop    The property to query.
927  */
928 bool
929 bhnd_nvram_prop_is_null(bhnd_nvram_prop *prop)
930 {
931 	return (bhnd_nvram_prop_type(prop) == BHND_NVRAM_TYPE_NULL);
932 }
933 
934 /**
935  * Return a borrowed reference to the property's internal value representation.
936  *
937  * @param	prop	The property to query.
938  * @param[out]	olen	The returned data's size, in bytes.
939  * @param[out]	otype	The returned data's type.
940  */
941 const void *
942 bhnd_nvram_prop_bytes(bhnd_nvram_prop *prop, size_t *olen,
943     bhnd_nvram_type *otype)
944 {
945 	const void *bytes;
946 
947 	bytes = bhnd_nvram_val_bytes(prop->val, olen, otype);
948 	BHND_NV_ASSERT(*otype == bhnd_nvram_prop_type(prop), ("type mismatch"));
949 
950 	return (bytes);
951 }
952 
953 /**
954  * Attempt to encode the property's value as @p otype, writing the result
955  * to @p outp.
956  *
957  * @param		prop	The property to be encoded.
958  * @param[out]		outp	On success, the value will be written to this
959  *				buffer. This argment may be NULL if the value is
960  *				not desired.
961  * @param[in,out]	olen	The capacity of @p outp. On success, will be set
962  *				to the actual size of the requested value.
963  * @param		otype	The data type to be written to @p outp.
964  *
965  * @retval 0		success
966  * @retval ENOMEM	If the @p outp is non-NULL, and the provided @p olen
967  *			is too small to hold the encoded value.
968  * @retval EFTYPE	If value coercion from @p prop to @p otype is
969  *			impossible.
970  * @retval ERANGE	If value coercion would overflow (or underflow) the
971  *			a @p otype representation.
972  */
973 int
974 bhnd_nvram_prop_encode(bhnd_nvram_prop *prop, void *outp, size_t *olen,
975     bhnd_nvram_type otype)
976 {
977 	return (bhnd_nvram_val_encode(prop->val, outp, olen, otype));
978 }
979