mach_descrip.c revision 2233:4fb39ac32c26
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * Kernel Machine Description (MD)
31 *
32 * The Kernel maintains a global copy of the machine description for
33 * the system. This is for use by all kernel subsystems and is exported
34 * to user applications through the the 'mdesc' device driver. It is
35 * initially copied in from the Hypervisor at boot time, but can be
36 * updated dynamically on demand. The Kernel provides an interface
37 * for consumers to obtain a handle to the global MD. Consumers of the
38 * MD must use the specified interfaces. An update interface is provided
39 * for platform services to intiate an MD update on notification by a
40 * service entity.
41 *
42 * Locks
43 * The current global MD is protected by the curr_mach_descrip_lock.
44 * Each Machine description has a lock to synchornize its ref count.
45 * The Obsolete MD list is protected by the obs_list_lock.
46 */
47
48#include <sys/machsystm.h>
49#include <sys/vm.h>
50#include <sys/cpu.h>
51#include <sys/intreg.h>
52#include <sys/machcpuvar.h>
53#include <sys/machparam.h>
54#include <vm/hat_sfmmu.h>
55#include <vm/seg_kmem.h>
56#include <sys/error.h>
57#include <sys/hypervisor_api.h>
58#include <sys/types.h>
59#include <sys/sysmacros.h>
60#include <sys/mdesc.h>
61#include <sys/mdesc_impl.h>
62#include <sys/mach_descrip.h>
63#include <sys/prom_plat.h>
64#include <sys/promif.h>
65
66
67static void *mach_descrip_strt_meta_alloc(size_t size);
68static void mach_descrip_strt_meta_free(void *buf, size_t size);
69static void *mach_descrip_strt_buf_alloc(size_t size, size_t align);
70static void mach_descrip_strt_buf_free(void *buf, size_t size);
71static void *mach_descrip_buf_alloc(size_t size, size_t align);
72static void *mach_descrip_meta_alloc(size_t size);
73static uint64_t mach_descrip_find_md_gen(caddr_t ptr);
74static void init_md_params(void);
75static void init_domaining_enabled(md_t *mdp, mde_cookie_t *listp);
76
77/*
78 * Global ptr of the current generation Machine Description
79 */
80static machine_descrip_t *curr_mach_descrip;
81
82/*
83 * Initialized by machine_descrip_startup_init in startup.
84 * machine_descript_init will reintialize the structure with
85 * the vmem allocators once the vmem is available in the boot up
86 * process.
87 */
88static machine_descrip_memops_t *curr_mach_descrip_memops = NULL;
89
90static machine_descrip_memops_t startup_memops = {
91	mach_descrip_strt_buf_alloc,
92	mach_descrip_strt_buf_free,
93	mach_descrip_strt_meta_alloc,
94	mach_descrip_strt_meta_free,
95};
96
97static machine_descrip_memops_t mach_descrip_memops = {
98	mach_descrip_buf_alloc,
99	contig_mem_free,
100	mach_descrip_meta_alloc,
101	kmem_free,
102};
103
104static kmutex_t curr_mach_descrip_lock;
105/*
106 * List of obsolete Machine Descriptions
107 * Machine descriptions that have users are put on this list
108 * and freed after the last user has called md_fini_handle.
109 */
110static machine_descrip_t *obs_machine_descrip_list;
111
112static kmutex_t obs_list_lock;
113
114static const char alloc_fail_msg[] =
115	"MD: cannot allocate MD buffer of size %ld bytes\n";
116
117/*
118 * Global flag that indicates whether domaining features are
119 * available. The value is set at boot time based on the value
120 * of the 'domaining-enabled' property in the MD and the global
121 * override flag below. Updates to this variable after boot are
122 * not supported.
123 */
124uint_t domaining_enabled;
125
126/*
127 * Global override for the 'domaining_enabled' flag. If this
128 * flag is set in /etc/system, domaining features are disabled,
129 * ignoring the value of the 'domaining-enabled' property in
130 * the MD.
131 */
132uint_t force_domaining_disabled;
133
134#define	META_ALLOC_ALIGN	8
135#define	HAS_GEN(x)	(x != MDESC_INVAL_GEN)
136
137#ifdef DEBUG
138static int mach_descrip_debug = 0;
139
140#define	MDP(ARGS)	if (mach_descrip_debug) prom_printf ARGS
141#define	PRINT_LIST() 	if (mach_descrip_debug) print_obs_list()
142
143#ifdef	MACH_DESC_DEBUG
144static void
145dump_buf(uint8_t *bufp, int size)
146{
147	int i;
148	for (i = 0; i < size; i += 16) {
149		int j;
150		prom_printf("0x%04x :", i);
151		for (j = 0; j < 16 && (i+j) < size; j++)
152			prom_printf(" %02x", bufp[i+j]);
153		prom_printf("\n");
154	}
155}
156#endif /* MACH_DESC_DEBUG */
157
158static void
159print_obs_list(void)
160{
161	machine_descrip_t *lmdescp;
162	mutex_enter(&obs_list_lock);
163
164	lmdescp	= obs_machine_descrip_list;
165	prom_printf("MD_obs_list->");
166	while (lmdescp != NULL) {
167		prom_printf("g:%ld,r:%d", lmdescp->gen, lmdescp->refcnt);
168
169		lmdescp = lmdescp->next;
170		prom_printf("->");
171	}
172	prom_printf("NULL\n");
173	mutex_exit(&obs_list_lock);
174}
175
176#else
177#define	MDP(ARGS)
178#define	PRINT_LIST()
179#endif /* DEBUG */
180
181/*
182 * MD obsolete list managment functions
183 */
184static machine_descrip_t *
185md_obs_list_look_up_by_gen(uint64_t gen)
186{
187	machine_descrip_t *mdescp;
188
189	mutex_enter(&obs_list_lock);
190	mdescp = obs_machine_descrip_list;
191
192	while (mdescp != NULL) {
193		if (mdescp->gen == gen) {
194			mutex_exit(&obs_list_lock);
195			return (mdescp);
196		}
197		mdescp = mdescp->next;
198	}
199
200	mutex_exit(&obs_list_lock);
201	return (mdescp);
202}
203
204static void
205md_obs_list_remove(machine_descrip_t *mdescp)
206{
207	machine_descrip_t *lmdescp;
208
209	mutex_enter(&obs_list_lock);
210
211	lmdescp	= obs_machine_descrip_list;
212
213	if (obs_machine_descrip_list == mdescp) {
214		obs_machine_descrip_list = mdescp->next;
215	} else {
216		while (lmdescp != NULL) {
217			if (lmdescp->next == mdescp) {
218				lmdescp->next = mdescp->next;
219				mdescp->next = NULL;
220				break;
221			}
222			lmdescp = lmdescp->next;
223		}
224	}
225	mutex_exit(&obs_list_lock);
226	PRINT_LIST();
227}
228
229static void
230md_obs_list_add(machine_descrip_t *mdescp)
231{
232	mutex_enter(&obs_list_lock);
233
234	mdescp->next = obs_machine_descrip_list;
235	obs_machine_descrip_list = mdescp;
236
237	mutex_exit(&obs_list_lock);
238	PRINT_LIST();
239}
240
241/*
242 * Allocate a machine_descrip meta structure and intitialize it.
243 */
244static machine_descrip_t *
245new_mach_descrip(void)
246{
247	machine_descrip_t *mdescp;
248
249	mdescp = (machine_descrip_t *)(*curr_mach_descrip_memops->meta_allocp)
250	    (sizeof (machine_descrip_t));
251	if (mdescp != NULL) {
252		bzero(mdescp, sizeof (*mdescp));
253		mdescp->memops = curr_mach_descrip_memops;
254		mutex_init(&mdescp->lock, NULL, MUTEX_DRIVER, NULL);
255	}
256
257	return (mdescp);
258}
259
260/*
261 * Free a machine_descrip meta structure and intitialize it.
262 * Also free the MD buffer.
263 */
264static void
265destroy_machine_descrip(machine_descrip_t *mdescp)
266{
267	machine_descrip_memops_t  *mdesc_memopsp;
268
269	ASSERT((mdescp != NULL));
270
271	mdesc_memopsp = mdescp->memops;
272	if (mdescp->memops == NULL)
273		panic("destroy_machine_descrip: memops NULL\n");
274
275	(*mdesc_memopsp->buf_freep)(mdescp->va, mdescp->space);
276	mutex_destroy(&mdescp->lock);
277	(*mdesc_memopsp->meta_freep)(mdescp, sizeof (*mdescp));
278}
279
280/*
281 * Call into the Hypervisor to retrieve the most recent copy of the
282 * machine description. If references to the current MD are active
283 * stow it in the obsolete MD list and update the current MD reference
284 * with the new one.
285 * The obsolete list contains one MD per generation. If the firmware
286 * doesn't support MD generation fail the call.
287 */
288int
289mach_descrip_update(void)
290{
291	uint64_t	md_size0, md_size;
292	uint64_t	md_space = 0;
293	uint64_t	hvret;
294	caddr_t		tbuf = NULL;
295	uint64_t	tbuf_pa;
296	uint64_t	tgen;
297	int		ret = 0;
298
299	MDP(("MD: Requesting buffer size\n"));
300
301	ASSERT((curr_mach_descrip != NULL));
302
303	mutex_enter(&curr_mach_descrip_lock);
304
305	/*
306	 * If the required MD size changes between our first call
307	 * to hv_mach_desc (to find the required buf size) and the
308	 * second call (to get the actual MD), the MD was in the
309	 * process of being updated. Loop until the two sizes are
310	 * identical.
311	 */
312	do {
313		if (tbuf != NULL)
314			(*curr_mach_descrip_memops->buf_freep)(tbuf, md_space);
315
316		md_size0 = 0LL;
317		(void) hv_mach_desc((uint64_t)0, &md_size0);
318		MDP(("MD: buffer size is %ld\n", md_size0));
319
320		/*
321		 * Align allocated space to nearest page.
322		 * contig_mem_alloc_align() requires a power of 2 alignment.
323		 */
324		md_space = P2ROUNDUP(md_size0, PAGESIZE);
325		MDP(("MD: allocated space is %ld\n", md_space));
326
327		tbuf = (caddr_t)(*curr_mach_descrip_memops->buf_allocp)
328		    (md_space, PAGESIZE);
329		if (tbuf == NULL) {
330			ret = -1;
331			goto done;
332		}
333
334		tbuf_pa =  va_to_pa(tbuf);
335		hvret = hv_mach_desc(tbuf_pa, &md_size);
336		MDP(("MD: HV return code = %ld\n", hvret));
337
338		/*
339		 * We get H_EINVAL if our buffer size is too small. In
340		 * that case stay in the loop, reallocate the buffer
341		 * and try again.
342		 */
343		if (hvret != H_EOK && hvret != H_EINVAL) {
344			MDP(("MD: Failed with code %ld from HV\n", hvret));
345			ret = -1;
346			goto done;
347		}
348
349	} while (md_size0 != md_size || hvret == H_EINVAL);
350
351	tgen = mach_descrip_find_md_gen(tbuf);
352
353#ifdef DEBUG
354	if (!HAS_GEN(tgen)) {
355		MDP(("MD: generation number not found\n"));
356	} else
357		MDP(("MD: generation number %ld\n", tgen));
358#endif /* DEBUG */
359
360	if (curr_mach_descrip->va != NULL) {
361
362		/* check for the same generation number */
363		if (HAS_GEN(tgen) && ((curr_mach_descrip->gen == tgen) &&
364		    (curr_mach_descrip->size == md_size))) {
365#ifdef DEBUG
366			/*
367			 * Pedantic Check for generation number. If the
368			 * generation number is the same, make sure the
369			 * MDs are really identical.
370			 */
371			if (bcmp(curr_mach_descrip->va, tbuf, md_size) != 0) {
372				cmn_err(CE_WARN, "machine_descrip_update: MDs "
373				    "with the same generation (%ld) are not "
374				    "identical", tgen);
375				ret = -1;
376				goto done;
377			}
378#endif
379			cmn_err(CE_WARN, "machine_descrip_update: new MD has "
380			    "the same generation (%ld) as the old MD", tgen);
381			ret = 0;
382			goto done;
383		}
384
385		/* check for generations moving backwards */
386		if (HAS_GEN(tgen) && HAS_GEN(curr_mach_descrip->gen) &&
387		    (curr_mach_descrip->gen > tgen)) {
388			cmn_err(CE_WARN, "machine_descrip_update: new MD"
389			    " older generation (%ld) than current MD (%ld)",
390			    tgen, curr_mach_descrip->gen);
391			ret = -1;
392			goto done;
393		}
394
395		if (curr_mach_descrip->refcnt == 0) {
396
397			MDP(("MD: freeing old md buffer gen %ld\n",
398			    curr_mach_descrip->gen));
399
400			/* Free old space */
401			ASSERT(curr_mach_descrip->space > 0);
402
403			(*curr_mach_descrip_memops->buf_freep)
404			    (curr_mach_descrip->va, curr_mach_descrip->space);
405		} else {
406			if (!HAS_GEN(tgen)) {
407				/*
408				 * No update support if FW
409				 * doesn't have MD generation id
410				 * feature.
411				 */
412				prom_printf("WARNING: F/W does not support MD "
413				    "generation count, MD update failed\n");
414				ret = -1;
415				goto done;
416			}
417
418			MDP(("MD: adding to obs list %ld\n",
419			    curr_mach_descrip->gen));
420
421			md_obs_list_add(curr_mach_descrip);
422
423			curr_mach_descrip = new_mach_descrip();
424
425			if (curr_mach_descrip == NULL) {
426				panic("Allocation for machine description"
427				    " failed\n");
428			}
429		}
430	}
431
432	curr_mach_descrip->va = tbuf;
433	curr_mach_descrip->gen = tgen;
434	curr_mach_descrip->size = md_size;
435	curr_mach_descrip->space = md_space;
436
437#ifdef MACH_DESC_DEBUG
438	dump_buf((uint8_t *)curr_mach_descrip->va, md_size);
439#endif /* MACH_DESC_DEBUG */
440
441	mutex_exit(&curr_mach_descrip_lock);
442	return (ret);
443
444done:
445	if (tbuf != NULL)
446		(*curr_mach_descrip_memops->buf_freep)(tbuf, md_space);
447	mutex_exit(&curr_mach_descrip_lock);
448	return (ret);
449}
450
451static void *
452mach_descrip_buf_alloc(size_t size, size_t align)
453{
454	void *p;
455
456	if ((p = contig_mem_alloc_align(size, align)) == NULL)
457		cmn_err(CE_WARN, alloc_fail_msg, size);
458
459	return (p);
460}
461
462static void *
463mach_descrip_strt_meta_alloc(size_t size)
464{
465	return (mach_descrip_strt_buf_alloc(size, META_ALLOC_ALIGN));
466}
467
468static void
469mach_descrip_strt_meta_free(void *buf, size_t size)
470{
471	mach_descrip_strt_buf_free(buf, size);
472}
473
474static void *
475mach_descrip_strt_buf_alloc(size_t size, size_t align)
476{
477	void *p = prom_alloc((caddr_t)0, size, align);
478
479	if (p == NULL)
480		prom_printf(alloc_fail_msg, size);
481
482	return (p);
483}
484
485static void
486mach_descrip_strt_buf_free(void *buf, size_t size)
487{
488	prom_free((caddr_t)buf, size);
489}
490
491static void *
492mach_descrip_meta_alloc(size_t size)
493{
494	return (kmem_alloc(size, KM_SLEEP));
495}
496
497/*
498 * Initialize the kernel's Machine Description(MD) framework
499 * early on in startup during mlsetup() so consumers
500 * can get to the MD before the VM system has been initialized.
501 *
502 * Also get the most recent version of the MD.
503 */
504void
505mach_descrip_startup_init(void)
506{
507
508	mutex_init(&curr_mach_descrip_lock, NULL, MUTEX_DRIVER, NULL);
509	mutex_init(&obs_list_lock, NULL, MUTEX_DRIVER, NULL);
510
511	obs_machine_descrip_list = NULL;
512
513	curr_mach_descrip_memops = &startup_memops;
514
515	curr_mach_descrip = new_mach_descrip();
516	if (curr_mach_descrip == NULL)
517		panic("Allocation for machine description failed\n");
518
519	if (mach_descrip_update())
520		panic("Machine description initialization failed\n");
521
522}
523
524/*
525 * Counterpart to the above init function.  Free up resources
526 * allocated at startup by mach_descrip_startup_setup().
527 * And reset machine description framework state.
528 *
529 * All consumers must have fini'ed their handles at this point.
530 */
531void
532mach_descrip_startup_fini(void)
533{
534
535	ASSERT((curr_mach_descrip != NULL));
536	ASSERT((curr_mach_descrip->refcnt == 0));
537	ASSERT((obs_machine_descrip_list == NULL));
538
539	destroy_machine_descrip(curr_mach_descrip);
540	curr_mach_descrip = NULL;
541	curr_mach_descrip_memops = NULL;
542}
543
544/*
545 * Initialize the kernel's Machine Description(MD) framework
546 * after the the VM system has been initialized.
547 *
548 * Also get the most recent version of the MD.
549 * Assumes that the machine description frame work is in a clean
550 * state and the machine description intialized during startup
551 * has been cleaned up and resources deallocated.
552 */
553void
554mach_descrip_init(void)
555{
556	ASSERT((curr_mach_descrip == NULL &&
557	    curr_mach_descrip_memops == NULL));
558
559	curr_mach_descrip_memops = &mach_descrip_memops;
560
561	curr_mach_descrip = new_mach_descrip();
562	if (curr_mach_descrip == NULL)
563		panic("Allocation for machine description failed\n");
564
565	if (mach_descrip_update())
566		panic("Machine description intialization failed\n");
567
568	/* read in global params */
569	init_md_params();
570}
571
572/*
573 * Client interface to get a handle to the current MD.
574 * The md_fini_handle() interface should be used to
575 * clean up the refernce to the MD returned by this function.
576 */
577md_t *
578md_get_handle(void)
579{
580	md_t *mdp;
581
582	mutex_enter(&curr_mach_descrip_lock);
583
584	if (curr_mach_descrip == NULL) {
585		return (NULL);
586	}
587
588	curr_mach_descrip->refcnt++;
589	mdp = md_init_intern(curr_mach_descrip->va,
590	    curr_mach_descrip->memops->meta_allocp,
591	    curr_mach_descrip->memops->meta_freep);
592
593	mutex_exit(&curr_mach_descrip_lock);
594
595	return (mdp);
596}
597
598/*
599 * Client interface to clean up the refernce to the MD returned
600 * by md_get_handle().
601 */
602int
603md_fini_handle(md_t *ptr)
604{
605	machine_descrip_t *mdescp;
606	md_impl_t *mdp;
607
608
609	mdp = (md_impl_t *)ptr;
610
611	if (mdp == NULL)
612		return (-1);
613	/*
614	 * Check if mdp is current MD gen
615	 */
616	mutex_enter(&curr_mach_descrip_lock);
617
618	if (curr_mach_descrip->gen == mdp->gen) {
619		curr_mach_descrip->refcnt--;
620		mutex_exit(&curr_mach_descrip_lock);
621		goto fini;
622	}
623	mutex_exit(&curr_mach_descrip_lock);
624
625	/*
626	 * MD is in the obsolete list
627	 */
628	mdescp = md_obs_list_look_up_by_gen(mdp->gen);
629	if (mdescp == NULL)
630		return (-1);
631
632	mutex_enter(&mdescp->lock);
633	mdescp->refcnt--;
634	if (mdescp->refcnt == 0) {
635		md_obs_list_remove(mdescp);
636		mutex_exit(&mdescp->lock);
637		destroy_machine_descrip(mdescp);
638		goto fini;
639	}
640	mutex_exit(&mdescp->lock);
641
642fini:
643	return (md_fini(ptr));
644}
645
646/*
647 * General purpose initialization function used to extract parameters
648 * from the MD during the boot process. This is called immediately after
649 * the in kernel copy of the MD has been initialized so that global
650 * flags are available to various subsystems as they get initialized.
651 */
652static void
653init_md_params(void)
654{
655	md_t		*mdp;
656	int		num_nodes;
657	mde_cookie_t	*listp;
658	int		listsz;
659
660	mdp = md_get_handle();
661	ASSERT(mdp);
662	num_nodes = md_node_count(mdp);
663	ASSERT(num_nodes >= 0);
664
665	listsz = num_nodes * sizeof (mde_cookie_t);
666	listp = (mde_cookie_t *)
667	    (*curr_mach_descrip_memops->meta_allocp)(listsz);
668
669	/*
670	 * Import various parameters from the MD. For now,
671	 * the only parameter of interest is whether or not
672	 * domaining features are supported.
673	 */
674	init_domaining_enabled(mdp, listp);
675
676	(*curr_mach_descrip_memops->meta_freep)(listp, listsz);
677	(void) md_fini_handle(mdp);
678}
679
680static void
681init_domaining_enabled(md_t *mdp, mde_cookie_t *listp)
682{
683	mde_cookie_t	rootnode;
684	int		num_nodes;
685	uint64_t	val = 0;
686
687	/*
688	 * If domaining has been manually disabled, always
689	 * honor that and ignore the value in the MD.
690	 */
691	if (force_domaining_disabled) {
692		domaining_enabled = 0;
693		MDP(("domaining manually disabled\n"));
694		return;
695	}
696
697	rootnode = md_root_node(mdp);
698	ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
699
700	num_nodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, "platform"),
701	    md_find_name(mdp, "fwd"), listp);
702
703	/* should only be one platform node */
704	ASSERT(num_nodes == 1);
705
706	if (md_get_prop_val(mdp, *listp, "domaining-enabled", &val) != 0) {
707		/*
708		 * The property is not present. This implies
709		 * that the firmware does not support domaining
710		 * features.
711		 */
712		MDP(("'domaining-enabled' property not present\n"));
713
714		domaining_enabled = 0;
715		return;
716	}
717
718	domaining_enabled = val;
719
720	MDP(("domaining_enabled = 0x%x\n", domaining_enabled));
721}
722
723/*
724 * Client interface to get a pointer to the raw MD buffer
725 * Private to kernel and mdesc driver.
726 */
727caddr_t
728md_get_md_raw(md_t *ptr)
729{
730	md_impl_t *mdp;
731
732	mdp = (md_impl_t *)ptr;
733	if (mdp ==  NULL)
734		return (NULL);
735	return (mdp->caddr);
736}
737
738/*
739 * This is called before an MD structure is intialized, so
740 * it walks the raw MD looking for the generation property.
741 */
742static uint64_t
743mach_descrip_find_md_gen(caddr_t ptr)
744{
745	md_header_t	*hdrp;
746	md_element_t	*mdep;
747	md_element_t	*rootnode = NULL;
748	md_element_t	*elem = NULL;
749	char		*namep;
750	boolean_t	done;
751	int		idx;
752
753	hdrp = (md_header_t *)ptr;
754	mdep = (md_element_t *)(ptr + MD_HEADER_SIZE);
755	namep = (char *)(ptr + MD_HEADER_SIZE + hdrp->node_blk_sz);
756
757	/*
758	 * Very basic check for alignment to avoid
759	 * bus error issues.
760	 */
761	if ((((uint64_t)ptr) & 7) != 0)
762		return (MDESC_INVAL_GEN);
763
764	if (mdtoh32(hdrp->transport_version) != MD_TRANSPORT_VERSION) {
765		return (MDESC_INVAL_GEN);
766	}
767
768	/*
769	 * Search for the root node. Perform the walk manually
770	 * since the MD structure is not set up yet.
771	 */
772	for (idx = 0, done = B_FALSE; done == B_FALSE; ) {
773
774		md_element_t *np = &(mdep[idx]);
775
776		switch (MDE_TAG(np)) {
777		case MDET_LIST_END:
778			done = B_TRUE;
779			break;
780
781		case MDET_NODE:
782			if (strcmp(namep + MDE_NAME(np), "root") == 0) {
783				/* found root node */
784				rootnode = np;
785				done = B_TRUE;
786				break;
787			}
788			idx = MDE_PROP_INDEX(np);
789			break;
790
791		default:
792			/* ignore */
793			idx++;
794		}
795	}
796
797	if (rootnode == NULL) {
798		/* root not found */
799		return (MDESC_INVAL_GEN);
800	}
801
802	/* search the rootnode for the generation property */
803	for (elem = (rootnode + 1); MDE_TAG(elem) != MDET_NODE_END; elem++) {
804
805		char *prop_name;
806
807		/* generation field is a prop_val */
808		if (MDE_TAG(elem) != MDET_PROP_VAL)
809			continue;
810
811		prop_name = namep + MDE_NAME(elem);
812
813		if (strcmp(prop_name, "md-generation#") == 0) {
814			return (MDE_PROP_VALUE(elem));
815		}
816	}
817
818	return (MDESC_INVAL_GEN);
819}
820
821/*
822 * Failed to allocate the list : Return value -1
823 * md_scan_dag API failed      : Return the result from md_scan_dag API
824 */
825int
826md_alloc_scan_dag(md_t *ptr,
827	mde_cookie_t startnode,
828	char *node_name,
829	char *dag,
830	mde_cookie_t **list)
831{
832	int res;
833	md_impl_t *mdp = (md_impl_t *)ptr;
834
835	*list = (mde_cookie_t *)mdp->allocp(sizeof (mde_cookie_t) *
836	    mdp->node_count);
837	if (*list == NULL)
838		return (-1);
839
840	res = md_scan_dag(ptr, startnode,
841	    md_find_name(ptr, node_name),
842	    md_find_name(ptr, dag), *list);
843
844	/*
845	 * If md_scan_dag API returned 0 or -1 then free the buffer
846	 * and return -1 to indicate the error from this API.
847	 */
848	if (res < 1) {
849		md_free_scan_dag(ptr, list);
850		*list = NULL;
851	}
852
853	return (res);
854}
855
856void
857md_free_scan_dag(md_t *ptr,
858	mde_cookie_t **list)
859{
860	md_impl_t *mdp = (md_impl_t *)ptr;
861
862	mdp->freep(*list, sizeof (mde_cookie_t) * mdp->node_count);
863}
864