xref: /illumos-gate/usr/src/uts/sun4v/os/mach_descrip.c (revision 09295472)
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 
67 static void *mach_descrip_strt_meta_alloc(size_t size);
68 static void mach_descrip_strt_meta_free(void *buf, size_t size);
69 static void *mach_descrip_strt_buf_alloc(size_t size, size_t align);
70 static void mach_descrip_strt_buf_free(void *buf, size_t size);
71 static void *mach_descrip_buf_alloc(size_t size, size_t align);
72 static void *mach_descrip_meta_alloc(size_t size);
73 static uint64_t mach_descrip_find_md_gen(caddr_t ptr);
74 static void init_md_params(void);
75 static void init_domaining_enabled(md_t *mdp, mde_cookie_t *listp);
76 
77 /*
78  * Global ptr of the current generation Machine Description
79  */
80 static 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  */
88 static machine_descrip_memops_t *curr_mach_descrip_memops = NULL;
89 
90 static 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 
97 static 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 
104 static 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  */
110 static machine_descrip_t *obs_machine_descrip_list;
111 
112 static kmutex_t obs_list_lock;
113 
114 static 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  */
124 uint_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  */
132 uint_t force_domaining_disabled;
133 
134 #define	META_ALLOC_ALIGN	8
135 #define	HAS_GEN(x)	(x != MDESC_INVAL_GEN)
136 
137 #ifdef DEBUG
138 static 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
144 static void
145 dump_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 
158 static void
159 print_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  */
184 static machine_descrip_t *
185 md_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 
204 static void
205 md_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 
229 static void
230 md_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  */
244 static machine_descrip_t *
245 new_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  */
264 static void
265 destroy_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  */
288 int
289 mach_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 
444 done:
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 
451 static void *
452 mach_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 
462 static void *
463 mach_descrip_strt_meta_alloc(size_t size)
464 {
465 	return (mach_descrip_strt_buf_alloc(size, META_ALLOC_ALIGN));
466 }
467 
468 static void
469 mach_descrip_strt_meta_free(void *buf, size_t size)
470 {
471 	mach_descrip_strt_buf_free(buf, size);
472 }
473 
474 static void *
475 mach_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 
485 static void
486 mach_descrip_strt_buf_free(void *buf, size_t size)
487 {
488 	prom_free((caddr_t)buf, size);
489 }
490 
491 static void *
492 mach_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  */
504 void
505 mach_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  */
531 void
532 mach_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  */
553 void
554 mach_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  */
577 md_t *
578 md_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  */
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_enabled(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_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  */
727 caddr_t
728 md_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  */
742 static uint64_t
743 mach_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  */
825 int
826 md_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 
856 void
857 md_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