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