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