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
52static 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 */
64bhnd_nvram_plist *
65bhnd_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 */
95bhnd_nvram_plist *
96bhnd_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 */
111void
112bhnd_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 */
141bhnd_nvram_plist *
142bhnd_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 */
174size_t
175bhnd_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 */
186bool
187bhnd_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 */
210int
211bhnd_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 */
244int
245bhnd_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 */
281int
282bhnd_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 */
314int
315bhnd_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 */
328void
329bhnd_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 */
360static bhnd_nvram_plist_entry *
361bhnd_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 */
389int
390bhnd_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 */
414int
415bhnd_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 */
460int
461bhnd_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 */
490int
491bhnd_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 */
518int
519bhnd_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 */
537bhnd_nvram_prop *
538bhnd_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 */
574bhnd_nvram_prop *
575bhnd_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 */
595bhnd_nvram_val *
596bhnd_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 */
628int
629bhnd_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 */
653int
654bhnd_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 */
674int
675bhnd_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 */
695int
696bhnd_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 */
716int
717bhnd_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 */
737int
738bhnd_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 */
758int
759bhnd_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 */
778struct bhnd_nvram_prop *
779bhnd_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
798failed:
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 */
824bhnd_nvram_prop *
825bhnd_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 */
860bhnd_nvram_prop *
861bhnd_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 */
876void
877bhnd_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 */
896const char *
897bhnd_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 */
907bhnd_nvram_val *
908bhnd_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 */
918bhnd_nvram_type
919bhnd_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 */
930bool
931bhnd_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 */
943const void *
944bhnd_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 */
975int
976bhnd_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