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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/param.h> 29 #include <sys/modctl.h> 30 #include <sys/modhash.h> 31 #include <sys/open.h> 32 #include <sys/conf.h> 33 #include <sys/errno.h> 34 #include <sys/sysmacros.h> 35 #include <sys/kmem.h> 36 #include <sys/cmn_err.h> 37 #include <sys/stat.h> 38 #include <sys/mode.h> 39 #include <sys/pathname.h> 40 #include <sys/vnode.h> 41 #include <sys/ddi_impldefs.h> 42 #include <sys/ddi_implfuncs.h> 43 #include <sys/esunddi.h> 44 #include <sys/sunddi.h> 45 #include <sys/sunndi.h> 46 #include <sys/systeminfo.h> 47 #include <sys/hwconf.h> 48 #include <sys/file.h> 49 #include <sys/varargs.h> 50 #include <sys/thread.h> 51 #include <sys/cred.h> 52 #include <sys/autoconf.h> 53 #include <sys/kobj.h> 54 #include <sys/consdev.h> 55 #include <sys/systm.h> 56 #include <sys/debug.h> 57 #include <sys/atomic.h> 58 59 extern struct dev_ops nodev_ops; 60 extern struct dev_ops mod_nodev_ops; 61 62 struct mod_noload { 63 struct mod_noload *mn_next; 64 char *mn_name; 65 }; 66 67 /* 68 * Function prototypes 69 */ 70 static int init_stubs(struct modctl *, struct mod_modinfo *); 71 static int nm_hash(char *); 72 static void make_syscallname(char *, int); 73 static void hwc_hash_init(); 74 static void hwc_hash(struct hwc_spec *, major_t); 75 static void hwc_unhash(struct hwc_spec *); 76 77 struct dev_ops * 78 mod_hold_dev_by_major(major_t major) 79 { 80 struct dev_ops **devopspp, *ops; 81 int loaded; 82 char *drvname; 83 84 if (major >= devcnt) 85 return (NULL); 86 87 LOCK_DEV_OPS(&(devnamesp[major].dn_lock)); 88 devopspp = &devopsp[major]; 89 loaded = 1; 90 while (loaded && !CB_DRV_INSTALLED(*devopspp)) { 91 UNLOCK_DEV_OPS(&(devnamesp[major].dn_lock)); 92 drvname = mod_major_to_name(major); 93 if (drvname == NULL) 94 return (NULL); 95 loaded = (modload("drv", drvname) != -1); 96 LOCK_DEV_OPS(&(devnamesp[major].dn_lock)); 97 } 98 if (loaded) { 99 INCR_DEV_OPS_REF(*devopspp); 100 ops = *devopspp; 101 } else { 102 ops = NULL; 103 } 104 UNLOCK_DEV_OPS(&(devnamesp[major].dn_lock)); 105 return (ops); 106 } 107 108 #ifdef DEBUG_RELE 109 static int mod_rele_pause = DEBUG_RELE; 110 #endif /* DEBUG_RELE */ 111 112 void 113 mod_rele_dev_by_major(major_t major) 114 { 115 struct dev_ops *ops; 116 struct devnames *dnp; 117 118 if (major >= devcnt) 119 return; 120 121 dnp = &devnamesp[major]; 122 LOCK_DEV_OPS(&dnp->dn_lock); 123 ops = devopsp[major]; 124 ASSERT(CB_DRV_INSTALLED(ops)); 125 126 #ifdef DEBUG_RELE 127 if (!DEV_OPS_HELD(ops)) { 128 char *s; 129 static char *msg = "mod_rele_dev_by_major: unheld driver!"; 130 131 printf("mod_rele_dev_by_major: Major dev <%u>, name <%s>\n", 132 (uint_t)major, 133 (s = mod_major_to_name(major)) ? s : "unknown"); 134 if (mod_rele_pause) 135 debug_enter(msg); 136 else 137 printf("%s\n", msg); 138 UNLOCK_DEV_OPS(&dnp->dn_lock); 139 return; /* XXX: Note changed behavior */ 140 } 141 142 #endif /* DEBUG_RELE */ 143 144 if (!DEV_OPS_HELD(ops)) { 145 cmn_err(CE_PANIC, 146 "mod_rele_dev_by_major: Unheld driver: major number <%u>", 147 (uint_t)major); 148 } 149 DECR_DEV_OPS_REF(ops); 150 UNLOCK_DEV_OPS(&dnp->dn_lock); 151 } 152 153 struct dev_ops * 154 mod_hold_dev_by_devi(dev_info_t *devi) 155 { 156 major_t major; 157 char *name; 158 159 name = ddi_get_name(devi); 160 if ((major = mod_name_to_major(name)) == DDI_MAJOR_T_NONE) 161 return (NULL); 162 return (mod_hold_dev_by_major(major)); 163 } 164 165 void 166 mod_rele_dev_by_devi(dev_info_t *devi) 167 { 168 major_t major; 169 char *name; 170 171 name = ddi_get_name(devi); 172 if ((major = mod_name_to_major(name)) == DDI_MAJOR_T_NONE) 173 return; 174 mod_rele_dev_by_major(major); 175 } 176 177 int 178 nomod_zero() 179 { 180 return (0); 181 } 182 183 int 184 nomod_minus_one() 185 { 186 return (-1); 187 } 188 189 int 190 nomod_einval() 191 { 192 return (EINVAL); 193 } 194 195 void 196 nomod_void() 197 { 198 /* nothing */ 199 } 200 201 /* 202 * Install all the stubs for a module. 203 * Return zero if there were no errors or an errno value. 204 */ 205 int 206 install_stubs_by_name(struct modctl *modp, char *name) 207 { 208 char *p; 209 char *filenamep; 210 char namebuf[MODMAXNAMELEN + 12]; 211 struct mod_modinfo *mp; 212 213 p = name; 214 filenamep = name; 215 216 while (*p) 217 if (*p++ == '/') 218 filenamep = p; 219 220 /* 221 * Concatenate "name" with "_modname" then look up this symbol 222 * in the kernel. If not found, we're done. 223 * If found, then find the "mod" info structure and call init_stubs(). 224 */ 225 p = namebuf; 226 227 while (*filenamep && *filenamep != '.') 228 *p++ = *filenamep++; 229 230 (void) strcpy(p, "_modinfo"); 231 232 if ((mp = (struct mod_modinfo *)modgetsymvalue(namebuf, 1)) != 0) 233 return (init_stubs(modp, mp)); 234 else 235 return (0); 236 } 237 238 static int 239 init_stubs(struct modctl *modp, struct mod_modinfo *mp) 240 { 241 struct mod_stub_info *sp; 242 int i; 243 ulong_t offset; 244 uintptr_t funcadr; 245 char *funcname; 246 247 modp->mod_modinfo = mp; 248 249 /* 250 * Fill in all stubs for this module. We can't be lazy, since 251 * some calls could come in from interrupt level, and we 252 * can't modlookup then (symbols may be paged out). 253 */ 254 sp = mp->modm_stubs; 255 for (i = 0; sp->mods_func_adr; i++, sp++) { 256 funcname = modgetsymname(sp->mods_stub_adr, &offset); 257 if (funcname == NULL) { 258 printf("init_stubs: couldn't find symbol " 259 "in module %s\n", mp->modm_module_name); 260 return (EFAULT); 261 } 262 funcadr = kobj_lookup(modp->mod_mp, funcname); 263 264 if (kobj_addrcheck(modp->mod_mp, (caddr_t)funcadr)) { 265 printf("%s:%s() not defined properly\n", 266 mp->modm_module_name, funcname); 267 return (EFAULT); 268 } 269 sp->mods_func_adr = funcadr; 270 } 271 mp->mp = modp; 272 return (0); 273 } 274 275 /* 276 * modp->mod_modinfo has to be checked in these functions before 277 * mod_stub_info is accessed because it's not guranteed that all 278 * modules define mod_stub_info structures. 279 */ 280 void 281 install_stubs(struct modctl *modp) 282 { 283 struct mod_stub_info *stub; 284 285 if (modp->mod_modinfo) { 286 membar_producer(); 287 for (stub = modp->mod_modinfo->modm_stubs; 288 stub->mods_func_adr; stub++) { 289 stub->mods_flag |= MODS_INSTALLED; 290 } 291 membar_producer(); 292 } 293 } 294 295 void 296 uninstall_stubs(struct modctl *modp) 297 { 298 struct mod_stub_info *stub; 299 300 if (modp->mod_modinfo) { 301 membar_producer(); 302 for (stub = modp->mod_modinfo->modm_stubs; 303 stub->mods_func_adr; stub++) { 304 stub->mods_flag &= ~MODS_INSTALLED; 305 } 306 membar_producer(); 307 } 308 } 309 310 void 311 reset_stubs(struct modctl *modp) 312 { 313 struct mod_stub_info *stub; 314 315 if (modp->mod_modinfo) { 316 for (stub = modp->mod_modinfo->modm_stubs; 317 stub->mods_func_adr; stub++) { 318 if (stub->mods_flag & (MODS_WEAK | MODS_NOUNLOAD)) 319 stub->mods_func_adr = 320 (uintptr_t)stub->mods_errfcn; 321 else 322 stub->mods_func_adr = 323 (uintptr_t)mod_hold_stub; 324 } 325 modp->mod_modinfo->mp = NULL; 326 } 327 } 328 329 struct modctl * 330 mod_getctl(struct modlinkage *modlp) 331 { 332 struct modctl *modp; 333 334 mutex_enter(&mod_lock); 335 modp = &modules; 336 do { 337 if (modp->mod_linkage == modlp) { 338 mutex_exit(&mod_lock); 339 return (modp); 340 } 341 } while ((modp = modp->mod_next) != &modules); 342 mutex_exit(&mod_lock); 343 return (NULL); 344 } 345 346 347 /* 348 * Attach driver.conf info to devnames for a driver 349 */ 350 struct par_list * 351 impl_make_parlist(major_t major) 352 { 353 int err; 354 struct par_list *pl = NULL, *tmp; 355 ddi_prop_t *props = NULL; 356 char *confname, *drvname; 357 struct devnames *dnp; 358 359 dnp = &devnamesp[major]; 360 361 ASSERT(mutex_owned(&dnp->dn_lock)); 362 363 /* 364 * If .conf file already parsed or driver removed, just return. 365 * May return NULL. 366 */ 367 if (dnp->dn_flags & (DN_CONF_PARSED | DN_DRIVER_REMOVED)) 368 return (dnp->dn_pl); 369 370 drvname = mod_major_to_name(major); 371 if (drvname == NULL) 372 return (NULL); 373 374 confname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 375 (void) snprintf(confname, MAXNAMELEN, "drv/%s.conf", drvname); 376 err = hwc_parse(confname, &pl, &props); 377 kmem_free(confname, MAXNAMELEN); 378 if (err) /* file doesn't exist */ 379 return (NULL); 380 381 /* 382 * If there are global properties, reference it from dnp. 383 */ 384 if (props) 385 dnp->dn_global_prop_ptr = i_ddi_prop_list_create(props); 386 387 /* 388 * Hash specs to be looked up by nexus drivers 389 */ 390 tmp = pl; 391 while (tmp) { 392 (void) hwc_hash(tmp->par_specs, major); 393 tmp = tmp->par_next; 394 } 395 396 if (!i_ddi_io_initialized()) { 397 if (i_ddi_prop_search(DDI_DEV_T_ANY, DDI_FORCEATTACH, 398 DDI_PROP_TYPE_INT, &props)) 399 dnp->dn_flags |= DN_FORCE_ATTACH; 400 if (i_ddi_prop_search(DDI_DEV_T_ANY, DDI_OPEN_RETURNS_EINTR, 401 DDI_PROP_TYPE_INT, &props)) 402 dnp->dn_flags |= DN_OPEN_RETURNS_EINTR; 403 if (i_ddi_prop_search(DDI_DEV_T_ANY, "scsi-size-clean", 404 DDI_PROP_TYPE_INT, &props)) 405 dnp->dn_flags |= DN_SCSI_SIZE_CLEAN; 406 } 407 408 if (i_ddi_prop_search(DDI_DEV_T_ANY, DDI_VHCI_CLASS, 409 DDI_PROP_TYPE_STRING, &props)) 410 dnp->dn_flags |= DN_PHCI_DRIVER; 411 412 dnp->dn_flags |= DN_CONF_PARSED; 413 dnp->dn_pl = pl; 414 return (pl); 415 } 416 417 /* 418 * Destroy driver.conf info in devnames array for a driver 419 */ 420 int 421 impl_free_parlist(major_t major) 422 { 423 struct par_list *pl; 424 struct devnames *dnp = &devnamesp[major]; 425 426 /* 427 * Unref driver global property list. Don't destroy it 428 * because some instances may still be referencing it. 429 * The property list will be freed when the last ref 430 * goes away. 431 */ 432 if (dnp->dn_global_prop_ptr) { 433 i_ddi_prop_list_rele(dnp->dn_global_prop_ptr, dnp); 434 dnp->dn_global_prop_ptr = NULL; 435 } 436 437 /* 438 * remove specs from hash table 439 */ 440 for (pl = dnp->dn_pl; pl; pl = pl->par_next) 441 hwc_unhash(pl->par_specs); 442 443 impl_delete_par_list(dnp->dn_pl); 444 dnp->dn_pl = NULL; 445 dnp->dn_flags &= ~DN_CONF_PARSED; 446 return (0); 447 } 448 449 struct bind *mb_hashtab[MOD_BIND_HASHSIZE]; 450 struct bind *sb_hashtab[MOD_BIND_HASHSIZE]; 451 452 static int 453 nm_hash(char *name) 454 { 455 char c; 456 int hash = 0; 457 458 for (c = *name++; c; c = *name++) 459 hash ^= c; 460 461 return (hash & MOD_BIND_HASHMASK); 462 } 463 464 void 465 clear_binding_hash(struct bind **bhash) 466 { 467 int i; 468 struct bind *bp, *bp1; 469 470 for (i = 0; i < MOD_BIND_HASHSIZE; i++) { 471 bp = bhash[i]; 472 while (bp != NULL) { 473 kmem_free(bp->b_name, strlen(bp->b_name) + 1); 474 if (bp->b_bind_name) { 475 kmem_free(bp->b_bind_name, 476 strlen(bp->b_bind_name) + 1); 477 } 478 bp1 = bp; 479 bp = bp->b_next; 480 kmem_free(bp1, sizeof (struct bind)); 481 } 482 bhash[i] = NULL; 483 } 484 } 485 486 static struct bind * 487 find_mbind(char *name, struct bind **hashtab) 488 { 489 int hashndx; 490 struct bind *mb; 491 492 hashndx = nm_hash(name); 493 for (mb = hashtab[hashndx]; mb; mb = mb->b_next) { 494 if (strcmp(name, mb->b_name) == 0) 495 break; 496 } 497 498 return (mb); 499 } 500 501 /* 502 * Create an entry for the given (name, major, bind_name) tuple in the 503 * hash table supplied. Reject the attempt to do so if 'name' is already 504 * in the hash table. 505 * 506 * Does not provide synchronization, so use only during boot or with 507 * externally provided locking. 508 */ 509 int 510 make_mbind(char *name, int major, char *bind_name, struct bind **hashtab) 511 { 512 struct bind *bp; 513 int hashndx; 514 515 ASSERT(hashtab != NULL); 516 517 /* 518 * Fail if the key being added is already in the hash table 519 */ 520 if (find_mbind(name, hashtab) != NULL) 521 return (-1); 522 523 bp = kmem_zalloc(sizeof (struct bind), KM_SLEEP); 524 bp->b_name = kmem_alloc(strlen(name) + 1, KM_SLEEP); 525 (void) strcpy(bp->b_name, name); 526 bp->b_num = major; 527 if (bind_name != NULL) { 528 bp->b_bind_name = kmem_alloc(strlen(bind_name) + 1, KM_SLEEP); 529 (void) strcpy(bp->b_bind_name, bind_name); 530 } 531 hashndx = nm_hash(name); 532 bp->b_next = hashtab[hashndx]; 533 hashtab[hashndx] = bp; 534 535 return (0); 536 } 537 538 /* 539 * Delete a binding from a binding-hash. 540 * 541 * Does not provide synchronization, so use only during boot or with 542 * externally provided locking. 543 */ 544 void 545 delete_mbind(char *name, struct bind **hashtab) 546 { 547 int hashndx; 548 struct bind *b, *bparent = NULL; 549 struct bind *t = NULL; /* target to delete */ 550 551 hashndx = nm_hash(name); 552 553 if (hashtab[hashndx] == NULL) 554 return; 555 556 b = hashtab[hashndx]; 557 if (strcmp(name, b->b_name) == 0) { /* special case first elem. */ 558 hashtab[hashndx] = b->b_next; 559 t = b; 560 } else { 561 for (b = hashtab[hashndx]; b; b = b->b_next) { 562 if (strcmp(name, b->b_name) == 0) { 563 ASSERT(bparent); 564 t = b; 565 bparent->b_next = b->b_next; 566 break; 567 } 568 bparent = b; 569 } 570 } 571 572 if (t != NULL) { /* delete the target */ 573 ASSERT(t->b_name); 574 kmem_free(t->b_name, strlen(t->b_name) + 1); 575 if (t->b_bind_name) 576 kmem_free(t->b_bind_name, strlen(t->b_bind_name) + 1); 577 kmem_free(t, sizeof (struct bind)); 578 } 579 } 580 581 582 major_t 583 mod_name_to_major(char *name) 584 { 585 struct bind *mbind; 586 587 if ((mbind = find_mbind(name, mb_hashtab)) != NULL) 588 return ((major_t)mbind->b_num); 589 590 return (DDI_MAJOR_T_NONE); 591 } 592 593 char * 594 mod_major_to_name(major_t major) 595 { 596 if (major >= devcnt) 597 return (NULL); 598 return ((&devnamesp[major])->dn_name); 599 } 600 601 /* 602 * Set up the devnames array. Error check for duplicate entries. 603 */ 604 void 605 init_devnamesp(int size) 606 { 607 int hshndx; 608 struct bind *bp; 609 static char dupwarn[] = 610 "!Device entry \"%s %d\" conflicts with previous entry \"%s %d\" " 611 "in /etc/name_to_major."; 612 static char badmaj[] = "The major number %u is invalid."; 613 614 ASSERT(size <= L_MAXMAJ32 && size > 0); 615 616 /* 617 * Allocate the devnames array. All mutexes and cv's will be 618 * automagically initialized. 619 */ 620 devnamesp = kobj_zalloc(size * sizeof (struct devnames), KM_SLEEP); 621 622 /* 623 * Stick the contents of mb_hashtab into the devnames array. Warn if 624 * two hash entries correspond to the same major number, or if a 625 * major number is out of range. 626 */ 627 for (hshndx = 0; hshndx < MOD_BIND_HASHSIZE; hshndx++) { 628 for (bp = mb_hashtab[hshndx]; bp; bp = bp->b_next) { 629 if (make_devname(bp->b_name, (major_t)bp->b_num) != 0) { 630 /* 631 * If there is not an entry at b_num already, 632 * then this must be a bad major number. 633 */ 634 char *nm = mod_major_to_name(bp->b_num); 635 if (nm == NULL) { 636 cmn_err(CE_WARN, badmaj, 637 (uint_t)bp->b_num); 638 } else { 639 cmn_err(CE_WARN, dupwarn, bp->b_name, 640 bp->b_num, nm, bp->b_num); 641 } 642 } 643 } 644 } 645 646 /* Initialize hash table for hwc_spec's */ 647 hwc_hash_init(); 648 } 649 650 int 651 make_devname(char *name, major_t major) 652 { 653 struct devnames *dnp; 654 char *copy; 655 656 /* 657 * Until on-disk support for major nums > 14 bits arrives, fail 658 * any major numbers that are too big. 659 */ 660 if (major > L_MAXMAJ32) 661 return (EINVAL); 662 663 dnp = &devnamesp[major]; 664 LOCK_DEV_OPS(&dnp->dn_lock); 665 if (dnp->dn_name) { 666 if (strcmp(dnp->dn_name, name) != 0) { 667 /* Another driver already here */ 668 UNLOCK_DEV_OPS(&dnp->dn_lock); 669 return (EINVAL); 670 } 671 /* Adding back a removed driver */ 672 dnp->dn_flags &= ~DN_DRIVER_REMOVED; 673 UNLOCK_DEV_OPS(&dnp->dn_lock); 674 return (0); 675 } 676 677 /* 678 * Check if flag is taken by getudev() 679 */ 680 if (dnp->dn_flags & DN_TAKEN_GETUDEV) { 681 UNLOCK_DEV_OPS(&dnp->dn_lock); 682 return (EINVAL); 683 } 684 685 copy = kmem_alloc(strlen(name) + 1, KM_SLEEP); 686 (void) strcpy(copy, name); 687 688 /* Make sure string is copied before setting dn_name */ 689 membar_producer(); 690 dnp->dn_name = copy; 691 dnp->dn_flags = 0; 692 UNLOCK_DEV_OPS(&dnp->dn_lock); 693 return (0); 694 } 695 696 /* 697 * Set up the syscallnames array. 698 */ 699 void 700 init_syscallnames(int size) 701 { 702 int hshndx; 703 struct bind *bp; 704 705 syscallnames = kobj_zalloc(size * sizeof (char *), KM_SLEEP); 706 707 for (hshndx = 0; hshndx < MOD_BIND_HASHSIZE; hshndx++) { 708 for (bp = sb_hashtab[hshndx]; bp; bp = bp->b_next) { 709 if (bp->b_num < 0 || bp->b_num >= size) { 710 cmn_err(CE_WARN, 711 "!Couldn't add system call \"%s %d\". " 712 "Value out of range (0..%d) in " 713 "/etc/name_to_sysnum.", 714 bp->b_name, bp->b_num, size - 1); 715 continue; 716 } 717 make_syscallname(bp->b_name, bp->b_num); 718 } 719 } 720 } 721 722 static void 723 make_syscallname(char *name, int sysno) 724 { 725 char **cp = &syscallnames[sysno]; 726 727 if (*cp != NULL) { 728 cmn_err(CE_WARN, "!Couldn't add system call \"%s %d\". " 729 "It conflicts with \"%s %d\" in /etc/name_to_sysnum.", 730 name, sysno, *cp, sysno); 731 return; 732 } 733 *cp = kmem_alloc(strlen(name) + 1, KM_SLEEP); 734 (void) strcpy(*cp, name); 735 } 736 737 /* 738 * Given a system call name, get its number. 739 */ 740 int 741 mod_getsysnum(char *name) 742 { 743 struct bind *mbind; 744 745 if ((mbind = find_mbind(name, sb_hashtab)) != NULL) 746 return (mbind->b_num); 747 748 return (-1); 749 } 750 751 /* 752 * Given a system call number, get the system call name. 753 */ 754 char * 755 mod_getsysname(int sysnum) 756 { 757 return (syscallnames[sysnum]); 758 } 759 760 /* 761 * Find the name of the module containing the specified pc. 762 * Returns the name on success, "<unknown>" on failure. 763 * No mod_lock locking is required because things are never deleted from 764 * the &modules list. 765 */ 766 char * 767 mod_containing_pc(caddr_t pc) 768 { 769 struct modctl *mcp = &modules; 770 771 do { 772 if (mcp->mod_mp != NULL && 773 (size_t)pc - (size_t)mcp->mod_text < mcp->mod_text_size) 774 return (mcp->mod_modname); 775 } while ((mcp = mcp->mod_next) != &modules); 776 return ("<unknown>"); 777 } 778 779 /* 780 * Hash tables for hwc_spec 781 * 782 * The purpose of these hash tables are to allow the framework to discover 783 * all possible .conf children for a given nexus. There are two hash tables. 784 * One is hashed based on parent name, the on the class name. Each 785 * driver.conf file translates to a list of hwc_spec's. Adding and 786 * removing the entire list is an atomic operation, protected by 787 * the hwc_hash_lock. 788 * 789 * What we get from all the hashing is the function hwc_get_child_spec(). 790 */ 791 #define HWC_SPEC_HASHSIZE (1 << 6) /* 64 */ 792 793 static mod_hash_t *hwc_par_hash; /* hash by parent name */ 794 static mod_hash_t *hwc_class_hash; /* hash by class name */ 795 static kmutex_t hwc_hash_lock; /* lock protecting hwc hashes */ 796 797 /* 798 * Initialize hash tables for parent and class specs 799 */ 800 static void 801 hwc_hash_init() 802 { 803 hwc_par_hash = mod_hash_create_strhash("hwc parent spec hash", 804 HWC_SPEC_HASHSIZE, mod_hash_null_valdtor); 805 hwc_class_hash = mod_hash_create_strhash("hwc class spec hash", 806 HWC_SPEC_HASHSIZE, mod_hash_null_valdtor); 807 } 808 809 /* 810 * Insert a spec into hash table. hwc_hash_lock must be held 811 */ 812 static void 813 hwc_hash_insert(struct hwc_spec *spec, char *name, mod_hash_t *hash) 814 { 815 mod_hash_key_t key; 816 struct hwc_spec *entry = NULL; 817 818 ASSERT(name != NULL); 819 820 if (mod_hash_find(hash, (mod_hash_key_t)name, 821 (mod_hash_val_t)&entry) != 0) { 822 /* Name doesn't exist, insert a new key */ 823 key = kmem_alloc(strlen(name) + 1, KM_SLEEP); 824 (void) strcpy((char *)key, name); 825 if (mod_hash_insert(hash, key, (mod_hash_val_t)spec) != 0) { 826 kmem_free(key, strlen(name) + 1); 827 cmn_err(CE_WARN, "hwc hash state inconsistent"); 828 } 829 return; 830 } 831 832 /* 833 * Name is already present, append spec to the list. 834 * This is the case when driver.conf specifies multiple 835 * nodes under a single parent or class. 836 */ 837 while (entry->hwc_hash_next) 838 entry = entry->hwc_hash_next; 839 entry->hwc_hash_next = spec; 840 } 841 842 /* 843 * Remove a spec entry from spec hash table, the spec itself is 844 * destroyed external to this function. 845 */ 846 static void 847 hwc_hash_remove(struct hwc_spec *spec, char *name, mod_hash_t *hash) 848 { 849 char *key; 850 struct hwc_spec *entry; 851 852 ASSERT(name != NULL); 853 854 if (mod_hash_find(hash, (mod_hash_key_t)name, 855 (mod_hash_val_t)&entry) != 0) { 856 return; /* name not found in hash */ 857 } 858 859 /* 860 * If the head is the spec to be removed, either destroy the 861 * entry or replace it with the remaining list. 862 */ 863 if (entry == spec) { 864 if (spec->hwc_hash_next == NULL) { 865 (void) mod_hash_destroy(hash, (mod_hash_key_t)name); 866 return; 867 } 868 key = kmem_alloc(strlen(name) + 1, KM_SLEEP); 869 (void) strcpy(key, name); 870 (void) mod_hash_replace(hash, (mod_hash_key_t)key, 871 (mod_hash_val_t)spec->hwc_hash_next); 872 spec->hwc_hash_next = NULL; 873 return; 874 } 875 876 /* 877 * If the head is not the one, look for the spec in the 878 * hwc_hash_next linkage. 879 */ 880 while (entry->hwc_hash_next && (entry->hwc_hash_next != spec)) 881 entry = entry->hwc_hash_next; 882 883 if (entry->hwc_hash_next) { 884 entry->hwc_hash_next = spec->hwc_hash_next; 885 spec->hwc_hash_next = NULL; 886 } 887 } 888 889 /* 890 * Hash a list of specs based on either parent name or class name 891 */ 892 static void 893 hwc_hash(struct hwc_spec *spec_list, major_t major) 894 { 895 struct hwc_spec *spec = spec_list; 896 897 mutex_enter(&hwc_hash_lock); 898 while (spec) { 899 /* Put driver major here so parent can find it */ 900 spec->hwc_major = major; 901 902 if (spec->hwc_parent_name != NULL) { 903 hwc_hash_insert(spec, spec->hwc_parent_name, 904 hwc_par_hash); 905 } else if (spec->hwc_class_name != NULL) { 906 hwc_hash_insert(spec, spec->hwc_class_name, 907 hwc_class_hash); 908 } else { 909 cmn_err(CE_WARN, 910 "hwc_hash: No class or parent specified"); 911 } 912 spec = spec->hwc_next; 913 } 914 mutex_exit(&hwc_hash_lock); 915 } 916 917 /* 918 * Remove a list of specs from hash tables. Don't destroy the specs yet. 919 */ 920 static void 921 hwc_unhash(struct hwc_spec *spec_list) 922 { 923 struct hwc_spec *spec = spec_list; 924 925 mutex_enter(&hwc_hash_lock); 926 while (spec) { 927 if (spec->hwc_parent_name != NULL) { 928 hwc_hash_remove(spec, spec->hwc_parent_name, 929 hwc_par_hash); 930 } else if (spec->hwc_class_name != NULL) { 931 hwc_hash_remove(spec, spec->hwc_class_name, 932 hwc_class_hash); 933 } else { 934 cmn_err(CE_WARN, 935 "hwc_unhash: No class or parent specified"); 936 } 937 spec = spec->hwc_next; 938 } 939 mutex_exit(&hwc_hash_lock); 940 } 941 942 /* 943 * Make a copy of specs in a hash entry and add to the end of listp. 944 * Called by nexus to locate a list of child specs. 945 * 946 * entry is a list of hwc_spec chained together with hwc_hash_next. 947 * listp points to list chained together with hwc_next. 948 */ 949 static void 950 hwc_spec_add(struct hwc_spec **listp, struct hwc_spec *entry, 951 major_t match_major) 952 { 953 /* Find the tail of the list */ 954 while (*listp) 955 listp = &(*listp)->hwc_next; 956 957 while (entry) { 958 struct hwc_spec *spec; 959 960 if ((match_major != DDI_MAJOR_T_NONE) && 961 (match_major != entry->hwc_major)) { 962 entry = entry->hwc_hash_next; 963 continue; 964 } 965 966 /* 967 * Allocate spec and copy the content of entry. 968 * No need to copy class/parent name since caller 969 * already knows the parent dip. 970 */ 971 spec = kmem_zalloc(sizeof (*spec), KM_SLEEP); 972 spec->hwc_devi_name = i_ddi_strdup( 973 entry->hwc_devi_name, KM_SLEEP); 974 spec->hwc_major = entry->hwc_major; 975 spec->hwc_devi_sys_prop_ptr = i_ddi_prop_list_dup( 976 entry->hwc_devi_sys_prop_ptr, KM_SLEEP); 977 978 *listp = spec; 979 listp = &spec->hwc_next; 980 entry = entry->hwc_hash_next; 981 } 982 } 983 984 /* 985 * Given a dip, find the list of child .conf specs from most specific 986 * (parent pathname) to least specific (class name). 987 * 988 * This function allows top-down loading to be implemented without 989 * changing the format of driver.conf file. 990 */ 991 struct hwc_spec * 992 hwc_get_child_spec(dev_info_t *dip, major_t match_major) 993 { 994 extern char *i_ddi_parname(dev_info_t *, char *); 995 extern int i_ddi_get_exported_classes(dev_info_t *, char ***); 996 extern void i_ddi_free_exported_classes(char **, int); 997 998 int i, nclass; 999 char **classes; 1000 struct hwc_spec *list = NULL; 1001 mod_hash_val_t val; 1002 char *parname, *parname_buf; 1003 char *deviname, *deviname_buf; 1004 char *pathname, *pathname_buf; 1005 char *bindname; 1006 char *drvname; 1007 1008 pathname_buf = kmem_alloc(3 * MAXPATHLEN, KM_SLEEP); 1009 deviname_buf = pathname_buf + MAXPATHLEN; 1010 parname_buf = pathname_buf + (2 * MAXPATHLEN); 1011 1012 mutex_enter(&hwc_hash_lock); 1013 1014 /* 1015 * Lookup based on full path. 1016 * In the case of root node, ddi_pathname would return 1017 * null string so just skip calling it. 1018 * As the pathname always begins with /, no simpler 1019 * name can duplicate it. 1020 */ 1021 pathname = (dip == ddi_root_node()) ? "/" : 1022 ddi_pathname(dip, pathname_buf); 1023 ASSERT(pathname != NULL); 1024 ASSERT(*pathname == '/'); 1025 1026 if (mod_hash_find(hwc_par_hash, (mod_hash_key_t)pathname, &val) == 0) { 1027 hwc_spec_add(&list, (struct hwc_spec *)val, match_major); 1028 } 1029 1030 /* 1031 * Lookup nodename@address. 1032 * Note deviname cannot match pathname. 1033 */ 1034 deviname = ddi_deviname(dip, deviname_buf); 1035 if (*deviname != '\0') { 1036 /* 1037 * Skip leading / returned by ddi_deviname. 1038 */ 1039 ASSERT(*deviname == '/'); 1040 deviname++; 1041 if ((*deviname != '\0') && (mod_hash_find(hwc_par_hash, 1042 (mod_hash_key_t)deviname, &val) == 0)) 1043 hwc_spec_add(&list, 1044 (struct hwc_spec *)val, match_major); 1045 } 1046 1047 /* 1048 * Lookup bindingname@address. 1049 * Take care not to perform duplicate lookups. 1050 */ 1051 parname = i_ddi_parname(dip, parname_buf); 1052 if (*parname != '\0') { 1053 ASSERT(*parname != '/'); 1054 if ((strcmp(parname, deviname) != 0) && 1055 (mod_hash_find(hwc_par_hash, 1056 (mod_hash_key_t)parname, &val) == 0)) { 1057 hwc_spec_add(&list, 1058 (struct hwc_spec *)val, match_major); 1059 } 1060 } 1061 1062 /* 1063 * Lookup driver binding name 1064 */ 1065 bindname = ddi_binding_name(dip); 1066 ASSERT(*bindname != '/'); 1067 if ((strcmp(bindname, parname) != 0) && 1068 (strcmp(bindname, deviname) != 0) && 1069 (mod_hash_find(hwc_par_hash, (mod_hash_key_t)bindname, &val) == 0)) 1070 hwc_spec_add(&list, (struct hwc_spec *)val, match_major); 1071 1072 /* 1073 * Lookup driver name 1074 */ 1075 drvname = (char *)ddi_driver_name(dip); 1076 ASSERT(*drvname != '/'); 1077 if ((strcmp(drvname, bindname) != 0) && 1078 (strcmp(drvname, parname) != 0) && 1079 (strcmp(drvname, deviname) != 0) && 1080 (mod_hash_find(hwc_par_hash, (mod_hash_key_t)drvname, &val) == 0)) 1081 hwc_spec_add(&list, (struct hwc_spec *)val, match_major); 1082 1083 kmem_free(pathname_buf, 3 * MAXPATHLEN); 1084 1085 /* 1086 * Lookup classes exported by this node and lookup the 1087 * class hash table for all .conf specs 1088 */ 1089 nclass = i_ddi_get_exported_classes(dip, &classes); 1090 for (i = 0; i < nclass; i++) { 1091 if (mod_hash_find(hwc_class_hash, (mod_hash_key_t)classes[i], 1092 &val) == 0) 1093 hwc_spec_add(&list, (struct hwc_spec *)val, 1094 match_major); 1095 } 1096 i_ddi_free_exported_classes(classes, nclass); 1097 1098 mutex_exit(&hwc_hash_lock); 1099 return (list); 1100 } 1101