xref: /illumos-gate/usr/src/uts/sun4v/os/mach_descrip.c (revision 1a220b56b93ff1dc80855691548503117af4cc10)
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 
68 static void *mach_descrip_strt_meta_alloc(size_t size);
69 static void mach_descrip_strt_meta_free(void *buf, size_t size);
70 static void *mach_descrip_strt_buf_alloc(size_t size, size_t align);
71 static void mach_descrip_strt_buf_free(void *buf, size_t size);
72 static void *mach_descrip_buf_alloc(size_t size, size_t align);
73 static void *mach_descrip_meta_alloc(size_t size);
74 static uint64_t mach_descrip_find_md_gen(caddr_t ptr);
75 static void init_md_params(void);
76 static void init_domaining_enabled(md_t *mdp, mde_cookie_t *listp);
77 
78 extern struct bootops *bootops;
79 
80 /*
81  * Global ptr of the current generation Machine Description
82  */
83 static 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  */
91 static machine_descrip_memops_t *curr_mach_descrip_memops = NULL;
92 
93 static 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 
100 static 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 
107 static 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  */
113 static machine_descrip_t *obs_machine_descrip_list;
114 
115 static kmutex_t obs_list_lock;
116 
117 static 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  */
127 uint_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  */
135 uint_t force_domaining_disabled;
136 
137 #define	HAS_GEN(x)	(x != MDESC_INVAL_GEN)
138 
139 #ifdef DEBUG
140 static 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
146 static void
147 dump_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 
160 static void
161 print_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  */
186 static machine_descrip_t *
187 md_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 
206 static void
207 md_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 
231 static void
232 md_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  */
246 static machine_descrip_t *
247 new_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  */
266 static void
267 destroy_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  */
290 int
291 mach_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 
446 done:
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 
453 static void *
454 mach_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 
464 static void *
465 mach_descrip_strt_meta_alloc(size_t size)
466 {
467 	return (BOP_ALLOC(bootops, (caddr_t)0, size, PAGESIZE));
468 }
469 
470 static void
471 mach_descrip_strt_meta_free(void *buf, size_t size)
472 {
473 	BOP_FREE(bootops, buf, size);
474 }
475 
476 static void *
477 mach_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 
487 static void
488 mach_descrip_strt_buf_free(void *buf, size_t size)
489 {
490 	prom_free((caddr_t)buf, size);
491 }
492 
493 static void *
494 mach_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  */
506 void
507 mach_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  */
533 void
534 mach_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  */
555 void
556 mach_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  */
579 md_t *
580 md_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  */
604 int
605 md_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 
644 fini:
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  */
654 static void
655 init_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 
682 static void
683 init_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  */
729 caddr_t
730 md_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  */
744 static uint64_t
745 mach_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  */
827 int
828 md_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 
858 void
859 md_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