1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Kernel Machine Description (MD) 31 * 32 * The Kernel maintains a global copy of the machine description for 33 * the system. This is for use by all kernel subsystems and is exported 34 * to user applications through the the 'mdesc' device driver. It is 35 * initially copied in from the Hypervisor at boot time, but can be 36 * updated dynamically on demand. The Kernel provides an interface 37 * for consumers to obtain a handle to the global MD. Consumers of the 38 * MD must use the specified interfaces. An update interface is provided 39 * for platform services to intiate an MD update on notification by a 40 * service entity. 41 * 42 * Locks 43 * The current global MD is protected by the curr_mach_descrip_lock. 44 * Each Machine description has a lock to synchornize its ref count. 45 * The Obsolete MD list is protected by the obs_list_lock. 46 */ 47 48 #include <sys/machsystm.h> 49 #include <sys/vm.h> 50 #include <sys/cpu.h> 51 #include <sys/intreg.h> 52 #include <sys/machcpuvar.h> 53 #include <sys/machparam.h> 54 #include <vm/hat_sfmmu.h> 55 #include <vm/seg_kmem.h> 56 #include <sys/error.h> 57 #include <sys/hypervisor_api.h> 58 #include <sys/types.h> 59 #include <sys/sysmacros.h> 60 #include <sys/mdesc.h> 61 #include <sys/mdesc_impl.h> 62 #include <sys/mach_descrip.h> 63 #include <sys/prom_plat.h> 64 #include <sys/promif.h> 65 66 67 static void *mach_descrip_strt_meta_alloc(size_t size); 68 static void mach_descrip_strt_meta_free(void *buf, size_t size); 69 static void *mach_descrip_strt_buf_alloc(size_t size, size_t align); 70 static void mach_descrip_strt_buf_free(void *buf, size_t size); 71 static void *mach_descrip_buf_alloc(size_t size, size_t align); 72 static void *mach_descrip_meta_alloc(size_t size); 73 static uint64_t mach_descrip_find_md_gen(caddr_t ptr); 74 static void init_md_params(void); 75 static void init_domaining_enabled(md_t *mdp, mde_cookie_t *listp); 76 77 /* 78 * Global ptr of the current generation Machine Description 79 */ 80 static machine_descrip_t *curr_mach_descrip; 81 82 /* 83 * Initialized by machine_descrip_startup_init in startup. 84 * machine_descript_init will reintialize the structure with 85 * the vmem allocators once the vmem is available in the boot up 86 * process. 87 */ 88 static machine_descrip_memops_t *curr_mach_descrip_memops = NULL; 89 90 static machine_descrip_memops_t startup_memops = { 91 mach_descrip_strt_buf_alloc, 92 mach_descrip_strt_buf_free, 93 mach_descrip_strt_meta_alloc, 94 mach_descrip_strt_meta_free, 95 }; 96 97 static machine_descrip_memops_t mach_descrip_memops = { 98 mach_descrip_buf_alloc, 99 contig_mem_free, 100 mach_descrip_meta_alloc, 101 kmem_free, 102 }; 103 104 static kmutex_t curr_mach_descrip_lock; 105 /* 106 * List of obsolete Machine Descriptions 107 * Machine descriptions that have users are put on this list 108 * and freed after the last user has called md_fini_handle. 109 */ 110 static machine_descrip_t *obs_machine_descrip_list; 111 112 static kmutex_t obs_list_lock; 113 114 static const char alloc_fail_msg[] = 115 "MD: cannot allocate MD buffer of size %ld bytes\n"; 116 117 /* 118 * Global flag that indicates whether domaining features are 119 * available. The value is set at boot time based on the value 120 * of the 'domaining-enabled' property in the MD and the global 121 * override flag below. Updates to this variable after boot are 122 * not supported. 123 */ 124 uint_t domaining_enabled; 125 126 /* 127 * Global override for the 'domaining_enabled' flag. If this 128 * flag is set in /etc/system, domaining features are disabled, 129 * ignoring the value of the 'domaining-enabled' property in 130 * the MD. 131 */ 132 uint_t force_domaining_disabled; 133 134 #define META_ALLOC_ALIGN 8 135 #define HAS_GEN(x) (x != MDESC_INVAL_GEN) 136 137 #ifdef DEBUG 138 static int mach_descrip_debug = 0; 139 140 #define MDP(ARGS) if (mach_descrip_debug) prom_printf ARGS 141 #define PRINT_LIST() if (mach_descrip_debug) print_obs_list() 142 143 #ifdef MACH_DESC_DEBUG 144 static void 145 dump_buf(uint8_t *bufp, int size) 146 { 147 int i; 148 for (i = 0; i < size; i += 16) { 149 int j; 150 prom_printf("0x%04x :", i); 151 for (j = 0; j < 16 && (i+j) < size; j++) 152 prom_printf(" %02x", bufp[i+j]); 153 prom_printf("\n"); 154 } 155 } 156 #endif /* MACH_DESC_DEBUG */ 157 158 static void 159 print_obs_list(void) 160 { 161 machine_descrip_t *lmdescp; 162 mutex_enter(&obs_list_lock); 163 164 lmdescp = obs_machine_descrip_list; 165 prom_printf("MD_obs_list->"); 166 while (lmdescp != NULL) { 167 prom_printf("g:%ld,r:%d", lmdescp->gen, lmdescp->refcnt); 168 169 lmdescp = lmdescp->next; 170 prom_printf("->"); 171 } 172 prom_printf("NULL\n"); 173 mutex_exit(&obs_list_lock); 174 } 175 176 #else 177 #define MDP(ARGS) 178 #define PRINT_LIST() 179 #endif /* DEBUG */ 180 181 /* 182 * MD obsolete list managment functions 183 */ 184 static machine_descrip_t * 185 md_obs_list_look_up_by_gen(uint64_t gen) 186 { 187 machine_descrip_t *mdescp; 188 189 mutex_enter(&obs_list_lock); 190 mdescp = obs_machine_descrip_list; 191 192 while (mdescp != NULL) { 193 if (mdescp->gen == gen) { 194 mutex_exit(&obs_list_lock); 195 return (mdescp); 196 } 197 mdescp = mdescp->next; 198 } 199 200 mutex_exit(&obs_list_lock); 201 return (mdescp); 202 } 203 204 static void 205 md_obs_list_remove(machine_descrip_t *mdescp) 206 { 207 machine_descrip_t *lmdescp; 208 209 mutex_enter(&obs_list_lock); 210 211 lmdescp = obs_machine_descrip_list; 212 213 if (obs_machine_descrip_list == mdescp) { 214 obs_machine_descrip_list = mdescp->next; 215 } else { 216 while (lmdescp != NULL) { 217 if (lmdescp->next == mdescp) { 218 lmdescp->next = mdescp->next; 219 mdescp->next = NULL; 220 break; 221 } 222 lmdescp = lmdescp->next; 223 } 224 } 225 mutex_exit(&obs_list_lock); 226 PRINT_LIST(); 227 } 228 229 static void 230 md_obs_list_add(machine_descrip_t *mdescp) 231 { 232 mutex_enter(&obs_list_lock); 233 234 mdescp->next = obs_machine_descrip_list; 235 obs_machine_descrip_list = mdescp; 236 237 mutex_exit(&obs_list_lock); 238 PRINT_LIST(); 239 } 240 241 /* 242 * Allocate a machine_descrip meta structure and intitialize it. 243 */ 244 static machine_descrip_t * 245 new_mach_descrip(void) 246 { 247 machine_descrip_t *mdescp; 248 249 mdescp = (machine_descrip_t *)(*curr_mach_descrip_memops->meta_allocp) 250 (sizeof (machine_descrip_t)); 251 if (mdescp != NULL) { 252 bzero(mdescp, sizeof (*mdescp)); 253 mdescp->memops = curr_mach_descrip_memops; 254 mutex_init(&mdescp->lock, NULL, MUTEX_DRIVER, NULL); 255 } 256 257 return (mdescp); 258 } 259 260 /* 261 * Free a machine_descrip meta structure and intitialize it. 262 * Also free the MD buffer. 263 */ 264 static void 265 destroy_machine_descrip(machine_descrip_t *mdescp) 266 { 267 machine_descrip_memops_t *mdesc_memopsp; 268 269 ASSERT((mdescp != NULL)); 270 271 mdesc_memopsp = mdescp->memops; 272 if (mdescp->memops == NULL) 273 panic("destroy_machine_descrip: memops NULL\n"); 274 275 (*mdesc_memopsp->buf_freep)(mdescp->va, mdescp->space); 276 mutex_destroy(&mdescp->lock); 277 (*mdesc_memopsp->meta_freep)(mdescp, sizeof (*mdescp)); 278 } 279 280 /* 281 * Call into the Hypervisor to retrieve the most recent copy of the 282 * machine description. If references to the current MD are active 283 * stow it in the obsolete MD list and update the current MD reference 284 * with the new one. 285 * The obsolete list contains one MD per generation. If the firmware 286 * doesn't support MD generation fail the call. 287 */ 288 int 289 mach_descrip_update(void) 290 { 291 uint64_t md_size0, md_size; 292 uint64_t md_space = 0; 293 uint64_t hvret; 294 caddr_t tbuf = NULL; 295 uint64_t tbuf_pa; 296 uint64_t tgen; 297 int ret = 0; 298 299 MDP(("MD: Requesting buffer size\n")); 300 301 ASSERT((curr_mach_descrip != NULL)); 302 303 mutex_enter(&curr_mach_descrip_lock); 304 305 /* 306 * If the required MD size changes between our first call 307 * to hv_mach_desc (to find the required buf size) and the 308 * second call (to get the actual MD), the MD was in the 309 * process of being updated. Loop until the two sizes are 310 * identical. 311 */ 312 do { 313 if (tbuf != NULL) 314 (*curr_mach_descrip_memops->buf_freep)(tbuf, md_space); 315 316 md_size0 = 0LL; 317 (void) hv_mach_desc((uint64_t)0, &md_size0); 318 MDP(("MD: buffer size is %ld\n", md_size0)); 319 320 /* 321 * Align allocated space to nearest page. 322 * contig_mem_alloc_align() requires a power of 2 alignment. 323 */ 324 md_space = P2ROUNDUP(md_size0, PAGESIZE); 325 MDP(("MD: allocated space is %ld\n", md_space)); 326 327 tbuf = (caddr_t)(*curr_mach_descrip_memops->buf_allocp) 328 (md_space, PAGESIZE); 329 if (tbuf == NULL) { 330 ret = -1; 331 goto done; 332 } 333 334 tbuf_pa = va_to_pa(tbuf); 335 hvret = hv_mach_desc(tbuf_pa, &md_size); 336 MDP(("MD: HV return code = %ld\n", hvret)); 337 338 /* 339 * We get H_EINVAL if our buffer size is too small. In 340 * that case stay in the loop, reallocate the buffer 341 * and try again. 342 */ 343 if (hvret != H_EOK && hvret != H_EINVAL) { 344 MDP(("MD: Failed with code %ld from HV\n", hvret)); 345 ret = -1; 346 goto done; 347 } 348 349 } while (md_size0 != md_size || hvret == H_EINVAL); 350 351 tgen = mach_descrip_find_md_gen(tbuf); 352 353 #ifdef DEBUG 354 if (!HAS_GEN(tgen)) { 355 MDP(("MD: generation number not found\n")); 356 } else 357 MDP(("MD: generation number %ld\n", tgen)); 358 #endif /* DEBUG */ 359 360 if (curr_mach_descrip->va != NULL) { 361 362 /* check for the same generation number */ 363 if (HAS_GEN(tgen) && ((curr_mach_descrip->gen == tgen) && 364 (curr_mach_descrip->size == md_size))) { 365 #ifdef DEBUG 366 /* 367 * Pedantic Check for generation number. If the 368 * generation number is the same, make sure the 369 * MDs are really identical. 370 */ 371 if (bcmp(curr_mach_descrip->va, tbuf, md_size) != 0) { 372 cmn_err(CE_WARN, "machine_descrip_update: MDs " 373 "with the same generation (%ld) are not " 374 "identical", tgen); 375 ret = -1; 376 goto done; 377 } 378 #endif 379 cmn_err(CE_WARN, "machine_descrip_update: new MD has " 380 "the same generation (%ld) as the old MD", tgen); 381 ret = 0; 382 goto done; 383 } 384 385 /* check for generations moving backwards */ 386 if (HAS_GEN(tgen) && HAS_GEN(curr_mach_descrip->gen) && 387 (curr_mach_descrip->gen > tgen)) { 388 cmn_err(CE_WARN, "machine_descrip_update: new MD" 389 " older generation (%ld) than current MD (%ld)", 390 tgen, curr_mach_descrip->gen); 391 ret = -1; 392 goto done; 393 } 394 395 if (curr_mach_descrip->refcnt == 0) { 396 397 MDP(("MD: freeing old md buffer gen %ld\n", 398 curr_mach_descrip->gen)); 399 400 /* Free old space */ 401 ASSERT(curr_mach_descrip->space > 0); 402 403 (*curr_mach_descrip_memops->buf_freep) 404 (curr_mach_descrip->va, curr_mach_descrip->space); 405 } else { 406 if (!HAS_GEN(tgen)) { 407 /* 408 * No update support if FW 409 * doesn't have MD generation id 410 * feature. 411 */ 412 prom_printf("WARNING: F/W does not support MD " 413 "generation count, MD update failed\n"); 414 ret = -1; 415 goto done; 416 } 417 418 MDP(("MD: adding to obs list %ld\n", 419 curr_mach_descrip->gen)); 420 421 md_obs_list_add(curr_mach_descrip); 422 423 curr_mach_descrip = new_mach_descrip(); 424 425 if (curr_mach_descrip == NULL) { 426 panic("Allocation for machine description" 427 " failed\n"); 428 } 429 } 430 } 431 432 curr_mach_descrip->va = tbuf; 433 curr_mach_descrip->gen = tgen; 434 curr_mach_descrip->size = md_size; 435 curr_mach_descrip->space = md_space; 436 437 #ifdef MACH_DESC_DEBUG 438 dump_buf((uint8_t *)curr_mach_descrip->va, md_size); 439 #endif /* MACH_DESC_DEBUG */ 440 441 mutex_exit(&curr_mach_descrip_lock); 442 return (ret); 443 444 done: 445 if (tbuf != NULL) 446 (*curr_mach_descrip_memops->buf_freep)(tbuf, md_space); 447 mutex_exit(&curr_mach_descrip_lock); 448 return (ret); 449 } 450 451 static void * 452 mach_descrip_buf_alloc(size_t size, size_t align) 453 { 454 void *p; 455 456 if ((p = contig_mem_alloc_align(size, align)) == NULL) 457 cmn_err(CE_WARN, alloc_fail_msg, size); 458 459 return (p); 460 } 461 462 static void * 463 mach_descrip_strt_meta_alloc(size_t size) 464 { 465 return (mach_descrip_strt_buf_alloc(size, META_ALLOC_ALIGN)); 466 } 467 468 static void 469 mach_descrip_strt_meta_free(void *buf, size_t size) 470 { 471 mach_descrip_strt_buf_free(buf, size); 472 } 473 474 static void * 475 mach_descrip_strt_buf_alloc(size_t size, size_t align) 476 { 477 void *p = prom_alloc((caddr_t)0, size, align); 478 479 if (p == NULL) 480 prom_printf(alloc_fail_msg, size); 481 482 return (p); 483 } 484 485 static void 486 mach_descrip_strt_buf_free(void *buf, size_t size) 487 { 488 prom_free((caddr_t)buf, size); 489 } 490 491 static void * 492 mach_descrip_meta_alloc(size_t size) 493 { 494 return (kmem_alloc(size, KM_SLEEP)); 495 } 496 497 /* 498 * Initialize the kernel's Machine Description(MD) framework 499 * early on in startup during mlsetup() so consumers 500 * can get to the MD before the VM system has been initialized. 501 * 502 * Also get the most recent version of the MD. 503 */ 504 void 505 mach_descrip_startup_init(void) 506 { 507 508 mutex_init(&curr_mach_descrip_lock, NULL, MUTEX_DRIVER, NULL); 509 mutex_init(&obs_list_lock, NULL, MUTEX_DRIVER, NULL); 510 511 obs_machine_descrip_list = NULL; 512 513 curr_mach_descrip_memops = &startup_memops; 514 515 curr_mach_descrip = new_mach_descrip(); 516 if (curr_mach_descrip == NULL) 517 panic("Allocation for machine description failed\n"); 518 519 if (mach_descrip_update()) 520 panic("Machine description initialization failed\n"); 521 522 } 523 524 /* 525 * Counterpart to the above init function. Free up resources 526 * allocated at startup by mach_descrip_startup_setup(). 527 * And reset machine description framework state. 528 * 529 * All consumers must have fini'ed their handles at this point. 530 */ 531 void 532 mach_descrip_startup_fini(void) 533 { 534 535 ASSERT((curr_mach_descrip != NULL)); 536 ASSERT((curr_mach_descrip->refcnt == 0)); 537 ASSERT((obs_machine_descrip_list == NULL)); 538 539 destroy_machine_descrip(curr_mach_descrip); 540 curr_mach_descrip = NULL; 541 curr_mach_descrip_memops = NULL; 542 } 543 544 /* 545 * Initialize the kernel's Machine Description(MD) framework 546 * after the the VM system has been initialized. 547 * 548 * Also get the most recent version of the MD. 549 * Assumes that the machine description frame work is in a clean 550 * state and the machine description intialized during startup 551 * has been cleaned up and resources deallocated. 552 */ 553 void 554 mach_descrip_init(void) 555 { 556 ASSERT((curr_mach_descrip == NULL && 557 curr_mach_descrip_memops == NULL)); 558 559 curr_mach_descrip_memops = &mach_descrip_memops; 560 561 curr_mach_descrip = new_mach_descrip(); 562 if (curr_mach_descrip == NULL) 563 panic("Allocation for machine description failed\n"); 564 565 if (mach_descrip_update()) 566 panic("Machine description intialization failed\n"); 567 568 /* read in global params */ 569 init_md_params(); 570 } 571 572 /* 573 * Client interface to get a handle to the current MD. 574 * The md_fini_handle() interface should be used to 575 * clean up the refernce to the MD returned by this function. 576 */ 577 md_t * 578 md_get_handle(void) 579 { 580 md_t *mdp; 581 582 mutex_enter(&curr_mach_descrip_lock); 583 584 if (curr_mach_descrip == NULL) { 585 return (NULL); 586 } 587 588 curr_mach_descrip->refcnt++; 589 mdp = md_init_intern(curr_mach_descrip->va, 590 curr_mach_descrip->memops->meta_allocp, 591 curr_mach_descrip->memops->meta_freep); 592 593 mutex_exit(&curr_mach_descrip_lock); 594 595 return (mdp); 596 } 597 598 /* 599 * Client interface to clean up the refernce to the MD returned 600 * by md_get_handle(). 601 */ 602 int 603 md_fini_handle(md_t *ptr) 604 { 605 machine_descrip_t *mdescp; 606 md_impl_t *mdp; 607 608 609 mdp = (md_impl_t *)ptr; 610 611 if (mdp == NULL) 612 return (-1); 613 /* 614 * Check if mdp is current MD gen 615 */ 616 mutex_enter(&curr_mach_descrip_lock); 617 618 if (curr_mach_descrip->gen == mdp->gen) { 619 curr_mach_descrip->refcnt--; 620 mutex_exit(&curr_mach_descrip_lock); 621 goto fini; 622 } 623 mutex_exit(&curr_mach_descrip_lock); 624 625 /* 626 * MD is in the obsolete list 627 */ 628 mdescp = md_obs_list_look_up_by_gen(mdp->gen); 629 if (mdescp == NULL) 630 return (-1); 631 632 mutex_enter(&mdescp->lock); 633 mdescp->refcnt--; 634 if (mdescp->refcnt == 0) { 635 md_obs_list_remove(mdescp); 636 mutex_exit(&mdescp->lock); 637 destroy_machine_descrip(mdescp); 638 goto fini; 639 } 640 mutex_exit(&mdescp->lock); 641 642 fini: 643 return (md_fini(ptr)); 644 } 645 646 /* 647 * General purpose initialization function used to extract parameters 648 * from the MD during the boot process. This is called immediately after 649 * the in kernel copy of the MD has been initialized so that global 650 * flags are available to various subsystems as they get initialized. 651 */ 652 static void 653 init_md_params(void) 654 { 655 md_t *mdp; 656 int num_nodes; 657 mde_cookie_t *listp; 658 int listsz; 659 660 mdp = md_get_handle(); 661 ASSERT(mdp); 662 num_nodes = md_node_count(mdp); 663 ASSERT(num_nodes >= 0); 664 665 listsz = num_nodes * sizeof (mde_cookie_t); 666 listp = (mde_cookie_t *) 667 (*curr_mach_descrip_memops->meta_allocp)(listsz); 668 669 /* 670 * Import various parameters from the MD. For now, 671 * the only parameter of interest is whether or not 672 * domaining features are supported. 673 */ 674 init_domaining_enabled(mdp, listp); 675 676 (*curr_mach_descrip_memops->meta_freep)(listp, listsz); 677 (void) md_fini_handle(mdp); 678 } 679 680 static void 681 init_domaining_enabled(md_t *mdp, mde_cookie_t *listp) 682 { 683 mde_cookie_t rootnode; 684 int num_nodes; 685 uint64_t val = 0; 686 687 /* 688 * If domaining has been manually disabled, always 689 * honor that and ignore the value in the MD. 690 */ 691 if (force_domaining_disabled) { 692 domaining_enabled = 0; 693 MDP(("domaining manually disabled\n")); 694 return; 695 } 696 697 rootnode = md_root_node(mdp); 698 ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE); 699 700 num_nodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, "platform"), 701 md_find_name(mdp, "fwd"), listp); 702 703 /* should only be one platform node */ 704 ASSERT(num_nodes == 1); 705 706 if (md_get_prop_val(mdp, *listp, "domaining-enabled", &val) != 0) { 707 /* 708 * The property is not present. This implies 709 * that the firmware does not support domaining 710 * features. 711 */ 712 MDP(("'domaining-enabled' property not present\n")); 713 714 domaining_enabled = 0; 715 return; 716 } 717 718 domaining_enabled = val; 719 720 MDP(("domaining_enabled = 0x%x\n", domaining_enabled)); 721 } 722 723 /* 724 * Client interface to get a pointer to the raw MD buffer 725 * Private to kernel and mdesc driver. 726 */ 727 caddr_t 728 md_get_md_raw(md_t *ptr) 729 { 730 md_impl_t *mdp; 731 732 mdp = (md_impl_t *)ptr; 733 if (mdp == NULL) 734 return (NULL); 735 return (mdp->caddr); 736 } 737 738 /* 739 * This is called before an MD structure is intialized, so 740 * it walks the raw MD looking for the generation property. 741 */ 742 static uint64_t 743 mach_descrip_find_md_gen(caddr_t ptr) 744 { 745 md_header_t *hdrp; 746 md_element_t *mdep; 747 md_element_t *rootnode = NULL; 748 md_element_t *elem = NULL; 749 char *namep; 750 boolean_t done; 751 int idx; 752 753 hdrp = (md_header_t *)ptr; 754 mdep = (md_element_t *)(ptr + MD_HEADER_SIZE); 755 namep = (char *)(ptr + MD_HEADER_SIZE + hdrp->node_blk_sz); 756 757 /* 758 * Very basic check for alignment to avoid 759 * bus error issues. 760 */ 761 if ((((uint64_t)ptr) & 7) != 0) 762 return (MDESC_INVAL_GEN); 763 764 if (mdtoh32(hdrp->transport_version) != MD_TRANSPORT_VERSION) { 765 return (MDESC_INVAL_GEN); 766 } 767 768 /* 769 * Search for the root node. Perform the walk manually 770 * since the MD structure is not set up yet. 771 */ 772 for (idx = 0, done = B_FALSE; done == B_FALSE; ) { 773 774 md_element_t *np = &(mdep[idx]); 775 776 switch (MDE_TAG(np)) { 777 case MDET_LIST_END: 778 done = B_TRUE; 779 break; 780 781 case MDET_NODE: 782 if (strcmp(namep + MDE_NAME(np), "root") == 0) { 783 /* found root node */ 784 rootnode = np; 785 done = B_TRUE; 786 break; 787 } 788 idx = MDE_PROP_INDEX(np); 789 break; 790 791 default: 792 /* ignore */ 793 idx++; 794 } 795 } 796 797 if (rootnode == NULL) { 798 /* root not found */ 799 return (MDESC_INVAL_GEN); 800 } 801 802 /* search the rootnode for the generation property */ 803 for (elem = (rootnode + 1); MDE_TAG(elem) != MDET_NODE_END; elem++) { 804 805 char *prop_name; 806 807 /* generation field is a prop_val */ 808 if (MDE_TAG(elem) != MDET_PROP_VAL) 809 continue; 810 811 prop_name = namep + MDE_NAME(elem); 812 813 if (strcmp(prop_name, "md-generation#") == 0) { 814 return (MDE_PROP_VALUE(elem)); 815 } 816 } 817 818 return (MDESC_INVAL_GEN); 819 } 820 821 /* 822 * Failed to allocate the list : Return value -1 823 * md_scan_dag API failed : Return the result from md_scan_dag API 824 */ 825 int 826 md_alloc_scan_dag(md_t *ptr, 827 mde_cookie_t startnode, 828 char *node_name, 829 char *dag, 830 mde_cookie_t **list) 831 { 832 int res; 833 md_impl_t *mdp = (md_impl_t *)ptr; 834 835 *list = (mde_cookie_t *)mdp->allocp(sizeof (mde_cookie_t) * 836 mdp->node_count); 837 if (*list == NULL) 838 return (-1); 839 840 res = md_scan_dag(ptr, startnode, 841 md_find_name(ptr, node_name), 842 md_find_name(ptr, dag), *list); 843 844 /* 845 * If md_scan_dag API returned 0 or -1 then free the buffer 846 * and return -1 to indicate the error from this API. 847 */ 848 if (res < 1) { 849 md_free_scan_dag(ptr, list); 850 *list = NULL; 851 } 852 853 return (res); 854 } 855 856 void 857 md_free_scan_dag(md_t *ptr, 858 mde_cookie_t **list) 859 { 860 md_impl_t *mdp = (md_impl_t *)ptr; 861 862 mdp->freep(*list, sizeof (mde_cookie_t) * mdp->node_count); 863 } 864