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