xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c (revision da5432eda807c4b7232d030d5157d5b417ea4f52)
1 /*-
2  * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  */
29 
30 #include <sys/cdefs.h>
31 #include <sys/param.h>
32 #include <sys/hash.h>
33 #include <sys/queue.h>
34 
35 #ifdef _KERNEL
36 
37 #include <sys/ctype.h>
38 #include <sys/systm.h>
39 
40 #include <machine/_inttypes.h>
41 
42 #else /* !_KERNEL */
43 
44 #include <ctype.h>
45 #include <errno.h>
46 #include <inttypes.h>
47 #include <stdbool.h>
48 #include <stdio.h>
49 #include <stdint.h>
50 #include <stdlib.h>
51 #include <string.h>
52 
53 #endif /* _KERNEL */
54 
55 #include "bhnd_nvram_private.h"
56 #include "bhnd_nvram_datavar.h"
57 
58 #include "bhnd_nvram_storevar.h"
59 
60 static int bhnd_nvstore_idx_cmp(const void *lhs, const void *rhs, void *ctx);
61 
62 /**
63  * Allocate and initialize a new path instance.
64  *
65  * The caller is responsible for deallocating the instance via
66  * bhnd_nvstore_path_free().
67  *
68  * @param	path_str	The path's canonical string representation.
69  * @param	path_len	The length of @p path_str.
70  *
71  * @retval non-NULL	success
72  * @retval NULL		if allocation fails.
73  */
74 bhnd_nvstore_path *
75 bhnd_nvstore_path_new(const char *path_str, size_t path_len)
76 {
77 	bhnd_nvstore_path *path;
78 
79 	/* Allocate new entry */
80 	path = bhnd_nv_malloc(sizeof(*path));
81 	if (path == NULL)
82 		return (NULL);
83 
84 	path->index = NULL;
85 	path->num_vars = 0;
86 
87 	path->pending = bhnd_nvram_plist_new();
88 	if (path->pending == NULL)
89 		goto failed;
90 
91 	path->path_str = bhnd_nv_strndup(path_str, path_len);
92 	if (path->path_str == NULL)
93 		goto failed;
94 
95 	return (path);
96 
97 failed:
98 	if (path->pending != NULL)
99 		bhnd_nvram_plist_release(path->pending);
100 
101 	if (path->path_str != NULL)
102 		bhnd_nv_free(path->path_str);
103 
104 	bhnd_nv_free(path);
105 
106 	return (NULL);
107 }
108 
109 /**
110  * Free an NVRAM path instance, releasing all associated resources.
111  */
112 void
113 bhnd_nvstore_path_free(struct bhnd_nvstore_path *path)
114 {
115 	/* Free the per-path index */
116 	if (path->index != NULL)
117 		bhnd_nvstore_index_free(path->index);
118 
119 	bhnd_nvram_plist_release(path->pending);
120 	bhnd_nv_free(path->path_str);
121 	bhnd_nv_free(path);
122 }
123 
124 /**
125  * Allocate and initialize a new index instance with @p capacity.
126  *
127  * The caller is responsible for deallocating the instance via
128  * bhnd_nvstore_index_free().
129  *
130  * @param	capacity	The maximum number of variables to be indexed.
131  *
132  * @retval non-NULL	success
133  * @retval NULL		if allocation fails.
134  */
135 bhnd_nvstore_index *
136 bhnd_nvstore_index_new(size_t capacity)
137 {
138 	bhnd_nvstore_index	*index;
139 	size_t			 bytes;
140 
141 	/* Allocate and populate variable index */
142 	bytes = sizeof(struct bhnd_nvstore_index) + (sizeof(void *) * capacity);
143 	index = bhnd_nv_malloc(bytes);
144 	if (index == NULL) {
145 		BHND_NV_LOG("error allocating %zu byte index\n", bytes);
146 		return (NULL);
147 	}
148 
149 	index->count = 0;
150 	index->capacity = capacity;
151 
152 	return (index);
153 }
154 
155 /**
156  * Free an index instance, releasing all associated resources.
157  *
158  * @param	index	An index instance previously allocated via
159  *			bhnd_nvstore_index_new().
160  */
161 void
162 bhnd_nvstore_index_free(bhnd_nvstore_index *index)
163 {
164 	bhnd_nv_free(index);
165 }
166 
167 /**
168  * Append a new NVRAM variable's @p cookiep value to @p index.
169  *
170  * After one or more append requests, the index must be prepared via
171  * bhnd_nvstore_index_prepare() before any indexed lookups are performed.
172  *
173  * @param	sc	The NVRAM store from which NVRAM values will be queried.
174  * @param	index	The index to be modified.
175  * @param	cookiep	The cookiep value (as provided by the backing NVRAM
176  *			data instance of @p sc) to be included in @p index.
177  *
178  * @retval 0		success
179  * @retval ENOMEM	if appending an additional entry would exceed the
180  *			capacity of @p index.
181  */
182 int
183 bhnd_nvstore_index_append(struct bhnd_nvram_store *sc,
184     bhnd_nvstore_index *index, void *cookiep)
185 {
186 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
187 
188 	if (index->count >= index->capacity)
189 		return (ENOMEM);
190 
191 	index->cookiep[index->count] = cookiep;
192 	index->count++;
193 	return (0);
194 }
195 
196 /* sort function for bhnd_nvstore_index_prepare() */
197 static int
198 bhnd_nvstore_idx_cmp(const void *lhs, const void *rhs, void *ctx)
199 {
200 	struct bhnd_nvram_store	*sc;
201 	void			*l_cookiep, *r_cookiep;
202 	const char		*l_str, *r_str;
203 	const char		*l_name, *r_name;
204 	int			 order;
205 
206 	sc = ctx;
207 	l_cookiep = *(void * const *)lhs;
208 	r_cookiep = *(void * const *)rhs;
209 
210 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
211 
212 	/* Fetch string pointers from the cookiep values */
213 	l_str = bhnd_nvram_data_getvar_name(sc->data, l_cookiep);
214 	r_str = bhnd_nvram_data_getvar_name(sc->data, r_cookiep);
215 
216 	/* Trim device path prefixes */
217 	if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) {
218 		l_name = bhnd_nvram_trim_path_name(l_str);
219 		r_name = bhnd_nvram_trim_path_name(r_str);
220 	} else {
221 		l_name = l_str;
222 		r_name = r_str;
223 	}
224 
225 	/* Perform comparison */
226 	order = strcmp(l_name, r_name);
227 	if (order != 0 || lhs == rhs)
228 		return (order);
229 
230 	/* If the backing data incorrectly contains variables with duplicate
231 	 * names, we need a sort order that provides stable behavior.
232 	 *
233 	 * Since Broadcom's own code varies wildly on this question, we just
234 	 * use a simple precedence rule: The first declaration of a variable
235 	 * takes precedence. */
236 	return (bhnd_nvram_data_getvar_order(sc->data, l_cookiep, r_cookiep));
237 }
238 
239 /**
240  * Prepare @p index for querying via bhnd_nvstore_index_lookup().
241  *
242  * After one or more append requests, the index must be prepared via
243  * bhnd_nvstore_index_prepare() before any indexed lookups are performed.
244  *
245  * @param	sc	The NVRAM store from which NVRAM values will be queried.
246  * @param	index	The index to be prepared.
247  *
248  * @retval 0		success
249  * @retval non-zero	if preparing @p index otherwise fails, a regular unix
250  *			error code will be returned.
251  */
252 int
253 bhnd_nvstore_index_prepare(struct bhnd_nvram_store *sc,
254     bhnd_nvstore_index *index)
255 {
256 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
257 
258 	/* Sort the index table */
259 	qsort_r(index->cookiep, index->count, sizeof(index->cookiep[0]),
260 	    bhnd_nvstore_idx_cmp, sc);
261 
262 	return (0);
263 }
264 
265 /**
266  * Return a borrowed reference to the root path node.
267  *
268  * @param	sc	The NVRAM store.
269  */
270 bhnd_nvstore_path *
271 bhnd_nvstore_get_root_path(struct bhnd_nvram_store *sc)
272 {
273 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
274 	return (sc->root_path);
275 }
276 
277 /**
278  * Return true if @p path is the root path node.
279  *
280  * @param	sc	The NVRAM store.
281  * @param	path	The path to query.
282  */
283 bool
284 bhnd_nvstore_is_root_path(struct bhnd_nvram_store *sc, bhnd_nvstore_path *path)
285 {
286 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
287 	return (sc->root_path == path);
288 }
289 
290 /**
291  * Return the update entry matching @p name in @p path, or NULL if no entry
292  * found.
293  *
294  * @param sc	The NVRAM store.
295  * @param path	The path to query.
296  * @param name	The NVRAM variable name to search for in @p path's update list.
297  *
298  * @retval non-NULL	success
299  * @retval NULL		if @p name is not found in @p path.
300  */
301 bhnd_nvram_prop *
302 bhnd_nvstore_path_get_update(struct bhnd_nvram_store *sc,
303     bhnd_nvstore_path *path, const char *name)
304 {
305 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
306 	return (bhnd_nvram_plist_get_prop(path->pending, name));
307 }
308 
309 /**
310  * Register or remove an update record for @p name in @p path.
311  *
312  * @param sc	The NVRAM store.
313  * @param path	The path to be modified.
314  * @param name	The path-relative variable name to be modified.
315  * @param value	The new value. A value of BHND_NVRAM_TYPE_NULL denotes deletion.
316  *
317  * @retval 0		success
318  * @retval ENOMEM	if allocation fails.
319  * @retval ENOENT	if @p name is unknown.
320  * @retval EINVAL	if @p value is NULL, and deletion of @p is not
321  *			supported.
322  * @retval EINVAL	if @p value cannot be converted to a supported value
323  *			type.
324  */
325 int
326 bhnd_nvstore_path_register_update(struct bhnd_nvram_store *sc,
327     bhnd_nvstore_path *path, const char *name, bhnd_nvram_val *value)
328 {
329 	bhnd_nvram_val		*prop_val;
330 	const char		*full_name;
331 	void			*cookiep;
332 	char			*namebuf;
333 	int			 error;
334 	bool			 nvram_committed;
335 
336 	namebuf = NULL;
337 	prop_val = NULL;
338 
339 	/* Determine whether the variable is currently defined in the
340 	 * backing NVRAM data, and derive its full path-prefixed name */
341 	nvram_committed = false;
342 	cookiep = bhnd_nvstore_path_data_lookup(sc, path, name);
343 	if (cookiep != NULL) {
344 		/* Variable is defined in the backing data */
345 		nvram_committed = true;
346 
347 		/* Use the existing variable name */
348 		full_name = bhnd_nvram_data_getvar_name(sc->data, cookiep);
349 	} else if (path == sc->root_path) {
350 		/* No prefix required for root path */
351 		full_name = name;
352 	} else {
353 		bhnd_nvstore_alias	*alias;
354 		int			 len;
355 
356 		/* New variable is being set; we need to determine the
357 		 * appropriate path prefix */
358 		alias = bhnd_nvstore_find_alias(sc, path->path_str);
359 		if (alias != NULL) {
360 			/* Use <alias>:name */
361 			len = bhnd_nv_asprintf(&namebuf, "%lu:%s", alias->alias,
362 			    name);
363 		} else {
364 			/* Use path/name */
365 			len = bhnd_nv_asprintf(&namebuf, "%s/%s",
366 			    path->path_str, name);
367 		}
368 
369 		if (len < 0)
370 			return (ENOMEM);
371 
372 		full_name = namebuf;
373 	}
374 
375 	/* Allow the data store to filter the NVRAM operation */
376 	if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) {
377 		error = bhnd_nvram_data_filter_unsetvar(sc->data, full_name);
378 		if (error) {
379 			BHND_NV_LOG("cannot unset property %s: %d\n", full_name,
380 			    error);
381 			goto cleanup;
382 		}
383 
384 		if ((prop_val = bhnd_nvram_val_copy(value)) == NULL) {
385 			error = ENOMEM;
386 			goto cleanup;
387 		}
388 	} else {
389 		error = bhnd_nvram_data_filter_setvar(sc->data, full_name,
390 		    value,  &prop_val);
391 		if (error) {
392 			BHND_NV_LOG("cannot set property %s: %d\n", full_name,
393 			    error);
394 			goto cleanup;
395 		}
396 	}
397 
398 	/* Add relative variable name to the per-path update list */
399 	if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL &&
400 	    !nvram_committed)
401 	{
402 		/* This is a deletion request for a variable not defined in
403 		 * out backing store; we can simply remove the corresponding
404 		 * update entry. */
405 		bhnd_nvram_plist_remove(path->pending, name);
406 	} else {
407 		/* Update or append a pending update entry */
408 		error = bhnd_nvram_plist_replace_val(path->pending, name,
409 		    prop_val);
410 		if (error)
411 			goto cleanup;
412 	}
413 
414 	/* Success */
415 	error = 0;
416 
417 cleanup:
418 	if (namebuf != NULL)
419 		bhnd_nv_free(namebuf);
420 
421 	if (prop_val != NULL)
422 		bhnd_nvram_val_release(prop_val);
423 
424 	return (error);
425 }
426 
427 /**
428  * Iterate over all variable cookiep values retrievable from the backing
429  * data store in @p path.
430  *
431  * @warning Pending updates in @p path are ignored by this function.
432  *
433  * @param		sc	The NVRAM store.
434  * @param		path	The NVRAM path to be iterated.
435  * @param[in,out]	indexp	A pointer to an opaque indexp value previously
436  *				returned by bhnd_nvstore_path_data_next(), or a
437  *				NULL value to begin iteration.
438  *
439  * @return Returns the next variable name, or NULL if there are no more
440  * variables defined in @p path.
441  */
442 void *
443 bhnd_nvstore_path_data_next(struct bhnd_nvram_store *sc,
444      bhnd_nvstore_path *path, void **indexp)
445 {
446 	void **index_ref;
447 
448 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
449 
450 	/* No index */
451 	if (path->index == NULL) {
452 		/* An index is required for all non-empty, non-root path
453 		 * instances */
454 		BHND_NV_ASSERT(bhnd_nvstore_is_root_path(sc, path),
455 		    ("missing index for non-root path %s", path->path_str));
456 
457 		/* Iterate NVRAM data directly, using the NVRAM data's cookiep
458 		 * value as our indexp context */
459 		if ((bhnd_nvram_data_next(sc->data, indexp)) == NULL)
460 			return (NULL);
461 
462 		return (*indexp);
463 	}
464 
465 	/* Empty index */
466 	if (path->index->count == 0)
467 		return (NULL);
468 
469 	if (*indexp == NULL) {
470 		/* First index entry */
471 		index_ref = &path->index->cookiep[0];
472 	} else {
473 		size_t idxpos;
474 
475 		/* Advance to next index entry */
476 		index_ref = *indexp;
477 		index_ref++;
478 
479 		/* Hit end of index? */
480 		BHND_NV_ASSERT(index_ref > path->index->cookiep,
481 		    ("invalid indexp"));
482 
483 		idxpos = (index_ref - path->index->cookiep);
484 		if (idxpos >= path->index->count)
485 			return (NULL);
486 	}
487 
488 	/* Provide new index position */
489 	*indexp = index_ref;
490 
491 	/* Return the data's cookiep value */
492 	return (*index_ref);
493 }
494 
495 /**
496  * Perform an lookup of @p name in the backing NVRAM data for @p path,
497  * returning the associated cookiep value, or NULL if the variable is not found
498  * in the backing NVRAM data.
499  *
500  * @warning Pending updates in @p path are ignored by this function.
501  *
502  * @param	sc	The NVRAM store from which NVRAM values will be queried.
503  * @param	path	The path to be queried.
504  * @param	name	The variable name to be queried.
505  *
506  * @retval non-NULL	success
507  * @retval NULL		if @p name is not found in @p index.
508  */
509 void *
510 bhnd_nvstore_path_data_lookup(struct bhnd_nvram_store *sc,
511     bhnd_nvstore_path *path, const char *name)
512 {
513 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
514 
515 	/* No index */
516 	if (path->index == NULL) {
517 		/* An index is required for all non-empty, non-root path
518 		 * instances */
519 		BHND_NV_ASSERT(bhnd_nvstore_is_root_path(sc, path),
520 		    ("missing index for non-root path %s", path->path_str));
521 
522 		/* Look up directly in NVRAM data */
523 		return (bhnd_nvram_data_find(sc->data, name));
524 	}
525 
526 	/* Otherwise, delegate to an index-based lookup */
527 	return (bhnd_nvstore_index_lookup(sc, path->index, name));
528 }
529 
530 /**
531  * Perform an index lookup of @p name, returning the associated cookiep
532  * value, or NULL if the variable does not exist.
533  *
534  * @param	sc	The NVRAM store from which NVRAM values will be queried.
535  * @param	index	The index to be queried.
536  * @param	name	The variable name to be queried.
537  *
538  * @retval non-NULL	success
539  * @retval NULL		if @p name is not found in @p index.
540  */
541 void *
542 bhnd_nvstore_index_lookup(struct bhnd_nvram_store *sc,
543     bhnd_nvstore_index *index, const char *name)
544 {
545 	void		*cookiep;
546 	const char	*indexed_name;
547 	size_t		 min, mid, max;
548 	uint32_t	 data_caps;
549 	int		 order;
550 
551 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
552 	BHND_NV_ASSERT(index != NULL, ("NULL index"));
553 
554 	/*
555 	 * Locate the requested variable using a binary search.
556 	 */
557 	if (index->count == 0)
558 		return (NULL);
559 
560 	data_caps = sc->data_caps;
561 	min = 0;
562 	max = index->count - 1;
563 
564 	while (max >= min) {
565 		/* Select midpoint */
566 		mid = (min + max) / 2;
567 		cookiep = index->cookiep[mid];
568 
569 		/* Fetch variable name */
570 		indexed_name = bhnd_nvram_data_getvar_name(sc->data, cookiep);
571 
572 		/* Trim any path prefix */
573 		if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)
574 			indexed_name = bhnd_nvram_trim_path_name(indexed_name);
575 
576 		/* Determine which side of the partition to search */
577 		order = strcmp(indexed_name, name);
578 		if (order < 0) {
579 			/* Search upper partition */
580 			min = mid + 1;
581 		} else if (order > 0) {
582 			/* Search (non-empty) lower partition */
583 			if (mid == 0)
584 				break;
585 			max = mid - 1;
586 		} else if (order == 0) {
587 			size_t	idx;
588 
589 			/*
590 			 * Match found.
591 			 *
592 			 * If this happens to be a key with multiple definitions
593 			 * in the backing store, we need to find the entry with
594 			 * the highest declaration precedence.
595 			 *
596 			 * Duplicates are sorted in order of descending
597 			 * precedence; to find the highest precedence entry,
598 			 * we search backwards through the index.
599 			 */
600 			idx = mid;
601 			while (idx > 0) {
602 				void		*dup_cookiep;
603 				const char	*dup_name;
604 
605 				/* Fetch preceding index entry */
606 				idx--;
607 				dup_cookiep = index->cookiep[idx];
608 				dup_name = bhnd_nvram_data_getvar_name(sc->data,
609 				    dup_cookiep);
610 
611 				/* Trim any path prefix */
612 				if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) {
613 					dup_name = bhnd_nvram_trim_path_name(
614 					    dup_name);
615 				}
616 
617 				/* If no match, current cookiep is the variable
618 				 * definition with the highest precedence */
619 				if (strcmp(indexed_name, dup_name) != 0)
620 					return (cookiep);
621 
622 				/* Otherwise, prefer this earlier definition,
623 				 * and keep searching for a higher-precedence
624 				 * definitions */
625 				cookiep = dup_cookiep;
626 			}
627 
628 			return (cookiep);
629 		}
630 	}
631 
632 	/* Not found */
633 	return (NULL);
634 }
635 
636 /**
637  * Return the device path entry registered for @p path, if any.
638  *
639  * @param	sc		The NVRAM store to be queried.
640  * @param	path		The device path to search for.
641  * @param	path_len	The length of @p path.
642  *
643  * @retval non-NULL	if found.
644  * @retval NULL		if not found.
645  */
646 bhnd_nvstore_path *
647 bhnd_nvstore_get_path(struct bhnd_nvram_store *sc, const char *path,
648     size_t path_len)
649 {
650 	bhnd_nvstore_path_list	*plist;
651 	bhnd_nvstore_path	*p;
652 	uint32_t		 h;
653 
654 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
655 
656 	/* Use hash lookup */
657 	h = hash32_strn(path, path_len, HASHINIT);
658 	plist = &sc->paths[h % nitems(sc->paths)];
659 
660 	LIST_FOREACH(p, plist, np_link) {
661 		/* Check for prefix match */
662 		if (strncmp(p->path_str, path, path_len) != 0)
663 			continue;
664 
665 		/* Check for complete match */
666 		if (strnlen(path, path_len) != strlen(p->path_str))
667 			continue;
668 
669 		return (p);
670 	}
671 
672 	/* Not found */
673 	return (NULL);
674 }
675 
676 /**
677  * Resolve @p aval to its corresponding device path entry, if any.
678  *
679  * @param	sc		The NVRAM store to be queried.
680  * @param	aval		The device path alias value to search for.
681  *
682  * @retval non-NULL	if found.
683  * @retval NULL		if not found.
684  */
685 bhnd_nvstore_path *
686 bhnd_nvstore_resolve_path_alias(struct bhnd_nvram_store *sc, u_long aval)
687 {
688 	bhnd_nvstore_alias *alias;
689 
690 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
691 
692 	/* Fetch alias entry */
693 	if ((alias = bhnd_nvstore_get_alias(sc, aval)) == NULL)
694 		return (NULL);
695 
696 	return (alias->path);
697 }
698 
699 /**
700  * Register a device path entry for the path referenced by variable name
701  * @p info, if any.
702  *
703  * @param	sc		The NVRAM store to be updated.
704  * @param	info		The NVRAM variable name info.
705  * @param	cookiep		The NVRAM variable's cookiep value.
706  *
707  * @retval 0		if the path was successfully registered, or an identical
708  *			path or alias entry exists.
709  * @retval EEXIST	if a conflicting entry already exists for the path or
710  *			alias referenced by @p info.
711  * @retval ENOENT	if @p info contains a dangling alias reference.
712  * @retval EINVAL	if @p info contains an unsupported bhnd_nvstore_var_type
713  *			and bhnd_nvstore_path_type combination.
714  * @retval ENOMEM	if allocation fails.
715  */
716 int
717 bhnd_nvstore_var_register_path(struct bhnd_nvram_store *sc,
718     bhnd_nvstore_name_info *info, void *cookiep)
719 {
720 	switch (info->type) {
721 	case BHND_NVSTORE_VAR:
722 		/* Variable */
723 		switch (info->path_type) {
724 		case BHND_NVSTORE_PATH_STRING:
725 			/* Variable contains a full path string
726 			 * (pci/1/1/varname); register the path */
727 			return (bhnd_nvstore_register_path(sc,
728 			    info->path.str.value, info->path.str.value_len));
729 
730 		case BHND_NVSTORE_PATH_ALIAS:
731 			/* Variable contains an alias reference (0:varname).
732 			 * There's no path to register */
733 			return (0);
734 		}
735 
736 		BHND_NV_PANIC("unsupported path type %d", info->path_type);
737 		break;
738 
739 	case BHND_NVSTORE_ALIAS_DECL:
740 		/* Alias declaration */
741 		return (bhnd_nvstore_register_alias(sc, info, cookiep));
742 	}
743 
744 	BHND_NV_PANIC("unsupported var type %d", info->type);
745 }
746 
747 /**
748  * Resolve the device path entry referenced by @p info.
749  *
750  * @param	sc		The NVRAM store to be updated.
751  * @param	info		Variable name information descriptor containing
752  *				the path or path alias to be resolved.
753  *
754  * @retval non-NULL	if found.
755  * @retval NULL		if not found.
756  */
757 bhnd_nvstore_path *
758 bhnd_nvstore_var_get_path(struct bhnd_nvram_store *sc,
759     bhnd_nvstore_name_info *info)
760 {
761 	switch (info->path_type) {
762 	case BHND_NVSTORE_PATH_STRING:
763 		return (bhnd_nvstore_get_path(sc, info->path.str.value,
764 		    info->path.str.value_len));
765 	case BHND_NVSTORE_PATH_ALIAS:
766 		return (bhnd_nvstore_resolve_path_alias(sc,
767 		    info->path.alias.value));
768 	}
769 
770 	BHND_NV_PANIC("unsupported path type %d", info->path_type);
771 }
772 
773 /**
774  * Return the device path alias entry registered for @p alias_val, if any.
775  *
776  * @param	sc		The NVRAM store to be queried.
777  * @param	alias_val	The alias value to search for.
778  *
779  * @retval non-NULL	if found.
780  * @retval NULL		if not found.
781  */
782 bhnd_nvstore_alias *
783 bhnd_nvstore_get_alias(struct bhnd_nvram_store *sc, u_long alias_val)
784 {
785 	bhnd_nvstore_alias_list	*alist;
786 	bhnd_nvstore_alias	*alias;
787 
788 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
789 
790 	/* Can use hash lookup */
791 	alist = &sc->aliases[alias_val % nitems(sc->aliases)];
792 	LIST_FOREACH(alias, alist, na_link) {
793 		if (alias->alias == alias_val)
794 			return (alias);
795 	}
796 
797 	/* Not found */
798 	return (NULL);
799 }
800 
801 /**
802  * Return the device path alias entry registered for @p path, if any.
803  *
804  * @param	sc	The NVRAM store to be queried.
805  * @param	path	The alias path to search for.
806  *
807  * @retval non-NULL	if found.
808  * @retval NULL		if not found.
809  */
810 bhnd_nvstore_alias *
811 bhnd_nvstore_find_alias(struct bhnd_nvram_store *sc, const char *path)
812 {
813 	bhnd_nvstore_alias *alias;
814 
815 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
816 
817 	/* Have to scan the full table */
818 	for (size_t i = 0; i < nitems(sc->aliases); i++) {
819 		LIST_FOREACH(alias, &sc->aliases[i], na_link) {
820 			if (strcmp(alias->path->path_str, path) == 0)
821 				return (alias);
822 		}
823 	}
824 
825 	/* Not found */
826 	return (NULL);
827 }
828 
829 /**
830  * Register a device path entry for @p path.
831  *
832  * @param	sc		The NVRAM store to be updated.
833  * @param	path_str	The absolute device path string.
834  * @param	path_len	The length of @p path_str.
835  *
836  * @retval 0		if the path was successfully registered, or an identical
837  *			path/alias entry already exists.
838  * @retval ENOMEM	if allocation fails.
839  */
840 int
841 bhnd_nvstore_register_path(struct bhnd_nvram_store *sc, const char *path_str,
842     size_t path_len)
843 {
844 	bhnd_nvstore_path_list	*plist;
845 	bhnd_nvstore_path	*path;
846 	uint32_t		 h;
847 
848 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
849 
850 	/* Already exists? */
851 	if (bhnd_nvstore_get_path(sc, path_str, path_len) != NULL)
852 		return (0);
853 
854 	/* Can't represent more than SIZE_MAX paths */
855 	if (sc->num_paths == SIZE_MAX)
856 		return (ENOMEM);
857 
858 	/* Allocate new entry */
859 	path = bhnd_nvstore_path_new(path_str, path_len);
860 	if (path == NULL)
861 		return (ENOMEM);
862 
863 	/* Insert in path hash table */
864 	h = hash32_str(path->path_str, HASHINIT);
865 	plist = &sc->paths[h % nitems(sc->paths)];
866 	LIST_INSERT_HEAD(plist, path, np_link);
867 
868 	/* Increment path count */
869 	sc->num_paths++;
870 
871 	return (0);
872 }
873 
874 /**
875  * Register a device path alias for an NVRAM 'devpathX' variable.
876  *
877  * The path value for the alias will be fetched from the backing NVRAM data.
878  *
879  * @param	sc	The NVRAM store to be updated.
880  * @param	info	The NVRAM variable name info.
881  * @param	cookiep	The NVRAM variable's cookiep value.
882  *
883  * @retval 0		if the alias was successfully registered, or an
884  *			identical alias entry exists.
885  * @retval EEXIST	if a conflicting alias or path entry already exists.
886  * @retval EINVAL	if @p info is not a BHND_NVSTORE_ALIAS_DECL or does
887  *			not contain a BHND_NVSTORE_PATH_ALIAS entry.
888  * @retval ENOMEM	if allocation fails.
889  */
890 int
891 bhnd_nvstore_register_alias(struct bhnd_nvram_store *sc,
892     const bhnd_nvstore_name_info *info, void *cookiep)
893 {
894 	bhnd_nvstore_alias_list	*alist;
895 	bhnd_nvstore_alias	*alias;
896 	bhnd_nvstore_path	*path;
897 	char			*path_str;
898 	size_t			 path_len;
899 	int			 error;
900 
901 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
902 
903 	path_str = NULL;
904 	alias = NULL;
905 
906 	/* Can't represent more than SIZE_MAX aliases */
907 	if (sc->num_aliases == SIZE_MAX)
908 		return (ENOMEM);
909 
910 	/* Must be an alias declaration */
911 	if (info->type != BHND_NVSTORE_ALIAS_DECL)
912 		return (EINVAL);
913 
914 	if (info->path_type != BHND_NVSTORE_PATH_ALIAS)
915 		return (EINVAL);
916 
917 	/* Fetch the devpath variable's value length */
918 	error = bhnd_nvram_data_getvar(sc->data, cookiep, NULL, &path_len,
919 	    BHND_NVRAM_TYPE_STRING);
920 	if (error)
921 		return (ENOMEM);
922 
923 	/* Allocate path string buffer */
924 	if ((path_str = bhnd_nv_malloc(path_len)) == NULL)
925 		return (ENOMEM);
926 
927 	/* Decode to our new buffer */
928 	error = bhnd_nvram_data_getvar(sc->data, cookiep, path_str, &path_len,
929 	    BHND_NVRAM_TYPE_STRING);
930 	if (error)
931 		goto failed;
932 
933 	/* Trim trailing '/' character(s) from the path length */
934 	path_len = strnlen(path_str, path_len);
935 	while (path_len > 0 && path_str[path_len-1] == '/') {
936 		path_str[path_len-1] = '\0';
937 		path_len--;
938 	}
939 
940 	/* Is a conflicting alias entry already registered for this alias
941 	 * value? */
942 	alias = bhnd_nvstore_get_alias(sc, info->path.alias.value);
943 	if (alias != NULL) {
944 		if (alias->cookiep != cookiep ||
945 		    strcmp(alias->path->path_str, path_str) != 0)
946 		{
947 			error = EEXIST;
948 			goto failed;
949 		}
950 	}
951 
952 	/* Is a conflicting entry already registered for the alias path? */
953 	if ((alias = bhnd_nvstore_find_alias(sc, path_str)) != NULL) {
954 		if (alias->alias != info->path.alias.value ||
955 		    alias->cookiep != cookiep ||
956 		    strcmp(alias->path->path_str, path_str) != 0)
957 		{
958 			error = EEXIST;
959 			goto failed;
960 		}
961 	}
962 
963 	/* Get (or register) the target path entry */
964 	path = bhnd_nvstore_get_path(sc, path_str, path_len);
965 	if (path == NULL) {
966 		error = bhnd_nvstore_register_path(sc, path_str, path_len);
967 		if (error)
968 			goto failed;
969 
970 		path = bhnd_nvstore_get_path(sc, path_str, path_len);
971 		BHND_NV_ASSERT(path != NULL, ("missing registered path"));
972 	}
973 
974 	/* Allocate alias entry */
975 	alias = bhnd_nv_calloc(1, sizeof(*alias));
976 	if (alias == NULL) {
977 		error = ENOMEM;
978 		goto failed;
979 	}
980 
981 	alias->path = path;
982 	alias->cookiep = cookiep;
983 	alias->alias = info->path.alias.value;
984 
985 	/* Insert in alias hash table */
986 	alist = &sc->aliases[alias->alias % nitems(sc->aliases)];
987 	LIST_INSERT_HEAD(alist, alias, na_link);
988 
989 	/* Increment alias count */
990 	sc->num_aliases++;
991 
992 	bhnd_nv_free(path_str);
993 	return (0);
994 
995 failed:
996 	if (path_str != NULL)
997 		bhnd_nv_free(path_str);
998 
999 	if (alias != NULL)
1000 		bhnd_nv_free(alias);
1001 
1002 	return (error);
1003 }
1004 
1005 /**
1006  * If @p child is equal to or a child path of @p parent, return a pointer to
1007  * @p child's path component(s) relative to @p parent; otherwise, return NULL.
1008  */
1009 const char *
1010 bhnd_nvstore_parse_relpath(const char *parent, const char *child)
1011 {
1012 	size_t prefix_len;
1013 
1014 	/* All paths have an implicit leading '/'; this allows us to treat
1015 	 * our manufactured root path of "/" as a prefix to all NVRAM-defined
1016 	 * paths (which do not necessarily include a leading '/' */
1017 	if (*parent == '/')
1018 		parent++;
1019 
1020 	if (*child == '/')
1021 		child++;
1022 
1023 	/* Is parent a prefix of child? */
1024 	prefix_len = strlen(parent);
1025 	if (strncmp(parent, child, prefix_len) != 0)
1026 		return (NULL);
1027 
1028 	/* A zero-length prefix matches everything */
1029 	if (prefix_len == 0)
1030 		return (child);
1031 
1032 	/* Is child equal to parent? */
1033 	if (child[prefix_len] == '\0')
1034 		return (child + prefix_len);
1035 
1036 	/* Is child actually a child of parent? */
1037 	if (child[prefix_len] == '/')
1038 		return (child + prefix_len + 1);
1039 
1040 	/* No match (e.g. parent=/foo..., child=/fooo...) */
1041 	return (NULL);
1042 }
1043 
1044 /**
1045  * Parse a raw NVRAM variable name and return its @p entry_type, its
1046  * type-specific @p prefix (e.g. '0:', 'pci/1/1', 'devpath'), and its
1047  * type-specific @p suffix (e.g. 'varname', '0').
1048  *
1049  * @param	name		The NVRAM variable name to be parsed. This
1050  *				value must remain valid for the lifetime of
1051  *				@p info.
1052  * @param	type		The NVRAM name type -- either INTERNAL for names
1053  *				parsed from backing NVRAM data, or EXTERNAL for
1054  *				names provided by external NVRAM store clients.
1055  * @param	data_caps	The backing NVRAM data capabilities
1056  *				(see bhnd_nvram_data_caps()).
1057  * @param[out]	info		On success, the parsed variable name info.
1058  *
1059  * @retval 0		success
1060  * @retval non-zero	if parsing @p name otherwise fails, a regular unix
1061  *			error code will be returned.
1062  */
1063 int
1064 bhnd_nvstore_parse_name_info(const char *name, bhnd_nvstore_name_type type,
1065     uint32_t data_caps, bhnd_nvstore_name_info *info)
1066 {
1067 	const char	*p;
1068 	char		*endp;
1069 
1070 	/* Skip path parsing? */
1071 	if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) {
1072 		/* devpath declaration? (devpath0=pci/1/1) */
1073 		if (strncmp(name, "devpath", strlen("devpath")) == 0) {
1074 			u_long alias;
1075 
1076 			/* Perform standard validation on the relative
1077 			 * variable name */
1078 			if (type != BHND_NVSTORE_NAME_INTERNAL &&
1079 			    !bhnd_nvram_validate_name(name))
1080 			{
1081 				return (ENOENT);
1082 			}
1083 
1084 			/* Parse alias value that should follow a 'devpath'
1085 			 * prefix */
1086 			p = name + strlen("devpath");
1087 			alias = strtoul(p, &endp, 10);
1088 			if (endp != p && *endp == '\0') {
1089 				info->type = BHND_NVSTORE_ALIAS_DECL;
1090 				info->path_type = BHND_NVSTORE_PATH_ALIAS;
1091 				info->name = name;
1092 				info->path.alias.value = alias;
1093 
1094 				return (0);
1095 			}
1096 		}
1097 
1098 		/* device aliased variable? (0:varname) */
1099 		if (bhnd_nv_isdigit(*name)) {
1100 			u_long alias;
1101 
1102 			/* Parse '0:' alias prefix */
1103 			alias = strtoul(name, &endp, 10);
1104 			if (endp != name && *endp == ':') {
1105 				/* Perform standard validation on the relative
1106 				 * variable name */
1107 				if (type != BHND_NVSTORE_NAME_INTERNAL &&
1108 				    !bhnd_nvram_validate_name(name))
1109 				{
1110 					return (ENOENT);
1111 				}
1112 
1113 				info->type = BHND_NVSTORE_VAR;
1114 				info->path_type = BHND_NVSTORE_PATH_ALIAS;
1115 
1116 				/* name follows 0: prefix */
1117 				info->name = endp + 1;
1118 				info->path.alias.value = alias;
1119 
1120 				return (0);
1121 			}
1122 		}
1123 
1124 		/* device variable? (pci/1/1/varname) */
1125 		if ((p = strrchr(name, '/')) != NULL) {
1126 			const char	*path, *relative_name;
1127 			size_t		 path_len;
1128 
1129 			/* Determine the path length; 'p' points at the last
1130 			 * path separator in 'name' */
1131 			path_len = p - name;
1132 			path = name;
1133 
1134 			/* The relative variable name directly follows the
1135 			 * final path separator '/' */
1136 			relative_name = path + path_len + 1;
1137 
1138 			/* Now that we calculated the name offset, exclude all
1139 			 * trailing '/' characters from the path length */
1140 			while (path_len > 0 && path[path_len-1] == '/')
1141 				path_len--;
1142 
1143 			/* Perform standard validation on the relative
1144 			 * variable name */
1145 			if (type != BHND_NVSTORE_NAME_INTERNAL &&
1146 			    !bhnd_nvram_validate_name(relative_name))
1147 			{
1148 				return (ENOENT);
1149 			}
1150 
1151 			/* Initialize result with pointers into the name
1152 			 * buffer */
1153 			info->type = BHND_NVSTORE_VAR;
1154 			info->path_type = BHND_NVSTORE_PATH_STRING;
1155 			info->name = relative_name;
1156 			info->path.str.value = path;
1157 			info->path.str.value_len = path_len;
1158 
1159 			return (0);
1160 		}
1161 	}
1162 
1163 	/* If all other parsing fails, the result is a simple variable with
1164 	 * an implicit path of "/" */
1165 	if (type != BHND_NVSTORE_NAME_INTERNAL &&
1166 	    !bhnd_nvram_validate_name(name))
1167 	{
1168 		/* Invalid relative name */
1169 		return (ENOENT);
1170 	}
1171 
1172 	info->type = BHND_NVSTORE_VAR;
1173 	info->path_type = BHND_NVSTORE_PATH_STRING;
1174 	info->name = name;
1175 	info->path.str.value = BHND_NVSTORE_ROOT_PATH;
1176 	info->path.str.value_len = BHND_NVSTORE_ROOT_PATH_LEN;
1177 
1178 	return (0);
1179 }
1180