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
dump_buf(uint8_t * bufp,int size)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
print_obs_list(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 *
md_obs_list_look_up_by_gen(uint64_t gen)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
md_obs_list_remove(machine_descrip_t * mdescp)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
md_obs_list_add(machine_descrip_t * mdescp)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 *
new_mach_descrip(void)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
destroy_machine_descrip(machine_descrip_t * mdescp)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
mach_descrip_update(void)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 *
mach_descrip_buf_alloc(size_t size,size_t align)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 *
mach_descrip_strt_meta_alloc(size_t size)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
mach_descrip_strt_meta_free(void * buf,size_t size)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 *
mach_descrip_strt_buf_alloc(size_t size,size_t align)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
mach_descrip_strt_buf_free(void * buf,size_t size)483 mach_descrip_strt_buf_free(void *buf, size_t size)
484 {
485 prom_free((caddr_t)buf, size);
486 }
487
488 static void *
mach_descrip_meta_alloc(size_t size)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
mach_descrip_startup_init(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
mach_descrip_startup_fini(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
mach_descrip_init(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 *
md_get_handle(void)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
md_fini_handle(md_t * ptr)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
init_md_params(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
init_domaining_capabilities(md_t * mdp,mde_cookie_t * listp)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
md_get_md_raw(md_t * ptr)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
mach_descrip_find_md_gen(caddr_t ptr)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
md_alloc_scan_dag(md_t * ptr,mde_cookie_t startnode,char * node_name,char * dag,mde_cookie_t ** list)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
md_free_scan_dag(md_t * ptr,mde_cookie_t ** list)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
md_get_current_gen(void)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