xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_plist.c (revision fdafd315)
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 
49 static 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  */
61 bhnd_nvram_plist *
bhnd_nvram_plist_new(void)62 bhnd_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  */
92 bhnd_nvram_plist *
bhnd_nvram_plist_retain(bhnd_nvram_plist * plist)93 bhnd_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  */
108 void
bhnd_nvram_plist_release(bhnd_nvram_plist * plist)109 bhnd_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  */
138 bhnd_nvram_plist *
bhnd_nvram_plist_copy(bhnd_nvram_plist * plist)139 bhnd_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  */
171 size_t
bhnd_nvram_plist_count(bhnd_nvram_plist * plist)172 bhnd_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  */
183 bool
bhnd_nvram_plist_contains(bhnd_nvram_plist * plist,const char * name)184 bhnd_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  */
207 int
bhnd_nvram_plist_replace(bhnd_nvram_plist * plist,bhnd_nvram_prop * prop)208 bhnd_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  */
241 int
bhnd_nvram_plist_replace_val(bhnd_nvram_plist * plist,const char * name,bhnd_nvram_val * val)242 bhnd_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  */
278 int
bhnd_nvram_plist_replace_bytes(bhnd_nvram_plist * plist,const char * name,const void * inp,size_t ilen,bhnd_nvram_type itype)279 bhnd_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  */
311 int
bhnd_nvram_plist_replace_string(bhnd_nvram_plist * plist,const char * name,const char * val)312 bhnd_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  */
325 void
bhnd_nvram_plist_remove(bhnd_nvram_plist * plist,const char * name)326 bhnd_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  */
357 static bhnd_nvram_plist_entry *
bhnd_nvram_plist_get_entry(bhnd_nvram_plist * plist,const char * name)358 bhnd_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  */
386 int
bhnd_nvram_plist_append_list(bhnd_nvram_plist * plist,bhnd_nvram_plist * tail)387 bhnd_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  */
411 int
bhnd_nvram_plist_append(bhnd_nvram_plist * plist,bhnd_nvram_prop * prop)412 bhnd_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  */
457 int
bhnd_nvram_plist_append_val(bhnd_nvram_plist * plist,const char * name,bhnd_nvram_val * val)458 bhnd_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  */
487 int
bhnd_nvram_plist_append_bytes(bhnd_nvram_plist * plist,const char * name,const void * inp,size_t ilen,bhnd_nvram_type itype)488 bhnd_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  */
515 int
bhnd_nvram_plist_append_string(bhnd_nvram_plist * plist,const char * name,const char * val)516 bhnd_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  */
534 bhnd_nvram_prop *
bhnd_nvram_plist_next(bhnd_nvram_plist * plist,bhnd_nvram_prop * prop)535 bhnd_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  */
571 bhnd_nvram_prop *
bhnd_nvram_plist_get_prop(bhnd_nvram_plist * plist,const char * name)572 bhnd_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  */
592 bhnd_nvram_val *
bhnd_nvram_plist_get_val(bhnd_nvram_plist * plist,const char * name)593 bhnd_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  */
625 int
bhnd_nvram_plist_get_encoded(bhnd_nvram_plist * plist,const char * name,void * outp,size_t olen,bhnd_nvram_type otype)626 bhnd_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  */
650 int
bhnd_nvram_plist_get_char(bhnd_nvram_plist * plist,const char * name,u_char * val)651 bhnd_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  */
671 int
bhnd_nvram_plist_get_uint8(bhnd_nvram_plist * plist,const char * name,uint8_t * val)672 bhnd_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  */
692 int
bhnd_nvram_plist_get_uint16(bhnd_nvram_plist * plist,const char * name,uint16_t * val)693 bhnd_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  */
713 int
bhnd_nvram_plist_get_uint32(bhnd_nvram_plist * plist,const char * name,uint32_t * val)714 bhnd_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  */
734 int
bhnd_nvram_plist_get_uint64(bhnd_nvram_plist * plist,const char * name,uint64_t * val)735 bhnd_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  */
755 int
bhnd_nvram_plist_get_bool(bhnd_nvram_plist * plist,const char * name,bool * val)756 bhnd_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  */
775 struct bhnd_nvram_prop *
bhnd_nvram_prop_new(const char * name,bhnd_nvram_val * val)776 bhnd_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 
795 failed:
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  */
821 bhnd_nvram_prop *
bhnd_nvram_prop_bytes_new(const char * name,const void * inp,size_t ilen,bhnd_nvram_type itype)822 bhnd_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  */
857 bhnd_nvram_prop *
bhnd_nvram_prop_retain(bhnd_nvram_prop * prop)858 bhnd_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  */
873 void
bhnd_nvram_prop_release(bhnd_nvram_prop * prop)874 bhnd_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  */
893 const char *
bhnd_nvram_prop_name(bhnd_nvram_prop * prop)894 bhnd_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  */
904 bhnd_nvram_val *
bhnd_nvram_prop_val(bhnd_nvram_prop * prop)905 bhnd_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  */
915 bhnd_nvram_type
bhnd_nvram_prop_type(bhnd_nvram_prop * prop)916 bhnd_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  */
927 bool
bhnd_nvram_prop_is_null(bhnd_nvram_prop * prop)928 bhnd_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  */
940 const void *
bhnd_nvram_prop_bytes(bhnd_nvram_prop * prop,size_t * olen,bhnd_nvram_type * otype)941 bhnd_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  */
972 int
bhnd_nvram_prop_encode(bhnd_nvram_prop * prop,void * outp,size_t * olen,bhnd_nvram_type otype)973 bhnd_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