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