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