xref: /illumos-gate/usr/src/uts/sun4v/os/mach_descrip.c (revision 069e6b7e31ba5dcbc5441b98af272714d9a5455c)
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 
65 static void *mach_descrip_strt_meta_alloc(size_t size);
66 static void mach_descrip_strt_meta_free(void *buf, size_t size);
67 static void *mach_descrip_strt_buf_alloc(size_t size, size_t align);
68 static void mach_descrip_strt_buf_free(void *buf, size_t size);
69 static void *mach_descrip_buf_alloc(size_t size, size_t align);
70 static void *mach_descrip_meta_alloc(size_t size);
71 static uint64_t mach_descrip_find_md_gen(caddr_t ptr);
72 static void init_md_params(void);
73 static void init_domaining_capabilities(md_t *mdp, mde_cookie_t *listp);
74 
75 /*
76  * Global ptr of the current generation Machine Description
77  */
78 static 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  */
86 static machine_descrip_memops_t *curr_mach_descrip_memops = NULL;
87 
88 static 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 
95 static 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 
102 static 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  */
108 static machine_descrip_t *obs_machine_descrip_list;
109 
110 static kmutex_t obs_list_lock;
111 
112 static 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  */
122 uint_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  */
130 uint_t force_domaining_disabled;
131 
132 #define	META_ALLOC_ALIGN	8
133 #define	HAS_GEN(x)	(x != MDESC_INVAL_GEN)
134 
135 #ifdef DEBUG
136 static 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
142 static void
143 dump_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 
156 static void
157 print_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  */
182 static machine_descrip_t *
183 md_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 
202 static void
203 md_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 
227 static void
228 md_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  */
242 static machine_descrip_t *
243 new_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  */
262 static void
263 destroy_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  */
286 int
287 mach_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 
441 done:
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 
448 static void *
449 mach_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 
459 static void *
460 mach_descrip_strt_meta_alloc(size_t size)
461 {
462 	return (mach_descrip_strt_buf_alloc(size, META_ALLOC_ALIGN));
463 }
464 
465 static void
466 mach_descrip_strt_meta_free(void *buf, size_t size)
467 {
468 	mach_descrip_strt_buf_free(buf, size);
469 }
470 
471 static void *
472 mach_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 
482 static void
483 mach_descrip_strt_buf_free(void *buf, size_t size)
484 {
485 	prom_free((caddr_t)buf, size);
486 }
487 
488 static void *
489 mach_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  */
501 void
502 mach_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  */
528 void
529 mach_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  */
550 void
551 mach_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  */
574 md_t *
575 md_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  */
602 int
603 md_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 
642 fini:
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  */
652 static void
653 init_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 
680 static void
681 init_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  */
725 caddr_t
726 md_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  */
740 static uint64_t
741 mach_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  */
823 int
824 md_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 
854 void
855 md_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  */
868 uint64_t
869 md_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