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