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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright 2017 Nexenta Systems, Inc. 26 * Copyright 2019 Joyent, Inc. 27 */ 28 29 #include <sys/types.h> 30 #include <sys/inttypes.h> 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/user.h> 34 #include <sys/disp.h> 35 #include <sys/conf.h> 36 #include <sys/bootconf.h> 37 #include <sys/sysconf.h> 38 #include <sys/sunddi.h> 39 #include <sys/esunddi.h> 40 #include <sys/ddi_impldefs.h> 41 #include <sys/kmem.h> 42 #include <sys/vmem.h> 43 #include <sys/fs/ufs_fsdir.h> 44 #include <sys/hwconf.h> 45 #include <sys/modctl.h> 46 #include <sys/cmn_err.h> 47 #include <sys/kobj.h> 48 #include <sys/kobj_lex.h> 49 #include <sys/errno.h> 50 #include <sys/debug.h> 51 #include <sys/autoconf.h> 52 #include <sys/callb.h> 53 #include <sys/sysmacros.h> 54 #include <sys/dacf.h> 55 #include <vm/seg_kmem.h> 56 57 struct hwc_class *hcl_head; /* head of list of classes */ 58 static kmutex_t hcl_lock; /* for accessing list of classes */ 59 60 #define DAFILE "/etc/driver_aliases" 61 #define PPTFILE "/etc/ppt_aliases" 62 #define CLASSFILE "/etc/driver_classes" 63 #define DACFFILE "/etc/dacf.conf" 64 65 static char class_file[] = CLASSFILE; 66 static char pptfile[] = PPTFILE; 67 static char dafile[] = DAFILE; 68 static char dacffile[] = DACFFILE; 69 70 char *self_assembly = "/etc/system.d/.self-assembly"; 71 char *systemfile = "/etc/system"; /* name of ascii system file */ 72 73 #define BUILDVERSION_LEN (4096) 74 75 char *versionfile = "/etc/versions/build"; 76 char buildversion[BUILDVERSION_LEN]; 77 78 static struct sysparam *sysparam_hd; /* head of parameters list */ 79 static struct sysparam *sysparam_tl; /* tail of parameters list */ 80 static vmem_t *mod_sysfile_arena; /* parser memory */ 81 82 char obp_bootpath[BO_MAXOBJNAME]; /* bootpath from obp */ 83 84 #if defined(_PSM_MODULES) 85 86 struct psm_mach { 87 struct psm_mach *m_next; 88 char *m_machname; 89 }; 90 91 static struct psm_mach *pmach_head; /* head of list of classes */ 92 93 #define MACHFILE "/etc/mach" 94 static char mach_file[] = MACHFILE; 95 96 #endif /* _PSM_MODULES */ 97 98 #if defined(_RTC_CONFIG) 99 static char rtc_config_file[] = "/etc/rtc_config"; 100 #endif 101 102 static void sys_set_var(int, struct sysparam *, void *); 103 104 static void setparams(void); 105 106 /* 107 * driver.conf parse thread control structure 108 */ 109 struct hwc_parse_mt { 110 ksema_t sema; 111 char *name; /* name of .conf files */ 112 struct par_list **pl; /* parsed parent list */ 113 ddi_prop_t **props; /* parsed properties */ 114 int rv; /* return value */ 115 }; 116 117 static int hwc_parse_now(char *, struct par_list **, ddi_prop_t **); 118 static void hwc_parse_thread(struct hwc_parse_mt *); 119 static struct hwc_parse_mt *hwc_parse_mtalloc(char *, struct par_list **, 120 ddi_prop_t **); 121 static void hwc_parse_mtfree(struct hwc_parse_mt *); 122 static void add_spec(struct hwc_spec *, struct par_list **); 123 static void add_props(struct hwc_spec *, ddi_prop_t **); 124 125 static void check_system_file(void); 126 static int sysparam_compare_entry(struct sysparam *, struct sysparam *); 127 static char *sysparam_type_to_str(int); 128 static void sysparam_count_entry(struct sysparam *, int *, u_longlong_t *); 129 static void sysparam_print_warning(struct sysparam *, u_longlong_t); 130 131 #ifdef DEBUG 132 static int parse_debug_on = 0; 133 134 /*VARARGS1*/ 135 static void 136 parse_debug(struct _buf *file, char *fmt, ...) 137 { 138 va_list adx; 139 140 if (parse_debug_on) { 141 va_start(adx, fmt); 142 vprintf(fmt, adx); 143 if (file) 144 printf(" on line %d of %s\n", kobj_linenum(file), 145 kobj_filename(file)); 146 va_end(adx); 147 } 148 } 149 #endif /* DEBUG */ 150 151 #define FE_BUFLEN 256 152 153 /*PRINTFLIKE3*/ 154 void 155 kobj_file_err(int type, struct _buf *file, char *fmt, ...) 156 { 157 va_list ap; 158 /* 159 * If we're in trouble, we might be short on stack... be paranoid 160 */ 161 char *buf = kmem_alloc(FE_BUFLEN, KM_SLEEP); 162 char *trailer = kmem_alloc(FE_BUFLEN, KM_SLEEP); 163 char *fmt_str = kmem_alloc(FE_BUFLEN, KM_SLEEP); 164 char prefix = '\0'; 165 166 va_start(ap, fmt); 167 if (strchr("^!?", fmt[0]) != NULL) { 168 prefix = fmt[0]; 169 fmt++; 170 } 171 (void) vsnprintf(buf, FE_BUFLEN, fmt, ap); 172 va_end(ap); 173 (void) snprintf(trailer, FE_BUFLEN, " on line %d of %s", 174 kobj_linenum(file), kobj_filename(file)); 175 176 /* 177 * If prefixed with !^?, prepend that character 178 */ 179 if (prefix != '\0') { 180 (void) snprintf(fmt_str, FE_BUFLEN, "%c%%s%%s", prefix); 181 } else { 182 (void) strncpy(fmt_str, "%s%s", FE_BUFLEN); 183 } 184 185 cmn_err(type, fmt_str, buf, trailer); 186 kmem_free(buf, FE_BUFLEN); 187 kmem_free(trailer, FE_BUFLEN); 188 kmem_free(fmt_str, FE_BUFLEN); 189 } 190 191 #ifdef DEBUG 192 char *tokennames[] = { 193 "UNEXPECTED", 194 "EQUALS", 195 "AMPERSAND", 196 "BIT_OR", 197 "STAR", 198 "POUND", 199 "COLON", 200 "SEMICOLON", 201 "COMMA", 202 "SLASH", 203 "WHITE_SPACE", 204 "NEWLINE", 205 "EOF", 206 "STRING", 207 "HEXVAL", 208 "DECVAL", 209 "NAME" 210 }; 211 #endif /* DEBUG */ 212 213 token_t 214 kobj_lex(struct _buf *file, char *val, size_t size) 215 { 216 char *cp; 217 int ch, oval, badquote; 218 size_t remain; 219 token_t token = UNEXPECTED; 220 221 if (size < 2) 222 return (token); /* this token is UNEXPECTED */ 223 224 cp = val; 225 while ((ch = kobj_getc(file)) == ' ' || ch == '\t') 226 ; 227 228 remain = size - 1; 229 *cp++ = (char)ch; 230 switch (ch) { 231 case '=': 232 token = EQUALS; 233 break; 234 case '&': 235 token = AMPERSAND; 236 break; 237 case '|': 238 token = BIT_OR; 239 break; 240 case '*': 241 token = STAR; 242 break; 243 case '#': 244 token = POUND; 245 break; 246 case ':': 247 token = COLON; 248 break; 249 case ';': 250 token = SEMICOLON; 251 break; 252 case ',': 253 token = COMMA; 254 break; 255 case '/': 256 token = SLASH; 257 break; 258 case ' ': 259 case '\t': 260 case '\f': 261 while ((ch = kobj_getc(file)) == ' ' || 262 ch == '\t' || ch == '\f') { 263 if (--remain == 0) { 264 token = UNEXPECTED; 265 goto out; 266 } 267 *cp++ = (char)ch; 268 } 269 (void) kobj_ungetc(file); 270 token = WHITE_SPACE; 271 break; 272 case '\n': 273 case '\r': 274 token = NEWLINE; 275 break; 276 case '"': 277 remain++; 278 cp--; 279 badquote = 0; 280 while (!badquote && (ch = kobj_getc(file)) != '"') { 281 switch (ch) { 282 case '\n': 283 case -1: 284 kobj_file_err(CE_WARN, file, "Missing \""); 285 remain = size - 1; 286 cp = val; 287 *cp++ = '\n'; 288 badquote = 1; 289 /* since we consumed the newline/EOF */ 290 (void) kobj_ungetc(file); 291 break; 292 293 case '\\': 294 if (--remain == 0) { 295 token = UNEXPECTED; 296 goto out; 297 } 298 ch = (char)kobj_getc(file); 299 if (!isdigit(ch)) { 300 /* escape the character */ 301 *cp++ = (char)ch; 302 break; 303 } 304 oval = 0; 305 while (ch >= '0' && ch <= '7') { 306 ch -= '0'; 307 oval = (oval << 3) + ch; 308 ch = (char)kobj_getc(file); 309 } 310 (void) kobj_ungetc(file); 311 /* check for character overflow? */ 312 if (oval > 127) { 313 cmn_err(CE_WARN, 314 "Character " 315 "overflow detected."); 316 } 317 *cp++ = (char)oval; 318 break; 319 default: 320 if (--remain == 0) { 321 token = UNEXPECTED; 322 goto out; 323 } 324 *cp++ = (char)ch; 325 break; 326 } 327 } 328 token = STRING; 329 break; 330 331 case -1: 332 token = EOF; 333 break; 334 335 default: 336 /* 337 * detect a lone '-' (including at the end of a line), and 338 * identify it as a 'name' 339 */ 340 if (ch == '-') { 341 if (--remain == 0) { 342 token = UNEXPECTED; 343 goto out; 344 } 345 *cp++ = (char)(ch = kobj_getc(file)); 346 if (iswhite(ch) || (ch == '\n')) { 347 (void) kobj_ungetc(file); 348 remain++; 349 cp--; 350 token = NAME; 351 break; 352 } 353 } else if (isunary(ch)) { 354 if (--remain == 0) { 355 token = UNEXPECTED; 356 goto out; 357 } 358 *cp++ = (char)(ch = kobj_getc(file)); 359 } 360 361 362 if (isdigit(ch)) { 363 if (ch == '0') { 364 if ((ch = kobj_getc(file)) == 'x') { 365 if (--remain == 0) { 366 token = UNEXPECTED; 367 goto out; 368 } 369 *cp++ = (char)ch; 370 ch = kobj_getc(file); 371 while (isxdigit(ch)) { 372 if (--remain == 0) { 373 token = UNEXPECTED; 374 goto out; 375 } 376 *cp++ = (char)ch; 377 ch = kobj_getc(file); 378 } 379 (void) kobj_ungetc(file); 380 token = HEXVAL; 381 } else { 382 goto digit; 383 } 384 } else { 385 ch = kobj_getc(file); 386 digit: 387 while (isdigit(ch)) { 388 if (--remain == 0) { 389 token = UNEXPECTED; 390 goto out; 391 } 392 *cp++ = (char)ch; 393 ch = kobj_getc(file); 394 } 395 (void) kobj_ungetc(file); 396 token = DECVAL; 397 } 398 } else if (isalpha(ch) || ch == '\\' || ch == '_') { 399 if (ch != '\\') { 400 ch = kobj_getc(file); 401 } else { 402 /* 403 * if the character was a backslash, 404 * back up so we can overwrite it with 405 * the next (i.e. escaped) character. 406 */ 407 remain++; 408 cp--; 409 } 410 while (isnamechar(ch) || ch == '\\') { 411 if (ch == '\\') 412 ch = kobj_getc(file); 413 if (--remain == 0) { 414 token = UNEXPECTED; 415 goto out; 416 } 417 *cp++ = (char)ch; 418 ch = kobj_getc(file); 419 } 420 (void) kobj_ungetc(file); 421 token = NAME; 422 } else { 423 token = UNEXPECTED; 424 } 425 break; 426 } 427 out: 428 *cp = '\0'; 429 430 #ifdef DEBUG 431 /* 432 * The UNEXPECTED token is the first element of the tokennames array, 433 * but its token value is -1. Adjust the value by adding one to it 434 * to change it to an index of the array. 435 */ 436 parse_debug(NULL, "kobj_lex: token %s value '%s'\n", 437 tokennames[token+1], val); 438 #endif 439 return (token); 440 } 441 442 /* 443 * Leave NEWLINE as the next character. 444 */ 445 446 void 447 kobj_find_eol(struct _buf *file) 448 { 449 int ch; 450 451 while ((ch = kobj_getc(file)) != -1) { 452 if (isnewline(ch)) { 453 (void) kobj_ungetc(file); 454 break; 455 } 456 } 457 } 458 459 /* 460 * The ascii system file is read and processed. 461 * 462 * The syntax of commands is as follows: 463 * 464 * '*' in column 1 is a comment line. 465 * <command> : <value> 466 * 467 * command is EXCLUDE, INCLUDE, FORCELOAD, ROOTDEV, ROOTFS, 468 * SWAPDEV, SWAPFS, MODDIR, SET 469 * 470 * value is an ascii string meaningful for the command. 471 */ 472 473 /* 474 * Table of commands 475 */ 476 static struct modcmd modcmd[] = { 477 { "EXCLUDE", MOD_EXCLUDE }, 478 { "exclude", MOD_EXCLUDE }, 479 { "INCLUDE", MOD_INCLUDE }, 480 { "include", MOD_INCLUDE }, 481 { "FORCELOAD", MOD_FORCELOAD }, 482 { "forceload", MOD_FORCELOAD }, 483 { "ROOTDEV", MOD_ROOTDEV }, 484 { "rootdev", MOD_ROOTDEV }, 485 { "ROOTFS", MOD_ROOTFS }, 486 { "rootfs", MOD_ROOTFS }, 487 { "SWAPDEV", MOD_SWAPDEV }, 488 { "swapdev", MOD_SWAPDEV }, 489 { "SWAPFS", MOD_SWAPFS }, 490 { "swapfs", MOD_SWAPFS }, 491 { "MODDIR", MOD_MODDIR }, 492 { "moddir", MOD_MODDIR }, 493 { "SET", MOD_SET }, 494 { "set", MOD_SET }, 495 { "SET32", MOD_SET32 }, 496 { "set32", MOD_SET32 }, 497 { "SET64", MOD_SET64 }, 498 { "set64", MOD_SET64 }, 499 { NULL, MOD_UNKNOWN } 500 }; 501 502 503 static char bad_op[] = "illegal operator '%s' used on a string"; 504 static char colon_err[] = "A colon (:) must follow the '%s' command"; 505 static char tok_err[] = "Unexpected token '%s'"; 506 static char extra_err[] = "extraneous input ignored starting at '%s'"; 507 static char oversize_err[] = "value too long"; 508 509 static struct sysparam * 510 do_sysfile_cmd(struct _buf *file, const char *cmd) 511 { 512 struct sysparam *sysp; 513 struct modcmd *mcp; 514 token_t token, op; 515 char *cp; 516 int ch; 517 char tok1[MOD_MAXPATH + 1]; /* used to read the path set by 'moddir' */ 518 char tok2[64]; 519 520 for (mcp = modcmd; mcp->mc_cmdname != NULL; mcp++) { 521 if (strcmp(mcp->mc_cmdname, cmd) == 0) 522 break; 523 } 524 sysp = vmem_alloc(mod_sysfile_arena, sizeof (struct sysparam), 525 VM_SLEEP); 526 bzero(sysp, sizeof (struct sysparam)); 527 sysp->sys_op = SETOP_NONE; /* set op to noop initially */ 528 529 switch (sysp->sys_type = mcp->mc_type) { 530 case MOD_INCLUDE: 531 case MOD_EXCLUDE: 532 case MOD_FORCELOAD: 533 /* 534 * Are followed by colon. 535 */ 536 case MOD_ROOTFS: 537 case MOD_SWAPFS: 538 if ((token = kobj_lex(file, tok1, sizeof (tok1))) == COLON) { 539 token = kobj_lex(file, tok1, sizeof (tok1)); 540 } else { 541 kobj_file_err(CE_WARN, file, colon_err, cmd); 542 } 543 if (token != NAME) { 544 kobj_file_err(CE_WARN, file, "value expected"); 545 goto bad; 546 } 547 548 cp = tok1 + strlen(tok1); 549 while ((ch = kobj_getc(file)) != -1 && !iswhite(ch) && 550 !isnewline(ch)) { 551 if (cp - tok1 >= sizeof (tok1) - 1) { 552 kobj_file_err(CE_WARN, file, oversize_err); 553 goto bad; 554 } 555 *cp++ = (char)ch; 556 } 557 *cp = '\0'; 558 559 if (ch != -1) 560 (void) kobj_ungetc(file); 561 if (sysp->sys_type == MOD_INCLUDE) 562 return (NULL); 563 sysp->sys_ptr = vmem_alloc(mod_sysfile_arena, strlen(tok1) + 1, 564 VM_SLEEP); 565 (void) strcpy(sysp->sys_ptr, tok1); 566 break; 567 case MOD_SET: 568 case MOD_SET64: 569 case MOD_SET32: 570 { 571 char *var; 572 token_t tok3; 573 574 if (kobj_lex(file, tok1, sizeof (tok1)) != NAME) { 575 kobj_file_err(CE_WARN, file, "value expected"); 576 goto bad; 577 } 578 579 /* 580 * If the next token is a colon (:), 581 * we have the <modname>:<variable> construct. 582 */ 583 if ((token = kobj_lex(file, tok2, sizeof (tok2))) == COLON) { 584 if ((token = kobj_lex(file, tok2, 585 sizeof (tok2))) == NAME) { 586 var = tok2; 587 /* 588 * Save the module name. 589 */ 590 sysp->sys_modnam = vmem_alloc(mod_sysfile_arena, 591 strlen(tok1) + 1, VM_SLEEP); 592 (void) strcpy(sysp->sys_modnam, tok1); 593 op = kobj_lex(file, tok1, sizeof (tok1)); 594 } else { 595 kobj_file_err(CE_WARN, file, "value expected"); 596 goto bad; 597 } 598 } else { 599 /* otherwise, it was the op */ 600 var = tok1; 601 op = token; 602 } 603 /* 604 * kernel param - place variable name in sys_ptr. 605 */ 606 sysp->sys_ptr = vmem_alloc(mod_sysfile_arena, strlen(var) + 1, 607 VM_SLEEP); 608 (void) strcpy(sysp->sys_ptr, var); 609 /* set operation */ 610 switch (op) { 611 case EQUALS: 612 /* simple assignment */ 613 sysp->sys_op = SETOP_ASSIGN; 614 break; 615 case AMPERSAND: 616 /* bitwise AND */ 617 sysp->sys_op = SETOP_AND; 618 break; 619 case BIT_OR: 620 /* bitwise OR */ 621 sysp->sys_op = SETOP_OR; 622 break; 623 default: 624 /* unsupported operation */ 625 kobj_file_err(CE_WARN, file, 626 "unsupported operator %s", tok2); 627 goto bad; 628 } 629 630 switch ((tok3 = kobj_lex(file, tok1, sizeof (tok1)))) { 631 case STRING: 632 /* string variable */ 633 if (sysp->sys_op != SETOP_ASSIGN) { 634 kobj_file_err(CE_WARN, file, bad_op, tok1); 635 goto bad; 636 } 637 if (kobj_get_string(&sysp->sys_info, tok1) == 0) { 638 kobj_file_err(CE_WARN, file, "string garbled"); 639 goto bad; 640 } 641 /* 642 * Set SYSPARAM_STR_TOKEN in sys_flags to notify 643 * sysparam_print_warning() that this is a string 644 * token. 645 */ 646 sysp->sys_flags |= SYSPARAM_STR_TOKEN; 647 break; 648 case HEXVAL: 649 case DECVAL: 650 if (kobj_getvalue(tok1, &sysp->sys_info) == -1) { 651 kobj_file_err(CE_WARN, file, 652 "invalid number '%s'", tok1); 653 goto bad; 654 } 655 656 /* 657 * Set the appropriate flag (hexadecimal or decimal) 658 * in sys_flags for sysparam_print_warning() to be 659 * able to print the number with the correct format. 660 */ 661 if (tok3 == HEXVAL) { 662 sysp->sys_flags |= SYSPARAM_HEX_TOKEN; 663 } else { 664 sysp->sys_flags |= SYSPARAM_DEC_TOKEN; 665 } 666 break; 667 default: 668 kobj_file_err(CE_WARN, file, "bad rvalue '%s'", tok1); 669 goto bad; 670 } /* end switch */ 671 672 /* 673 * Now that we've parsed it to check the syntax, consider 674 * discarding it (because it -doesn't- apply to this flavor 675 * of the kernel) 676 */ 677 #ifdef _LP64 678 if (sysp->sys_type == MOD_SET32) 679 return (NULL); 680 #else 681 if (sysp->sys_type == MOD_SET64) 682 return (NULL); 683 #endif 684 sysp->sys_type = MOD_SET; 685 break; 686 } 687 case MOD_MODDIR: 688 if ((token = kobj_lex(file, tok1, sizeof (tok1))) != COLON) { 689 kobj_file_err(CE_WARN, file, colon_err, cmd); 690 goto bad; 691 } 692 693 cp = tok1; 694 while ((token = kobj_lex(file, cp, 695 sizeof (tok1) - (cp - tok1))) != NEWLINE && token != EOF) { 696 if (token == -1) { 697 kobj_file_err(CE_WARN, file, oversize_err); 698 goto bad; 699 } 700 cp += strlen(cp); 701 while ((ch = kobj_getc(file)) != -1 && !iswhite(ch) && 702 !isnewline(ch) && ch != ':') { 703 if (cp - tok1 >= sizeof (tok1) - 1) { 704 kobj_file_err(CE_WARN, file, 705 oversize_err); 706 goto bad; 707 } 708 *cp++ = (char)ch; 709 } 710 *cp++ = ' '; 711 if (isnewline(ch)) { 712 cp--; 713 (void) kobj_ungetc(file); 714 } 715 } 716 (void) kobj_ungetc(file); 717 *cp = '\0'; 718 sysp->sys_ptr = vmem_alloc(mod_sysfile_arena, strlen(tok1) + 1, 719 VM_SLEEP); 720 (void) strcpy(sysp->sys_ptr, tok1); 721 break; 722 723 case MOD_SWAPDEV: 724 case MOD_ROOTDEV: 725 if ((token = kobj_lex(file, tok1, sizeof (tok1))) != COLON) { 726 kobj_file_err(CE_WARN, file, colon_err, cmd); 727 goto bad; 728 } 729 while ((ch = kobj_getc(file)) == ' ' || ch == '\t') 730 ; 731 cp = tok1; 732 while (!iswhite(ch) && !isnewline(ch) && ch != -1) { 733 if (cp - tok1 >= sizeof (tok1) - 1) { 734 kobj_file_err(CE_WARN, file, oversize_err); 735 goto bad; 736 } 737 738 *cp++ = (char)ch; 739 ch = kobj_getc(file); 740 } 741 if (ch != -1) 742 (void) kobj_ungetc(file); 743 *cp = '\0'; 744 745 sysp->sys_ptr = vmem_alloc(mod_sysfile_arena, strlen(tok1) + 1, 746 VM_SLEEP); 747 (void) strcpy(sysp->sys_ptr, tok1); 748 break; 749 750 case MOD_UNKNOWN: 751 default: 752 kobj_file_err(CE_WARN, file, "unknown command '%s'", cmd); 753 goto bad; 754 } 755 756 return (sysp); 757 758 bad: 759 kobj_find_eol(file); 760 return (NULL); 761 } 762 763 static void 764 read_system_file(char *name) 765 { 766 register struct sysparam *sp; 767 register struct _buf *file; 768 register token_t token, last_tok; 769 char tokval[MAXLINESIZE]; 770 771 if ((file = kobj_open_file(name)) == 772 (struct _buf *)-1) { 773 if (strcmp(name, systemfile) == 0) 774 cmn_err(CE_WARN, "cannot open system file: %s", 775 name); 776 } else { 777 if (sysparam_tl == NULL) 778 sysparam_tl = (struct sysparam *)&sysparam_hd; 779 780 last_tok = NEWLINE; 781 while ((token = kobj_lex(file, tokval, 782 sizeof (tokval))) != EOF) { 783 switch (token) { 784 case STAR: 785 case POUND: 786 /* 787 * Skip comments. 788 */ 789 kobj_find_eol(file); 790 break; 791 case NEWLINE: 792 kobj_newline(file); 793 last_tok = NEWLINE; 794 break; 795 case NAME: 796 if (last_tok != NEWLINE) { 797 kobj_file_err(CE_WARN, file, 798 extra_err, tokval); 799 kobj_find_eol(file); 800 } else if ((sp = do_sysfile_cmd(file, 801 tokval)) != NULL) { 802 sp->sys_next = NULL; 803 sysparam_tl->sys_next = sp; 804 sysparam_tl = sp; 805 } 806 last_tok = NAME; 807 break; 808 default: 809 kobj_file_err(CE_WARN, 810 file, tok_err, tokval); 811 kobj_find_eol(file); 812 break; 813 } 814 } 815 kobj_close_file(file); 816 } 817 } 818 819 void 820 mod_read_system_file(int ask) 821 { 822 struct _buf *file; 823 824 mod_sysfile_arena = vmem_create("mod_sysfile", NULL, 0, 8, 825 segkmem_alloc, segkmem_free, heap_arena, 0, VM_SLEEP); 826 827 if (ask) 828 mod_askparams(); 829 830 /* 831 * Read the user self-assembly file first 832 * to preserve existing system settings. 833 */ 834 if (self_assembly != NULL) 835 read_system_file(self_assembly); 836 837 if (systemfile != NULL) 838 read_system_file(systemfile); 839 840 /* 841 * Sanity check of /etc/system. 842 */ 843 check_system_file(); 844 845 param_preset(); 846 (void) mod_sysctl(SYS_SET_KVAR, NULL); 847 param_check(); 848 849 if (ask == 0) 850 setparams(); 851 852 /* 853 * A convenient place to read in our build version string. 854 */ 855 856 if ((file = kobj_open_file(versionfile)) != (struct _buf *)-1) { 857 if (kobj_read_file(file, buildversion, 858 sizeof (buildversion) - 1, 0) == -1) { 859 cmn_err(CE_WARN, "failed to read %s\n", versionfile); 860 } 861 kobj_close_file(file); 862 } 863 } 864 865 /* 866 * Search for a specific module variable assignment in /etc/system. If 867 * successful, 1 is returned and the value is stored in '*value'. 868 * Otherwise 0 is returned and '*value' isn't modified. If 'module' is 869 * NULL we look for global definitions. 870 * 871 * This is useful if the value of an assignment is needed before a 872 * module is loaded (e.g. to obtain a default privileged rctl limit). 873 */ 874 int 875 mod_sysvar(const char *module, const char *name, u_longlong_t *value) 876 { 877 struct sysparam *sysp; 878 int cnt = 0; /* dummy */ 879 880 ASSERT(name != NULL); 881 ASSERT(value != NULL); 882 for (sysp = sysparam_hd; sysp != NULL; sysp = sysp->sys_next) { 883 884 if ((sysp->sys_type == MOD_SET) && 885 (((module == NULL) && (sysp->sys_modnam == NULL)) || 886 ((module != NULL) && (sysp->sys_modnam != NULL) && 887 (strcmp(module, sysp->sys_modnam) == 0)))) { 888 889 ASSERT(sysp->sys_ptr != NULL); 890 891 if (strcmp(name, sysp->sys_ptr) == 0) { 892 sysparam_count_entry(sysp, &cnt, value); 893 if ((sysp->sys_flags & SYSPARAM_TERM) != 0) 894 return (1); 895 continue; 896 } 897 } 898 } 899 ASSERT(cnt == 0); 900 return (0); 901 } 902 903 /* 904 * This function scans sysparam records, which are created from the 905 * contents of /etc/system, for entries which are logical duplicates, 906 * and prints warning messages as appropriate. When multiple "set" 907 * commands are encountered, the pileup of values with "&", "|" 908 * and "=" operators results in the final value. 909 */ 910 static void 911 check_system_file(void) 912 { 913 struct sysparam *sysp; 914 915 for (sysp = sysparam_hd; sysp != NULL; sysp = sysp->sys_next) { 916 struct sysparam *entry, *final; 917 u_longlong_t value = 0; 918 int cnt = 1; 919 /* 920 * If the entry is already checked, skip it. 921 */ 922 if ((sysp->sys_flags & SYSPARAM_DUP) != 0) 923 continue; 924 /* 925 * Check if there is a duplicate entry by doing a linear 926 * search. 927 */ 928 final = sysp; 929 for (entry = sysp->sys_next; entry != NULL; 930 entry = entry->sys_next) { 931 /* 932 * Check the entry. if it's different, skip this. 933 */ 934 if (sysparam_compare_entry(sysp, entry) != 0) 935 continue; 936 /* 937 * Count the entry and put the mark. 938 */ 939 sysparam_count_entry(entry, &cnt, &value); 940 entry->sys_flags |= SYSPARAM_DUP; 941 final = entry; 942 } 943 final->sys_flags |= SYSPARAM_TERM; 944 /* 945 * Print the warning if it's duplicated. 946 */ 947 if (cnt >= 2) 948 sysparam_print_warning(final, value); 949 } 950 } 951 952 /* 953 * Compare the sysparam records. 954 * Return 0 if they are the same, return 1 if not. 955 */ 956 static int 957 sysparam_compare_entry(struct sysparam *sysp, struct sysparam *entry) 958 { 959 ASSERT(sysp->sys_ptr != NULL && entry->sys_ptr != NULL); 960 961 /* 962 * If the command is rootdev, rootfs, swapdev, swapfs or moddir, 963 * the record with the same type is treated as a duplicate record. 964 * In other cases, the record is treated as a duplicate record when 965 * its type, its module name (if it exists), and its variable name 966 * are the same. 967 */ 968 switch (sysp->sys_type) { 969 case MOD_ROOTDEV: 970 case MOD_ROOTFS: 971 case MOD_SWAPDEV: 972 case MOD_SWAPFS: 973 case MOD_MODDIR: 974 return (sysp->sys_type == entry->sys_type ? 0 : 1); 975 default: /* In other cases, just go through it. */ 976 break; 977 } 978 979 if (sysp->sys_type != entry->sys_type) 980 return (1); 981 982 if (sysp->sys_modnam != NULL && entry->sys_modnam == NULL) 983 return (1); 984 985 if (sysp->sys_modnam == NULL && entry->sys_modnam != NULL) 986 return (1); 987 988 if (sysp->sys_modnam != NULL && entry->sys_modnam != NULL && 989 strcmp(sysp->sys_modnam, entry->sys_modnam) != 0) 990 return (1); 991 992 return (strcmp(sysp->sys_ptr, entry->sys_ptr)); 993 } 994 995 /* 996 * Translate a sysparam type value to a string. 997 */ 998 static char * 999 sysparam_type_to_str(int type) 1000 { 1001 struct modcmd *mcp; 1002 1003 for (mcp = modcmd; mcp->mc_cmdname != NULL; mcp++) { 1004 if (mcp->mc_type == type) 1005 break; 1006 } 1007 ASSERT(mcp->mc_type == type); 1008 1009 if (type != MOD_UNKNOWN) 1010 return ((++mcp)->mc_cmdname); /* lower case */ 1011 else 1012 return (""); /* MOD_UNKNOWN */ 1013 } 1014 1015 /* 1016 * Check the entry and accumulate the number of entries. 1017 */ 1018 static void 1019 sysparam_count_entry(struct sysparam *sysp, int *cnt, u_longlong_t *value) 1020 { 1021 u_longlong_t ul = sysp->sys_info; 1022 1023 switch (sysp->sys_op) { 1024 case SETOP_ASSIGN: 1025 *value = ul; 1026 (*cnt)++; 1027 return; 1028 case SETOP_AND: 1029 *value &= ul; 1030 return; 1031 case SETOP_OR: 1032 *value |= ul; 1033 return; 1034 default: /* Not MOD_SET */ 1035 (*cnt)++; 1036 return; 1037 } 1038 } 1039 1040 /* 1041 * Print out the warning if multiple entries are found in the system file. 1042 */ 1043 static void 1044 sysparam_print_warning(struct sysparam *sysp, u_longlong_t value) 1045 { 1046 char *modnam = sysp->sys_modnam; 1047 char *varnam = sysp->sys_ptr; 1048 int type = sysp->sys_type; 1049 char *typenam = sysparam_type_to_str(type); 1050 boolean_t str_token = ((sysp->sys_flags & SYSPARAM_STR_TOKEN) != 0); 1051 boolean_t hex_number = ((sysp->sys_flags & SYSPARAM_HEX_TOKEN) != 0); 1052 #define warn_format1 " is set more than once in /%s. " 1053 #define warn_format2 " applied as the current setting.\n" 1054 1055 ASSERT(varnam != NULL); 1056 1057 if (type == MOD_SET) { 1058 /* 1059 * If a string token is set, print out the string 1060 * instead of its pointer value. In other cases, 1061 * print out the value with the appropriate format 1062 * for a hexadecimal number or a decimal number. 1063 */ 1064 if (modnam == NULL) { 1065 if (str_token == B_TRUE) { 1066 cmn_err(CE_WARN, "%s" warn_format1 1067 "\"%s %s = %s\"" warn_format2, 1068 varnam, systemfile, typenam, 1069 varnam, (char *)(uintptr_t)value); 1070 } else if (hex_number == B_TRUE) { 1071 cmn_err(CE_WARN, "%s" warn_format1 1072 "\"%s %s = 0x%llx\"" warn_format2, 1073 varnam, systemfile, typenam, 1074 varnam, value); 1075 } else { 1076 cmn_err(CE_WARN, "%s" warn_format1 1077 "\"%s %s = %lld\"" warn_format2, 1078 varnam, systemfile, typenam, 1079 varnam, value); 1080 } 1081 } else { 1082 if (str_token == B_TRUE) { 1083 cmn_err(CE_WARN, "%s:%s" warn_format1 1084 "\"%s %s:%s = %s\"" warn_format2, 1085 modnam, varnam, systemfile, 1086 typenam, modnam, varnam, 1087 (char *)(uintptr_t)value); 1088 } else if (hex_number == B_TRUE) { 1089 cmn_err(CE_WARN, "%s:%s" warn_format1 1090 "\"%s %s:%s = 0x%llx\"" warn_format2, 1091 modnam, varnam, systemfile, 1092 typenam, modnam, varnam, value); 1093 } else { 1094 cmn_err(CE_WARN, "%s:%s" warn_format1 1095 "\"%s %s:%s = %lld\"" warn_format2, 1096 modnam, varnam, systemfile, 1097 typenam, modnam, varnam, value); 1098 } 1099 } 1100 } else { 1101 /* 1102 * If the type is MOD_ROOTDEV, MOD_ROOTFS, MOD_SWAPDEV, 1103 * MOD_SWAPFS or MOD_MODDIR, the entry is treated as 1104 * a duplicate one if it has the same type regardless 1105 * of its variable name. 1106 */ 1107 switch (type) { 1108 case MOD_ROOTDEV: 1109 case MOD_ROOTFS: 1110 case MOD_SWAPDEV: 1111 case MOD_SWAPFS: 1112 case MOD_MODDIR: 1113 cmn_err(CE_WARN, "\"%s\" appears more than once " 1114 "in /%s.", typenam, systemfile); 1115 break; 1116 default: 1117 cmn_err(CE_NOTE, "\"%s: %s\" appears more than once " 1118 "in /%s.", typenam, varnam, systemfile); 1119 break; 1120 } 1121 } 1122 } 1123 1124 /* 1125 * Process the system file commands. 1126 */ 1127 int 1128 mod_sysctl(int fcn, void *p) 1129 { 1130 static char wmesg[] = "forceload of %s failed"; 1131 struct sysparam *sysp; 1132 char *name; 1133 struct modctl *modp; 1134 1135 if (sysparam_hd == NULL) 1136 return (0); 1137 1138 for (sysp = sysparam_hd; sysp != NULL; sysp = sysp->sys_next) { 1139 1140 switch (fcn) { 1141 1142 case SYS_FORCELOAD: 1143 if (sysp->sys_type == MOD_FORCELOAD) { 1144 name = sysp->sys_ptr; 1145 if (modload(NULL, name) == -1) 1146 cmn_err(CE_WARN, wmesg, name); 1147 /* 1148 * The following works because it 1149 * runs before autounloading is started!! 1150 */ 1151 modp = mod_find_by_filename(NULL, name); 1152 if (modp != NULL) 1153 modp->mod_loadflags |= MOD_NOAUTOUNLOAD; 1154 /* 1155 * For drivers, attempt to install it. 1156 */ 1157 if (strncmp(sysp->sys_ptr, "drv", 3) == 0) { 1158 (void) ddi_install_driver(name + 4); 1159 } 1160 } 1161 break; 1162 1163 case SYS_SET_KVAR: 1164 case SYS_SET_MVAR: 1165 if (sysp->sys_type == MOD_SET) 1166 sys_set_var(fcn, sysp, p); 1167 break; 1168 1169 case SYS_CHECK_EXCLUDE: 1170 if (sysp->sys_type == MOD_EXCLUDE) { 1171 if (p == NULL || sysp->sys_ptr == NULL) 1172 return (0); 1173 if (strcmp((char *)p, sysp->sys_ptr) == 0) 1174 return (1); 1175 } 1176 } 1177 } 1178 1179 return (0); 1180 } 1181 1182 /* 1183 * Process the system file commands, by type. 1184 */ 1185 int 1186 mod_sysctl_type(int type, int (*func)(struct sysparam *, void *), void *p) 1187 { 1188 struct sysparam *sysp; 1189 int err; 1190 1191 for (sysp = sysparam_hd; sysp != NULL; sysp = sysp->sys_next) 1192 if (sysp->sys_type == type) 1193 if (err = (*(func))(sysp, p)) 1194 return (err); 1195 return (0); 1196 } 1197 1198 1199 static char seterr[] = "Symbol %s has size of 0 in symbol table. %s"; 1200 static char assumption[] = "Assuming it is an 'int'"; 1201 static char defmsg[] = "Trying to set a variable that is of size %d"; 1202 1203 static void set_int8_var(uintptr_t, struct sysparam *); 1204 static void set_int16_var(uintptr_t, struct sysparam *); 1205 static void set_int32_var(uintptr_t, struct sysparam *); 1206 static void set_int64_var(uintptr_t, struct sysparam *); 1207 1208 static void 1209 sys_set_var(int fcn, struct sysparam *sysp, void *p) 1210 { 1211 uintptr_t symaddr; 1212 int size; 1213 1214 if (fcn == SYS_SET_KVAR && sysp->sys_modnam == NULL) { 1215 symaddr = kobj_getelfsym(sysp->sys_ptr, NULL, &size); 1216 } else if (fcn == SYS_SET_MVAR) { 1217 if (sysp->sys_modnam == (char *)NULL || 1218 strcmp(((struct modctl *)p)->mod_modname, 1219 sysp->sys_modnam) != 0) 1220 return; 1221 symaddr = kobj_getelfsym(sysp->sys_ptr, 1222 ((struct modctl *)p)->mod_mp, &size); 1223 } else 1224 return; 1225 1226 if (symaddr != (uintptr_t)NULL) { 1227 switch (size) { 1228 case 1: 1229 set_int8_var(symaddr, sysp); 1230 break; 1231 case 2: 1232 set_int16_var(symaddr, sysp); 1233 break; 1234 case 0: 1235 cmn_err(CE_WARN, seterr, sysp->sys_ptr, assumption); 1236 /*FALLTHROUGH*/ 1237 case 4: 1238 set_int32_var(symaddr, sysp); 1239 break; 1240 case 8: 1241 set_int64_var(symaddr, sysp); 1242 break; 1243 default: 1244 cmn_err(CE_WARN, defmsg, size); 1245 break; 1246 } 1247 } else { 1248 printf("sorry, variable '%s' is not defined in the '%s' ", 1249 sysp->sys_ptr, 1250 sysp->sys_modnam ? sysp->sys_modnam : "kernel"); 1251 if (sysp->sys_modnam) 1252 printf("module"); 1253 printf("\n"); 1254 } 1255 } 1256 1257 static void 1258 set_int8_var(uintptr_t symaddr, struct sysparam *sysp) 1259 { 1260 uint8_t uc = (uint8_t)sysp->sys_info; 1261 1262 if (moddebug & MODDEBUG_LOADMSG) 1263 printf("OP: %x: param '%s' was '0x%" PRIx8 1264 "' in module: '%s'.\n", sysp->sys_op, sysp->sys_ptr, 1265 *(uint8_t *)symaddr, sysp->sys_modnam); 1266 1267 switch (sysp->sys_op) { 1268 case SETOP_ASSIGN: 1269 *(uint8_t *)symaddr = uc; 1270 break; 1271 case SETOP_AND: 1272 *(uint8_t *)symaddr &= uc; 1273 break; 1274 case SETOP_OR: 1275 *(uint8_t *)symaddr |= uc; 1276 break; 1277 } 1278 1279 if (moddebug & MODDEBUG_LOADMSG) 1280 printf("now it is set to '0x%" PRIx8 "'.\n", 1281 *(uint8_t *)symaddr); 1282 } 1283 1284 static void 1285 set_int16_var(uintptr_t symaddr, struct sysparam *sysp) 1286 { 1287 uint16_t us = (uint16_t)sysp->sys_info; 1288 1289 if (moddebug & MODDEBUG_LOADMSG) 1290 printf("OP: %x: param '%s' was '0x%" PRIx16 1291 "' in module: '%s'.\n", sysp->sys_op, sysp->sys_ptr, 1292 *(uint16_t *)symaddr, sysp->sys_modnam); 1293 1294 switch (sysp->sys_op) { 1295 case SETOP_ASSIGN: 1296 *(uint16_t *)symaddr = us; 1297 break; 1298 case SETOP_AND: 1299 *(uint16_t *)symaddr &= us; 1300 break; 1301 case SETOP_OR: 1302 *(uint16_t *)symaddr |= us; 1303 break; 1304 } 1305 1306 if (moddebug & MODDEBUG_LOADMSG) 1307 printf("now it is set to '0x%" PRIx16 "'.\n", 1308 *(uint16_t *)symaddr); 1309 } 1310 1311 static void 1312 set_int32_var(uintptr_t symaddr, struct sysparam *sysp) 1313 { 1314 uint32_t ui = (uint32_t)sysp->sys_info; 1315 1316 if (moddebug & MODDEBUG_LOADMSG) 1317 printf("OP: %x: param '%s' was '0x%" PRIx32 1318 "' in module: '%s'.\n", sysp->sys_op, sysp->sys_ptr, 1319 *(uint32_t *)symaddr, sysp->sys_modnam); 1320 1321 switch (sysp->sys_op) { 1322 case SETOP_ASSIGN: 1323 *(uint32_t *)symaddr = ui; 1324 break; 1325 case SETOP_AND: 1326 *(uint32_t *)symaddr &= ui; 1327 break; 1328 case SETOP_OR: 1329 *(uint32_t *)symaddr |= ui; 1330 break; 1331 } 1332 1333 if (moddebug & MODDEBUG_LOADMSG) 1334 printf("now it is set to '0x%" PRIx32 "'.\n", 1335 *(uint32_t *)symaddr); 1336 } 1337 1338 static void 1339 set_int64_var(uintptr_t symaddr, struct sysparam *sysp) 1340 { 1341 uint64_t ul = sysp->sys_info; 1342 1343 if (moddebug & MODDEBUG_LOADMSG) 1344 printf("OP: %x: param '%s' was '0x%" PRIx64 1345 "' in module: '%s'.\n", sysp->sys_op, sysp->sys_ptr, 1346 *(uint64_t *)symaddr, sysp->sys_modnam); 1347 1348 switch (sysp->sys_op) { 1349 case SETOP_ASSIGN: 1350 *(uint64_t *)symaddr = ul; 1351 break; 1352 case SETOP_AND: 1353 *(uint64_t *)symaddr &= ul; 1354 break; 1355 case SETOP_OR: 1356 *(uint64_t *)symaddr |= ul; 1357 break; 1358 } 1359 1360 if (moddebug & MODDEBUG_LOADMSG) 1361 printf("now it is set to '0x%" PRIx64 "'.\n", 1362 *(uint64_t *)symaddr); 1363 } 1364 1365 /* 1366 * The next item on the line is a string value. Allocate memory for 1367 * it and copy the string. Return 1, and set arg ptr to newly allocated 1368 * and initialized buffer, or NULL if an error occurs. 1369 */ 1370 int 1371 kobj_get_string(u_longlong_t *llptr, char *tchar) 1372 { 1373 char *cp; 1374 char *start = (char *)0; 1375 int len = 0; 1376 1377 len = strlen(tchar); 1378 start = tchar; 1379 /* copy string */ 1380 cp = vmem_alloc(mod_sysfile_arena, len + 1, VM_SLEEP); 1381 bzero(cp, len + 1); 1382 *llptr = (u_longlong_t)(uintptr_t)cp; 1383 for (; len > 0; len--) { 1384 /* convert some common escape sequences */ 1385 if (*start == '\\') { 1386 switch (*(start + 1)) { 1387 case 't': 1388 /* tab */ 1389 *cp++ = '\t'; 1390 len--; 1391 start += 2; 1392 break; 1393 case 'n': 1394 /* new line */ 1395 *cp++ = '\n'; 1396 len--; 1397 start += 2; 1398 break; 1399 case 'b': 1400 /* back space */ 1401 *cp++ = '\b'; 1402 len--; 1403 start += 2; 1404 break; 1405 default: 1406 /* simply copy it */ 1407 *cp++ = *start++; 1408 break; 1409 } 1410 } else 1411 *cp++ = *start++; 1412 } 1413 *cp = '\0'; 1414 return (1); 1415 } 1416 1417 1418 /* 1419 * this function frees the memory allocated by kobj_get_string 1420 */ 1421 void 1422 kobj_free_string(void *ptr, int len) 1423 { 1424 vmem_free(mod_sysfile_arena, ptr, len); 1425 } 1426 1427 1428 /* 1429 * get a decimal octal or hex number. Handle '~' for one's complement. 1430 */ 1431 int 1432 kobj_getvalue(const char *token, u_longlong_t *valuep) 1433 { 1434 int radix; 1435 u_longlong_t retval = 0; 1436 int onescompl = 0; 1437 int negate = 0; 1438 char c; 1439 1440 if (*token == '~') { 1441 onescompl++; /* perform one's complement on result */ 1442 token++; 1443 } else if (*token == '-') { 1444 negate++; 1445 token++; 1446 } 1447 if (*token == '0') { 1448 token++; 1449 c = *token; 1450 1451 if (c == '\0') { 1452 *valuep = 0; /* value is 0 */ 1453 return (0); 1454 } 1455 1456 if (c == 'x' || c == 'X') { 1457 radix = 16; 1458 token++; 1459 } else 1460 radix = 8; 1461 } else 1462 radix = 10; 1463 1464 while ((c = *token++)) { 1465 switch (radix) { 1466 case 8: 1467 if (c >= '0' && c <= '7') 1468 c -= '0'; 1469 else 1470 return (-1); /* invalid number */ 1471 retval = (retval << 3) + c; 1472 break; 1473 case 10: 1474 if (c >= '0' && c <= '9') 1475 c -= '0'; 1476 else 1477 return (-1); /* invalid number */ 1478 retval = (retval * 10) + c; 1479 break; 1480 case 16: 1481 if (c >= 'a' && c <= 'f') 1482 c = c - 'a' + 10; 1483 else if (c >= 'A' && c <= 'F') 1484 c = c - 'A' + 10; 1485 else if (c >= '0' && c <= '9') 1486 c -= '0'; 1487 else 1488 return (-1); /* invalid number */ 1489 retval = (retval << 4) + c; 1490 break; 1491 } 1492 } 1493 if (onescompl) 1494 retval = ~retval; 1495 if (negate) 1496 retval = -retval; 1497 *valuep = retval; 1498 return (0); 1499 } 1500 1501 /* 1502 * Path to the root device and root filesystem type from 1503 * property information derived from the boot subsystem 1504 */ 1505 void 1506 setbootpath(char *path) 1507 { 1508 rootfs.bo_flags |= BO_VALID; 1509 (void) copystr(path, rootfs.bo_name, BO_MAXOBJNAME, NULL); 1510 BMDPRINTF(("rootfs bootpath: %s\n", rootfs.bo_name)); 1511 } 1512 1513 void 1514 setbootfstype(char *fstype) 1515 { 1516 (void) copystr(fstype, rootfs.bo_fstype, BO_MAXFSNAME, NULL); 1517 BMDPRINTF(("rootfs fstype: %s\n", rootfs.bo_fstype)); 1518 } 1519 1520 /* 1521 * set parameters that can be set early during initialization. 1522 */ 1523 static void 1524 setparams() 1525 { 1526 struct sysparam *sysp; 1527 struct bootobj *bootobjp; 1528 1529 for (sysp = sysparam_hd; sysp != NULL; sysp = sysp->sys_next) { 1530 1531 if (sysp->sys_type == MOD_MODDIR) { 1532 default_path = sysp->sys_ptr; 1533 continue; 1534 } 1535 1536 if (sysp->sys_type == MOD_SWAPDEV || 1537 sysp->sys_type == MOD_SWAPFS) 1538 bootobjp = &swapfile; 1539 else if (sysp->sys_type == MOD_ROOTFS) 1540 bootobjp = &rootfs; 1541 1542 switch (sysp->sys_type) { 1543 case MOD_SWAPDEV: 1544 bootobjp->bo_flags |= BO_VALID; 1545 (void) copystr(sysp->sys_ptr, bootobjp->bo_name, 1546 BO_MAXOBJNAME, NULL); 1547 break; 1548 case MOD_ROOTFS: 1549 case MOD_SWAPFS: 1550 bootobjp->bo_flags |= BO_VALID; 1551 (void) copystr(sysp->sys_ptr, bootobjp->bo_fstype, 1552 BO_MAXOBJNAME, NULL); 1553 break; 1554 case MOD_ROOTDEV: 1555 default: 1556 break; 1557 } 1558 } 1559 } 1560 1561 /* 1562 * clean up after an error. 1563 */ 1564 static void 1565 hwc_free(struct hwc_spec *hwcp) 1566 { 1567 char *name; 1568 1569 if ((name = hwcp->hwc_parent_name) != NULL) 1570 kmem_free(name, strlen(name) + 1); 1571 if ((name = hwcp->hwc_class_name) != NULL) 1572 kmem_free(name, strlen(name) + 1); 1573 if ((name = hwcp->hwc_devi_name) != NULL) 1574 kmem_free(name, strlen(name) + 1); 1575 i_ddi_prop_list_delete(hwcp->hwc_devi_sys_prop_ptr); 1576 kmem_free(hwcp, sizeof (struct hwc_spec)); 1577 } 1578 1579 /* 1580 * Free a list of specs 1581 */ 1582 void 1583 hwc_free_spec_list(struct hwc_spec *list) 1584 { 1585 while (list) { 1586 struct hwc_spec *tmp = list; 1587 list = tmp->hwc_next; 1588 hwc_free(tmp); 1589 } 1590 } 1591 1592 struct val_list { 1593 struct val_list *val_next; 1594 enum { 1595 VAL_STRING, 1596 VAL_INTEGER 1597 } val_type; 1598 int val_size; 1599 union { 1600 char *string; 1601 int integer; 1602 } val; 1603 }; 1604 1605 static struct val_list * 1606 add_val(struct val_list **val_listp, struct val_list *tail, 1607 int val_type, caddr_t val) 1608 { 1609 struct val_list *new_val; 1610 #ifdef DEBUG 1611 struct val_list *listp = *val_listp; 1612 #endif 1613 1614 new_val = kmem_alloc(sizeof (struct val_list), KM_SLEEP); 1615 new_val->val_next = NULL; 1616 if ((new_val->val_type = val_type) == VAL_STRING) { 1617 new_val->val_size = strlen((char *)val) + 1; 1618 new_val->val.string = kmem_alloc(new_val->val_size, KM_SLEEP); 1619 (void) strcpy(new_val->val.string, (char *)val); 1620 } else { 1621 new_val->val_size = sizeof (int); 1622 new_val->val.integer = (int)(uintptr_t)val; 1623 } 1624 1625 ASSERT((listp == NULL && tail == NULL) || 1626 (listp != NULL && tail != NULL)); 1627 1628 if (tail != NULL) { 1629 ASSERT(tail->val_next == NULL); 1630 tail->val_next = new_val; 1631 } else { 1632 *val_listp = new_val; 1633 } 1634 1635 return (new_val); 1636 } 1637 1638 static void 1639 free_val_list(struct val_list *head) 1640 { 1641 struct val_list *tval_list; 1642 1643 for (/* CSTYLED */; head != NULL; /* CSTYLED */) { 1644 tval_list = head; 1645 head = head->val_next; 1646 if (tval_list->val_type == VAL_STRING) 1647 kmem_free(tval_list->val.string, tval_list->val_size); 1648 kmem_free(tval_list, sizeof (struct val_list)); 1649 } 1650 } 1651 1652 /* 1653 * make sure there are no reserved IEEE 1275 characters (except 1654 * for uppercase characters). 1655 */ 1656 static int 1657 valid_prop_name(char *name) 1658 { 1659 int i; 1660 int len = strlen(name); 1661 1662 for (i = 0; i < len; i++) { 1663 if (name[i] < 0x21 || 1664 name[i] == '/' || 1665 name[i] == '\\' || 1666 name[i] == ':' || 1667 name[i] == '[' || 1668 name[i] == ']' || 1669 name[i] == '@') 1670 return (0); 1671 } 1672 return (1); 1673 } 1674 1675 static void 1676 make_prop(struct _buf *file, dev_info_t *devi, char *name, struct val_list *val) 1677 { 1678 int propcnt = 0, val_type; 1679 struct val_list *vl, *tvl; 1680 caddr_t valbuf = NULL; 1681 char **valsp; 1682 int *valip; 1683 1684 if (name == NULL) 1685 return; 1686 1687 #ifdef DEBUG 1688 parse_debug(NULL, "%s", name); 1689 #endif 1690 if (!valid_prop_name(name)) { 1691 cmn_err(CE_WARN, "invalid property name '%s'", name); 1692 return; 1693 } 1694 if (val) { 1695 for (vl = val, val_type = vl->val_type; vl; vl = vl->val_next) { 1696 if (val_type != vl->val_type) { 1697 cmn_err(CE_WARN, "Mixed types in value list"); 1698 return; 1699 } 1700 propcnt++; 1701 } 1702 1703 vl = val; 1704 1705 if (val_type == VAL_INTEGER) { 1706 valip = (int *)kmem_alloc( 1707 (propcnt * sizeof (int)), KM_SLEEP); 1708 valbuf = (caddr_t)valip; 1709 while (vl) { 1710 tvl = vl; 1711 vl = vl->val_next; 1712 #ifdef DEBUG 1713 parse_debug(NULL, " %x", tvl->val.integer); 1714 #endif 1715 *valip = tvl->val.integer; 1716 valip++; 1717 } 1718 /* restore valip */ 1719 valip = (int *)valbuf; 1720 1721 /* create the property */ 1722 if (e_ddi_prop_update_int_array(DDI_DEV_T_NONE, devi, 1723 name, valip, propcnt) != DDI_PROP_SUCCESS) { 1724 kobj_file_err(CE_WARN, file, 1725 "cannot create property %s", name); 1726 } 1727 /* cleanup */ 1728 kmem_free(valip, (propcnt * sizeof (int))); 1729 } else if (val_type == VAL_STRING) { 1730 valsp = (char **)kmem_alloc( 1731 ((propcnt + 1) * sizeof (char *)), KM_SLEEP); 1732 valbuf = (caddr_t)valsp; 1733 while (vl) { 1734 tvl = vl; 1735 vl = vl->val_next; 1736 #ifdef DEBUG 1737 parse_debug(NULL, " %s", tvl->val.string); 1738 #endif 1739 *valsp = tvl->val.string; 1740 valsp++; 1741 } 1742 /* terminate array with NULL */ 1743 *valsp = NULL; 1744 1745 /* restore valsp */ 1746 valsp = (char **)valbuf; 1747 1748 /* create the property */ 1749 if (e_ddi_prop_update_string_array(DDI_DEV_T_NONE, 1750 devi, name, valsp, propcnt) 1751 != DDI_PROP_SUCCESS) { 1752 kobj_file_err(CE_WARN, file, 1753 "cannot create property %s", name); 1754 } 1755 /* Clean up */ 1756 kmem_free(valsp, ((propcnt + 1) * sizeof (char *))); 1757 } else { 1758 cmn_err(CE_WARN, "Invalid property type"); 1759 return; 1760 } 1761 } else { 1762 /* 1763 * No value was passed in with property so we will assume 1764 * it is a "boolean" property and create an integer 1765 * property with 0 value. 1766 */ 1767 #ifdef DEBUG 1768 parse_debug(NULL, "\n"); 1769 #endif 1770 if (e_ddi_prop_update_int(DDI_DEV_T_NONE, devi, name, 0) 1771 != DDI_PROP_SUCCESS) { 1772 kobj_file_err(CE_WARN, file, 1773 "cannot create property %s", name); 1774 } 1775 } 1776 } 1777 1778 static char omit_err[] = "(the ';' may have been omitted on previous spec!)"; 1779 static char prnt_err[] = "'parent' property already specified"; 1780 static char nm_err[] = "'name' property already specified"; 1781 static char class_err[] = "'class' property already specified"; 1782 1783 typedef enum { 1784 hwc_begin, parent, drvname, drvclass, prop, 1785 parent_equals, name_equals, drvclass_equals, 1786 parent_equals_string, name_equals_string, 1787 drvclass_equals_string, 1788 prop_equals, prop_equals_string, prop_equals_integer, 1789 prop_equals_string_comma, prop_equals_integer_comma 1790 } hwc_state_t; 1791 1792 static struct hwc_spec * 1793 get_hwc_spec(struct _buf *file, char *tokbuf, size_t linesize) 1794 { 1795 char *prop_name; 1796 token_t token; 1797 struct hwc_spec *hwcp; 1798 struct dev_info *devi; 1799 struct val_list *val_list, *tail; 1800 hwc_state_t state; 1801 u_longlong_t ival; 1802 1803 hwcp = kmem_zalloc(sizeof (*hwcp), KM_SLEEP); 1804 devi = kmem_zalloc(sizeof (*devi), KM_SLEEP); 1805 1806 state = hwc_begin; 1807 token = NAME; 1808 prop_name = NULL; 1809 val_list = NULL; 1810 tail = NULL; 1811 do { 1812 #ifdef DEBUG 1813 parse_debug(NULL, "state 0x%x\n", state); 1814 #endif 1815 switch (token) { 1816 case NAME: 1817 switch (state) { 1818 case prop: 1819 case prop_equals_string: 1820 case prop_equals_integer: 1821 make_prop(file, (dev_info_t *)devi, 1822 prop_name, val_list); 1823 if (prop_name) { 1824 kmem_free(prop_name, 1825 strlen(prop_name) + 1); 1826 prop_name = NULL; 1827 } 1828 if (val_list) { 1829 free_val_list(val_list); 1830 val_list = NULL; 1831 } 1832 tail = NULL; 1833 /*FALLTHROUGH*/ 1834 case hwc_begin: 1835 if (strcmp(tokbuf, "PARENT") == 0 || 1836 strcmp(tokbuf, "parent") == 0) { 1837 state = parent; 1838 } else if (strcmp(tokbuf, "NAME") == 0 || 1839 strcmp(tokbuf, "name") == 0) { 1840 state = drvname; 1841 } else if (strcmp(tokbuf, "CLASS") == 0 || 1842 strcmp(tokbuf, "class") == 0) { 1843 state = drvclass; 1844 prop_name = kmem_alloc(strlen(tokbuf) + 1845 1, KM_SLEEP); 1846 (void) strcpy(prop_name, tokbuf); 1847 } else { 1848 state = prop; 1849 prop_name = kmem_alloc(strlen(tokbuf) + 1850 1, KM_SLEEP); 1851 (void) strcpy(prop_name, tokbuf); 1852 } 1853 break; 1854 default: 1855 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 1856 } 1857 break; 1858 case EQUALS: 1859 switch (state) { 1860 case drvname: 1861 state = name_equals; 1862 break; 1863 case parent: 1864 state = parent_equals; 1865 break; 1866 case drvclass: 1867 state = drvclass_equals; 1868 break; 1869 case prop: 1870 state = prop_equals; 1871 break; 1872 default: 1873 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 1874 } 1875 break; 1876 case STRING: 1877 switch (state) { 1878 case name_equals: 1879 if (ddi_get_name((dev_info_t *)devi)) { 1880 kobj_file_err(CE_WARN, file, "%s %s", 1881 nm_err, omit_err); 1882 goto bad; 1883 } 1884 devi->devi_name = kmem_alloc(strlen(tokbuf) + 1, 1885 KM_SLEEP); 1886 (void) strcpy(devi->devi_name, tokbuf); 1887 state = hwc_begin; 1888 break; 1889 case parent_equals: 1890 if (hwcp->hwc_parent_name) { 1891 kobj_file_err(CE_WARN, file, "%s %s", 1892 prnt_err, omit_err); 1893 goto bad; 1894 } 1895 hwcp->hwc_parent_name = kmem_alloc(strlen 1896 (tokbuf) + 1, KM_SLEEP); 1897 (void) strcpy(hwcp->hwc_parent_name, tokbuf); 1898 state = hwc_begin; 1899 break; 1900 case drvclass_equals: 1901 if (hwcp->hwc_class_name) { 1902 kobj_file_err(CE_WARN, file, class_err); 1903 goto bad; 1904 } 1905 hwcp->hwc_class_name = kmem_alloc( 1906 strlen(tokbuf) + 1, KM_SLEEP); 1907 (void) strcpy(hwcp->hwc_class_name, tokbuf); 1908 /*FALLTHROUGH*/ 1909 case prop_equals: 1910 case prop_equals_string_comma: 1911 tail = add_val(&val_list, tail, VAL_STRING, 1912 tokbuf); 1913 state = prop_equals_string; 1914 break; 1915 default: 1916 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 1917 } 1918 break; 1919 case HEXVAL: 1920 case DECVAL: 1921 switch (state) { 1922 case prop_equals: 1923 case prop_equals_integer_comma: 1924 (void) kobj_getvalue(tokbuf, &ival); 1925 tail = add_val(&val_list, tail, 1926 VAL_INTEGER, (caddr_t)(uintptr_t)ival); 1927 state = prop_equals_integer; 1928 break; 1929 default: 1930 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 1931 } 1932 break; 1933 case COMMA: 1934 switch (state) { 1935 case prop_equals_string: 1936 state = prop_equals_string_comma; 1937 break; 1938 case prop_equals_integer: 1939 state = prop_equals_integer_comma; 1940 break; 1941 default: 1942 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 1943 } 1944 break; 1945 case NEWLINE: 1946 kobj_newline(file); 1947 break; 1948 case POUND: 1949 /* 1950 * Skip comments. 1951 */ 1952 kobj_find_eol(file); 1953 break; 1954 case EOF: 1955 kobj_file_err(CE_WARN, file, "Unexpected EOF"); 1956 goto bad; 1957 default: 1958 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 1959 goto bad; 1960 } 1961 } while ((token = kobj_lex(file, tokbuf, linesize)) != SEMICOLON); 1962 1963 switch (state) { 1964 case prop: 1965 case prop_equals_string: 1966 case prop_equals_integer: 1967 make_prop(file, (dev_info_t *)devi, 1968 prop_name, val_list); 1969 break; 1970 1971 case hwc_begin: 1972 break; 1973 default: 1974 kobj_file_err(CE_WARN, file, "Unexpected end of line"); 1975 break; 1976 } 1977 1978 /* copy 2 relevant members of devi to hwcp */ 1979 hwcp->hwc_devi_sys_prop_ptr = devi->devi_sys_prop_ptr; 1980 hwcp->hwc_devi_name = devi->devi_name; 1981 1982 if (prop_name) 1983 kmem_free(prop_name, strlen(prop_name) + 1); 1984 if (val_list) 1985 free_val_list(val_list); 1986 1987 kmem_free(devi, sizeof (struct dev_info)); 1988 1989 return (hwcp); 1990 1991 bad: 1992 if (prop_name) 1993 kmem_free(prop_name, strlen(prop_name) + 1); 1994 if (val_list) 1995 free_val_list(val_list); 1996 1997 hwc_free(hwcp); 1998 1999 if (devi->devi_name) 2000 kmem_free(devi->devi_name, strlen(devi->devi_name) + 1); 2001 2002 kmem_free(devi, sizeof (struct dev_info)); 2003 2004 return (NULL); 2005 } 2006 2007 /* 2008 * This is the primary kernel interface to parse driver.conf files. 2009 * 2010 * Yet another bigstk thread handoff due to deep kernel stacks when booting 2011 * cache-only-clients. 2012 */ 2013 int 2014 hwc_parse(char *fname, struct par_list **pl, ddi_prop_t **props) 2015 { 2016 int ret; 2017 struct hwc_parse_mt *pltp = hwc_parse_mtalloc(fname, pl, props); 2018 2019 if (curthread != &t0) { 2020 (void) thread_create(NULL, DEFAULTSTKSZ * 2, 2021 hwc_parse_thread, pltp, 0, &p0, TS_RUN, maxclsyspri); 2022 sema_p(&pltp->sema); 2023 } else { 2024 pltp->rv = hwc_parse_now(fname, pl, props); 2025 } 2026 ret = pltp->rv; 2027 hwc_parse_mtfree(pltp); 2028 return (ret); 2029 } 2030 2031 /* 2032 * Calls to hwc_parse() are handled off to this routine in a separate 2033 * thread. 2034 */ 2035 static void 2036 hwc_parse_thread(struct hwc_parse_mt *pltp) 2037 { 2038 kmutex_t cpr_lk; 2039 callb_cpr_t cpr_i; 2040 2041 mutex_init(&cpr_lk, NULL, MUTEX_DEFAULT, NULL); 2042 CALLB_CPR_INIT(&cpr_i, &cpr_lk, callb_generic_cpr, "hwc_parse"); 2043 2044 /* 2045 * load and parse the .conf file 2046 * return the hwc_spec list (if any) to the creator of this thread 2047 */ 2048 pltp->rv = hwc_parse_now(pltp->name, pltp->pl, pltp->props); 2049 sema_v(&pltp->sema); 2050 mutex_enter(&cpr_lk); 2051 CALLB_CPR_EXIT(&cpr_i); 2052 mutex_destroy(&cpr_lk); 2053 thread_exit(); 2054 } 2055 2056 /* 2057 * allocate and initialize a hwc_parse thread control structure 2058 */ 2059 static struct hwc_parse_mt * 2060 hwc_parse_mtalloc(char *name, struct par_list **pl, ddi_prop_t **props) 2061 { 2062 struct hwc_parse_mt *pltp = kmem_zalloc(sizeof (*pltp), KM_SLEEP); 2063 2064 ASSERT(name != NULL); 2065 2066 pltp->name = kmem_alloc(strlen(name) + 1, KM_SLEEP); 2067 bcopy(name, pltp->name, strlen(name) + 1); 2068 pltp->pl = pl; 2069 pltp->props = props; 2070 2071 sema_init(&pltp->sema, 0, NULL, SEMA_DEFAULT, NULL); 2072 return (pltp); 2073 } 2074 2075 /* 2076 * free a hwc_parse thread control structure 2077 */ 2078 static void 2079 hwc_parse_mtfree(struct hwc_parse_mt *pltp) 2080 { 2081 sema_destroy(&pltp->sema); 2082 2083 kmem_free(pltp->name, strlen(pltp->name) + 1); 2084 kmem_free(pltp, sizeof (*pltp)); 2085 } 2086 2087 /* 2088 * hwc_parse -- parse an hwconf file. Ignore error lines and parse 2089 * as much as possible. 2090 */ 2091 static int 2092 hwc_parse_now(char *fname, struct par_list **pl, ddi_prop_t **props) 2093 { 2094 struct _buf *file; 2095 struct hwc_spec *hwcp; 2096 char *tokval; 2097 token_t token; 2098 2099 /* 2100 * Don't use kobj_open_path's use_moddir_suffix option, we only 2101 * expect to find conf files in the base module directory, not 2102 * an ISA-specific subdirectory. 2103 */ 2104 if ((file = kobj_open_path(fname, 1, 0)) == (struct _buf *)-1) { 2105 if (moddebug & MODDEBUG_ERRMSG) 2106 cmn_err(CE_WARN, "Cannot open %s", fname); 2107 return (-1); 2108 } 2109 2110 /* 2111 * Initialize variables 2112 */ 2113 tokval = kmem_alloc(MAX_HWC_LINESIZE, KM_SLEEP); 2114 2115 while ((token = kobj_lex(file, tokval, MAX_HWC_LINESIZE)) != EOF) { 2116 switch (token) { 2117 case POUND: 2118 /* 2119 * Skip comments. 2120 */ 2121 kobj_find_eol(file); 2122 break; 2123 case NAME: 2124 hwcp = get_hwc_spec(file, tokval, MAX_HWC_LINESIZE); 2125 if (hwcp == NULL) 2126 break; 2127 /* 2128 * No devi_name indicates global property. 2129 * Make sure parent and class not NULL. 2130 */ 2131 if (hwcp->hwc_devi_name == NULL) { 2132 if (hwcp->hwc_parent_name || 2133 hwcp->hwc_class_name) { 2134 kobj_file_err(CE_WARN, file, 2135 "missing name attribute"); 2136 hwc_free(hwcp); 2137 continue; 2138 } 2139 /* Add to global property list */ 2140 add_props(hwcp, props); 2141 break; 2142 } 2143 2144 /* 2145 * This is a node spec, either parent or class 2146 * must be specified. 2147 */ 2148 if ((hwcp->hwc_parent_name == NULL) && 2149 (hwcp->hwc_class_name == NULL)) { 2150 kobj_file_err(CE_WARN, file, 2151 "missing parent or class attribute"); 2152 hwc_free(hwcp); 2153 continue; 2154 } 2155 2156 /* add to node spec list */ 2157 add_spec(hwcp, pl); 2158 break; 2159 case NEWLINE: 2160 kobj_newline(file); 2161 break; 2162 default: 2163 kobj_file_err(CE_WARN, file, tok_err, tokval); 2164 break; 2165 } 2166 } 2167 /* 2168 * XXX - Check for clean termination. 2169 */ 2170 kmem_free(tokval, MAX_HWC_LINESIZE); 2171 kobj_close_file(file); 2172 return (0); /* always return success */ 2173 } 2174 2175 static void 2176 parse_aliases(struct bind **bhash, struct _buf *file) 2177 { 2178 enum { 2179 AL_NEW, AL_DRVNAME, AL_DRVNAME_COMMA, AL_ALIAS, AL_ALIAS_COMMA 2180 } state; 2181 2182 char tokbuf[MAXPATHLEN]; 2183 char drvbuf[MAXPATHLEN]; 2184 token_t token; 2185 major_t major; 2186 int done = 0; 2187 static char dupwarn[] = "!Driver alias \"%s\" conflicts with " 2188 "an existing driver name or alias."; 2189 2190 state = AL_NEW; 2191 major = DDI_MAJOR_T_NONE; 2192 while (!done) { 2193 token = kobj_lex(file, tokbuf, sizeof (tokbuf)); 2194 switch (token) { 2195 case POUND: 2196 /* 2197 * Skip comments. 2198 */ 2199 kobj_find_eol(file); 2200 break; 2201 case NAME: 2202 case STRING: 2203 switch (state) { 2204 case AL_NEW: 2205 (void) strcpy(drvbuf, tokbuf); 2206 state = AL_DRVNAME; 2207 break; 2208 case AL_DRVNAME_COMMA: 2209 (void) strcat(drvbuf, tokbuf); 2210 state = AL_DRVNAME; 2211 break; 2212 case AL_ALIAS_COMMA: 2213 (void) strcat(drvbuf, tokbuf); 2214 state = AL_ALIAS; 2215 break; 2216 case AL_DRVNAME: 2217 major = mod_name_to_major(drvbuf); 2218 if (major == DDI_MAJOR_T_NONE) { 2219 kobj_find_eol(file); 2220 state = AL_NEW; 2221 } else { 2222 (void) strcpy(drvbuf, tokbuf); 2223 state = AL_ALIAS; 2224 } 2225 break; 2226 case AL_ALIAS: 2227 if (make_mbind(drvbuf, major, NULL, bhash) 2228 != 0) { 2229 cmn_err(CE_WARN, dupwarn, drvbuf); 2230 } 2231 /* 2232 * copy this token just in case that there 2233 * are multiple names on the same line. 2234 */ 2235 (void) strcpy(drvbuf, tokbuf); 2236 break; 2237 } 2238 break; 2239 case COMMA: 2240 (void) strcat(drvbuf, tokbuf); 2241 switch (state) { 2242 case AL_DRVNAME: 2243 state = AL_DRVNAME_COMMA; 2244 break; 2245 case AL_ALIAS: 2246 state = AL_ALIAS_COMMA; 2247 break; 2248 default: 2249 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 2250 } 2251 break; 2252 case EOF: 2253 done = 1; 2254 /*FALLTHROUGH*/ 2255 case NEWLINE: 2256 if (state == AL_ALIAS) { 2257 if (make_mbind(drvbuf, major, NULL, bhash) 2258 != 0) { 2259 cmn_err(CE_WARN, dupwarn, drvbuf); 2260 } 2261 } else if (state != AL_NEW) { 2262 kobj_file_err(CE_WARN, file, 2263 "Missing alias for %s", drvbuf); 2264 } 2265 2266 kobj_newline(file); 2267 state = AL_NEW; 2268 major = DDI_MAJOR_T_NONE; 2269 break; 2270 default: 2271 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 2272 } 2273 } 2274 } 2275 2276 void 2277 make_aliases(struct bind **bhash) 2278 { 2279 struct _buf *file; 2280 2281 if ((file = kobj_open_file(pptfile)) != (struct _buf *)-1) { 2282 parse_aliases(bhash, file); 2283 kobj_close_file(file); 2284 } 2285 2286 if ((file = kobj_open_file(dafile)) != (struct _buf *)-1) { 2287 parse_aliases(bhash, file); 2288 kobj_close_file(file); 2289 } 2290 } 2291 2292 2293 /* 2294 * It is called for parsing these files: 2295 * - /etc/path_to_inst 2296 * - /etc/name_to_major 2297 * - /etc/name_to_sysnum 2298 * A callback "int (*line_parser)(char *, int, char *, struct bind **)" 2299 * is invoked for each line of the file. 2300 * The callback can inhash the entry into a hashtable by supplying 2301 * a pre-allocated hashtable in "struct bind **hashtab". 2302 */ 2303 int 2304 read_binding_file(char *bindfile, struct bind **hashtab, 2305 int (*line_parser)(char *, int, char *, struct bind **)) 2306 { 2307 enum { 2308 B_NEW, B_NAME, B_VAL, B_BIND_NAME 2309 } state; 2310 struct _buf *file; 2311 char tokbuf[MAXNAMELEN]; 2312 token_t token; 2313 int maxnum = 0; 2314 char *bind_name = NULL, *name = NULL, *bn = NULL; 2315 u_longlong_t val; 2316 int done = 0; 2317 2318 static char num_err[] = "Missing number on preceding line?"; 2319 static char dupwarn[] = "!The binding file entry \"%s %u\" conflicts " 2320 "with a previous entry"; 2321 2322 if (hashtab != NULL) { 2323 clear_binding_hash(hashtab); 2324 } 2325 2326 if ((file = kobj_open_file(bindfile)) == (struct _buf *)-1) 2327 panic("read_binding_file: %s file not found", bindfile); 2328 2329 state = B_NEW; 2330 2331 while (!done) { 2332 token = kobj_lex(file, tokbuf, sizeof (tokbuf)); 2333 2334 switch (token) { 2335 case POUND: 2336 /* 2337 * Skip comments. 2338 */ 2339 kobj_find_eol(file); 2340 break; 2341 case NAME: 2342 case STRING: 2343 switch (state) { 2344 case B_NEW: 2345 /* 2346 * This case is for the first name and 2347 * possibly only name in an entry. 2348 */ 2349 ASSERT(name == NULL); 2350 name = kmem_alloc(strlen(tokbuf) + 1, KM_SLEEP); 2351 (void) strcpy(name, tokbuf); 2352 state = B_NAME; 2353 break; 2354 case B_VAL: 2355 /* 2356 * This case is for a second name, which 2357 * would be the binding name if the first 2358 * name was actually a generic name. 2359 */ 2360 ASSERT(bind_name == NULL); 2361 bind_name = kmem_alloc(strlen(tokbuf) + 1, 2362 KM_SLEEP); 2363 (void) strcpy(bind_name, tokbuf); 2364 state = B_BIND_NAME; 2365 break; 2366 default: 2367 kobj_file_err(CE_WARN, file, num_err); 2368 } 2369 break; 2370 case HEXVAL: 2371 case DECVAL: 2372 if (state != B_NAME) { 2373 kobj_file_err(CE_WARN, file, "Missing name?"); 2374 state = B_NEW; 2375 continue; 2376 } 2377 (void) kobj_getvalue(tokbuf, &val); 2378 if (val > (u_longlong_t)INT_MAX) { 2379 kobj_file_err(CE_WARN, file, 2380 "value %llu too large", val); 2381 state = B_NEW; 2382 continue; 2383 } 2384 state = B_VAL; 2385 break; 2386 case EOF: 2387 done = 1; 2388 /*FALLTHROUGH*/ 2389 case NEWLINE: 2390 if ((state == B_BIND_NAME) || (state == B_VAL)) { 2391 if (state == B_BIND_NAME) 2392 bn = bind_name; 2393 else 2394 bn = NULL; 2395 2396 if (line_parser != NULL) { 2397 if ((*line_parser)(name, (int)val, bn, 2398 hashtab) == 0) 2399 maxnum = MAX((int)val, maxnum); 2400 else 2401 kobj_file_err(CE_WARN, file, 2402 dupwarn, name, (uint_t)val); 2403 } 2404 } else if (state != B_NEW) 2405 kobj_file_err(CE_WARN, file, "Syntax error?"); 2406 2407 if (name) { 2408 kmem_free(name, strlen(name) + 1); 2409 name = NULL; 2410 } 2411 if (bind_name) { 2412 kmem_free(bind_name, strlen(bind_name) + 1); 2413 bind_name = NULL; 2414 } 2415 state = B_NEW; 2416 kobj_newline(file); 2417 break; 2418 default: 2419 kobj_file_err(CE_WARN, file, "Missing name/number?"); 2420 break; 2421 } 2422 } 2423 2424 ASSERT(name == NULL); /* any leaks? */ 2425 ASSERT(bind_name == NULL); 2426 2427 kobj_close_file(file); 2428 return (maxnum); 2429 } 2430 2431 /* 2432 * read_dacf_binding_file() 2433 * Read the /etc/dacf.conf file and build the dacf_rule_t database from it. 2434 * 2435 * The syntax of a line in the dacf.conf file is: 2436 * dev-spec [module:]op-set operation options [config-args]; 2437 * 2438 * Where: 2439 * 1. dev-spec is of the format: name="data" 2440 * 2. operation is the operation that this rule matches. (i.e. pre-detach) 2441 * 3. options is a comma delimited list of options (i.e. debug,foobar) 2442 * 4. config-data is a whitespace delimited list of the format: name="data" 2443 */ 2444 int 2445 read_dacf_binding_file(char *filename) 2446 { 2447 enum { 2448 DACF_BEGIN, 2449 /* minor_nodetype="ddi_mouse:serial" */ 2450 DACF_NT_SPEC, DACF_NT_EQUALS, DACF_NT_DATA, 2451 /* consconfig:mouseconfig */ 2452 DACF_MN_MODNAME, DACF_MN_COLON, DACF_MN_OPSET, 2453 /* op */ 2454 DACF_OP_NAME, 2455 /* [ option1, option2, option3... | - ] */ 2456 DACF_OPT_OPTION, DACF_OPT_COMMA, DACF_OPT_END, 2457 /* argname1="argval1" argname2="argval2" ... */ 2458 DACF_OPARG_SPEC, DACF_OPARG_EQUALS, DACF_OPARG_DATA, 2459 DACF_ERR, DACF_ERR_NEWLINE, DACF_COMMENT 2460 } state = DACF_BEGIN; 2461 2462 struct _buf *file; 2463 char *fname; 2464 token_t token; 2465 2466 char tokbuf[MAXNAMELEN]; 2467 char mn_modname_buf[MAXNAMELEN], *mn_modnamep = NULL; 2468 char mn_opset_buf[MAXNAMELEN], *mn_opsetp = NULL; 2469 char nt_data_buf[MAXNAMELEN], *nt_datap = NULL; 2470 char arg_spec_buf[MAXNAMELEN]; 2471 2472 uint_t opts = 0; 2473 dacf_devspec_t nt_spec_type = DACF_DS_ERROR; 2474 2475 dacf_arg_t *arg_list = NULL; 2476 dacf_opid_t opid = DACF_OPID_ERROR; 2477 int done = 0; 2478 2479 static char w_syntax[] = "'%s' unexpected"; 2480 static char w_equals[] = "'=' is illegal in the current context"; 2481 static char w_baddevspec[] = "device specification '%s' unrecognized"; 2482 static char w_badop[] = "operation '%s' unrecognized"; 2483 static char w_badopt[] = "option '%s' unrecognized, ignoring"; 2484 static char w_newline[] = "rule is incomplete"; 2485 static char w_insert[] = "failed to register rule"; 2486 static char w_comment[] = "'#' not allowed except at start of line"; 2487 static char w_dupargs[] = 2488 "argument '%s' duplicates a previous argument, skipping"; 2489 static char w_nt_empty[] = "empty device specification not allowed"; 2490 2491 if (filename == NULL) { 2492 fname = dacffile; /* default binding file */ 2493 } else { 2494 fname = filename; /* user specified */ 2495 } 2496 2497 if ((file = kobj_open_file(fname)) == (struct _buf *)-1) { 2498 return (ENOENT); 2499 } 2500 2501 if (dacfdebug & DACF_DBG_MSGS) { 2502 printf("dacf debug: clearing rules database\n"); 2503 } 2504 2505 mutex_enter(&dacf_lock); 2506 dacf_clear_rules(); 2507 2508 if (dacfdebug & DACF_DBG_MSGS) { 2509 printf("dacf debug: parsing %s\n", fname); 2510 } 2511 2512 while (!done) { 2513 token = kobj_lex(file, tokbuf, sizeof (tokbuf)); 2514 2515 switch (token) { 2516 case POUND: /* comment line */ 2517 if (state != DACF_BEGIN) { 2518 kobj_file_err(CE_WARN, file, w_comment); 2519 state = DACF_ERR; 2520 break; 2521 } 2522 state = DACF_COMMENT; 2523 kobj_find_eol(file); 2524 break; 2525 2526 case EQUALS: 2527 switch (state) { 2528 case DACF_NT_SPEC: 2529 state = DACF_NT_EQUALS; 2530 break; 2531 case DACF_OPARG_SPEC: 2532 state = DACF_OPARG_EQUALS; 2533 break; 2534 default: 2535 kobj_file_err(CE_WARN, file, w_equals); 2536 state = DACF_ERR; 2537 } 2538 break; 2539 2540 case NAME: 2541 switch (state) { 2542 case DACF_BEGIN: 2543 nt_spec_type = dacf_get_devspec(tokbuf); 2544 if (nt_spec_type == DACF_DS_ERROR) { 2545 kobj_file_err(CE_WARN, file, 2546 w_baddevspec, tokbuf); 2547 state = DACF_ERR; 2548 break; 2549 } 2550 state = DACF_NT_SPEC; 2551 break; 2552 case DACF_NT_DATA: 2553 (void) strncpy(mn_modname_buf, tokbuf, 2554 sizeof (mn_modname_buf)); 2555 mn_modnamep = mn_modname_buf; 2556 state = DACF_MN_MODNAME; 2557 break; 2558 case DACF_MN_MODNAME: 2559 /* 2560 * This handles the 'optional' modname. 2561 * What we thought was the modname is really 2562 * the op-set. So it is copied over. 2563 */ 2564 ASSERT(mn_modnamep); 2565 (void) strncpy(mn_opset_buf, mn_modnamep, 2566 sizeof (mn_opset_buf)); 2567 mn_opsetp = mn_opset_buf; 2568 mn_modnamep = NULL; 2569 /* 2570 * Now, the token we just read is the opset, 2571 * so look that up and fill in opid 2572 */ 2573 if ((opid = dacf_get_op(tokbuf)) == 2574 DACF_OPID_ERROR) { 2575 kobj_file_err(CE_WARN, file, w_badop, 2576 tokbuf); 2577 state = DACF_ERR; 2578 break; 2579 } 2580 state = DACF_OP_NAME; 2581 break; 2582 case DACF_MN_COLON: 2583 (void) strncpy(mn_opset_buf, tokbuf, 2584 sizeof (mn_opset_buf)); 2585 mn_opsetp = mn_opset_buf; 2586 state = DACF_MN_OPSET; 2587 break; 2588 case DACF_MN_OPSET: 2589 if ((opid = dacf_get_op(tokbuf)) == 2590 DACF_OPID_ERROR) { 2591 kobj_file_err(CE_WARN, file, w_badop, 2592 tokbuf); 2593 state = DACF_ERR; 2594 break; 2595 } 2596 state = DACF_OP_NAME; 2597 break; 2598 case DACF_OP_NAME: 2599 /* 2600 * This case is just like DACF_OPT_COMMA below, 2601 * but we check for the sole '-' argument 2602 */ 2603 if (strcmp(tokbuf, "-") == 0) { 2604 state = DACF_OPT_END; 2605 break; 2606 } 2607 /*FALLTHROUGH*/ 2608 case DACF_OPT_COMMA: 2609 /* 2610 * figure out what option was given, but don't 2611 * make a federal case if invalid, just skip it 2612 */ 2613 if (dacf_getopt(tokbuf, &opts) != 0) { 2614 kobj_file_err(CE_WARN, file, w_badopt, 2615 tokbuf); 2616 } 2617 state = DACF_OPT_OPTION; 2618 break; 2619 case DACF_OPT_END: 2620 case DACF_OPT_OPTION: 2621 case DACF_OPARG_DATA: 2622 (void) strncpy(arg_spec_buf, tokbuf, 2623 sizeof (arg_spec_buf)); 2624 state = DACF_OPARG_SPEC; 2625 break; 2626 case DACF_OPARG_EQUALS: 2627 /* 2628 * Add the arg. Warn if it's a duplicate 2629 */ 2630 if (dacf_arg_insert(&arg_list, arg_spec_buf, 2631 tokbuf) != 0) { 2632 kobj_file_err(CE_WARN, file, w_dupargs, 2633 arg_spec_buf); 2634 } 2635 state = DACF_OPARG_DATA; 2636 break; 2637 default: 2638 kobj_file_err(CE_WARN, file, w_syntax, tokbuf); 2639 state = DACF_ERR; 2640 break; 2641 } 2642 break; 2643 2644 case STRING: 2645 /* 2646 * We need to check to see if the string has a \n in it. 2647 * If so, we had an unmatched " mark error, and lex has 2648 * already emitted an error for us, so we need to enter 2649 * the error state. Stupid lex. 2650 */ 2651 if (strchr(tokbuf, '\n')) { 2652 state = DACF_ERR; 2653 break; 2654 } 2655 switch (state) { 2656 case DACF_NT_EQUALS: 2657 if (strlen(tokbuf) == 0) { 2658 kobj_file_err(CE_WARN, file, 2659 w_nt_empty); 2660 state = DACF_ERR; 2661 break; 2662 } 2663 state = DACF_NT_DATA; 2664 nt_datap = nt_data_buf; 2665 (void) strncpy(nt_datap, tokbuf, 2666 sizeof (nt_data_buf)); 2667 break; 2668 case DACF_OPARG_EQUALS: 2669 /* 2670 * Add the arg. Warn if it's a duplicate 2671 */ 2672 if (dacf_arg_insert(&arg_list, arg_spec_buf, 2673 tokbuf) != 0) { 2674 kobj_file_err(CE_WARN, file, w_dupargs, 2675 arg_spec_buf); 2676 } 2677 state = DACF_OPARG_DATA; 2678 break; 2679 default: 2680 kobj_file_err(CE_WARN, file, w_syntax, tokbuf); 2681 state = DACF_ERR; 2682 break; 2683 } 2684 break; 2685 2686 case COMMA: 2687 switch (state) { 2688 case DACF_OPT_OPTION: 2689 state = DACF_OPT_COMMA; 2690 break; 2691 default: 2692 kobj_file_err(CE_WARN, file, w_syntax, ","); 2693 state = DACF_ERR; 2694 break; 2695 } 2696 break; 2697 2698 case COLON: 2699 if (state == DACF_MN_MODNAME) 2700 state = DACF_MN_COLON; 2701 else { 2702 kobj_file_err(CE_WARN, file, w_syntax, ":"); 2703 state = DACF_ERR; 2704 } 2705 break; 2706 2707 case EOF: 2708 done = 1; 2709 /*FALLTHROUGH*/ 2710 case NEWLINE: 2711 if (state == DACF_COMMENT || state == DACF_BEGIN) { 2712 state = DACF_BEGIN; 2713 kobj_newline(file); 2714 break; 2715 } 2716 if ((state != DACF_OPT_OPTION) && 2717 (state != DACF_OPARG_DATA) && 2718 (state != DACF_OPT_END)) { 2719 kobj_file_err(CE_WARN, file, w_newline); 2720 /* 2721 * We can't just do DACF_ERR here, since we'll 2722 * wind up eating the _next_ newline if so. 2723 */ 2724 state = DACF_ERR_NEWLINE; 2725 kobj_newline(file); 2726 break; 2727 } 2728 2729 /* 2730 * insert the rule. 2731 */ 2732 if (dacf_rule_insert(nt_spec_type, nt_datap, 2733 mn_modnamep, mn_opsetp, opid, opts, arg_list) < 0) { 2734 /* 2735 * We can't just do DACF_ERR here, since we'll 2736 * wind up eating the _next_ newline if so. 2737 */ 2738 kobj_file_err(CE_WARN, file, w_insert); 2739 state = DACF_ERR_NEWLINE; 2740 kobj_newline(file); 2741 break; 2742 } 2743 2744 state = DACF_BEGIN; 2745 kobj_newline(file); 2746 break; 2747 2748 default: 2749 kobj_file_err(CE_WARN, file, w_syntax, tokbuf); 2750 break; 2751 } /* switch */ 2752 2753 /* 2754 * Clean up after ourselves, either after a line has terminated 2755 * successfully or because of a syntax error; or when we reach 2756 * EOF (remember, we may reach EOF without being 'done' with 2757 * handling a particular line). 2758 */ 2759 if (state == DACF_ERR) { 2760 kobj_find_eol(file); 2761 } 2762 if ((state == DACF_BEGIN) || (state == DACF_ERR) || 2763 (state == DACF_ERR_NEWLINE) || done) { 2764 nt_datap = NULL; 2765 mn_modnamep = mn_opsetp = NULL; 2766 opts = 0; 2767 opid = DACF_OPID_ERROR; 2768 nt_spec_type = DACF_DS_ERROR; 2769 dacf_arglist_delete(&arg_list); 2770 state = DACF_BEGIN; 2771 } 2772 } /* while */ 2773 2774 if (dacfdebug & DACF_DBG_MSGS) { 2775 printf("\ndacf debug: done!\n"); 2776 } 2777 2778 mutex_exit(&dacf_lock); 2779 2780 kobj_close_file(file); 2781 return (0); 2782 } 2783 2784 void 2785 lock_hw_class_list() 2786 { 2787 mutex_enter(&hcl_lock); 2788 } 2789 2790 void 2791 unlock_hw_class_list() 2792 { 2793 mutex_exit(&hcl_lock); 2794 } 2795 2796 void 2797 add_class(char *exporter, char *class) 2798 { 2799 struct hwc_class *hcl; 2800 2801 /* 2802 * If exporter's major is not registered in /etc/name_to_major, 2803 * don't update hwc_class, but just return here. 2804 */ 2805 if (ddi_name_to_major(exporter) >= devcnt) { 2806 cmn_err(CE_WARN, "No major number for driver %s" 2807 " in class %s", exporter, class); 2808 return; 2809 } 2810 hcl = kmem_zalloc(sizeof (struct hwc_class), KM_SLEEP); 2811 hcl->class_exporter = kmem_alloc(strlen(exporter) + 1, KM_SLEEP); 2812 hcl->class_name = kmem_alloc(strlen(class) + 1, KM_SLEEP); 2813 (void) strcpy(hcl->class_exporter, exporter); 2814 (void) strcpy(hcl->class_name, class); 2815 lock_hw_class_list(); 2816 hcl->class_next = hcl_head; 2817 hcl_head = hcl; 2818 unlock_hw_class_list(); 2819 } 2820 2821 /* 2822 * Return the number of classes exported. If buf is not NULL, fill in 2823 * the array of the class names as well. 2824 * 2825 * Caller must hold hcl_lock to ensure the class list unmodified while 2826 * it is accessed. A typical caller will get a count first and then 2827 * allocate buf. The lock should be held by the caller. 2828 */ 2829 int 2830 get_class(const char *exporter, char **buf) 2831 { 2832 int n = 0; 2833 struct hwc_class *hcl; 2834 2835 ASSERT(mutex_owned(&hcl_lock)); 2836 for (hcl = hcl_head; hcl != NULL; hcl = hcl->class_next) { 2837 if (strcmp(exporter, hcl->class_exporter) == 0) { 2838 if (buf) 2839 buf[n] = hcl->class_name; 2840 ++n; 2841 } 2842 } 2843 2844 return (n); 2845 } 2846 2847 void 2848 read_class_file(void) 2849 { 2850 struct _buf *file; 2851 struct hwc_class *hcl, *hcl1; 2852 char tokbuf[MAXNAMELEN]; 2853 enum { 2854 C_BEGIN, C_EXPORTER, C_END 2855 } state; 2856 token_t token; 2857 int done = 0; 2858 char *exporter = NULL, *class = NULL, *name = NULL; 2859 2860 if (hcl_head != NULL) { 2861 hcl = hcl_head; 2862 while (hcl != NULL) { 2863 kmem_free(hcl->class_exporter, 2864 strlen(hcl->class_exporter) + 1); 2865 hcl1 = hcl; 2866 hcl = hcl->class_next; 2867 kmem_free(hcl1, sizeof (struct hwc_class)); 2868 } 2869 hcl_head = NULL; 2870 } 2871 2872 if ((file = kobj_open_file(class_file)) == (struct _buf *)-1) 2873 return; 2874 2875 state = C_BEGIN; 2876 while (!done) { 2877 token = kobj_lex(file, tokbuf, sizeof (tokbuf)); 2878 2879 switch (token) { 2880 case POUND: 2881 /* 2882 * Skip comments. 2883 */ 2884 kobj_find_eol(file); 2885 break; 2886 case NAME: 2887 case STRING: 2888 name = kmem_alloc(strlen(tokbuf) + 1, KM_SLEEP); 2889 (void) strcpy(name, tokbuf); 2890 switch (state) { 2891 case C_BEGIN: 2892 exporter = name; 2893 state = C_EXPORTER; 2894 break; 2895 case C_EXPORTER: 2896 class = name; 2897 add_class(exporter, class); 2898 state = C_END; 2899 break; 2900 case C_END: 2901 kobj_file_err(CE_WARN, file, 2902 "Extra noise after entry"); 2903 kmem_free(name, strlen(name) + 1); 2904 kobj_find_eol(file); 2905 break; 2906 } /* End Switch */ 2907 break; 2908 case EOF: 2909 done = 1; 2910 /*FALLTHROUGH*/ 2911 case NEWLINE: 2912 kobj_newline(file); 2913 if (state == C_EXPORTER) 2914 kobj_file_err(CE_WARN, file, 2915 "Partial entry ignored"); 2916 state = C_BEGIN; 2917 if (exporter) 2918 kmem_free(exporter, strlen(exporter) + 1); 2919 if (class) 2920 kmem_free(class, strlen(class) + 1); 2921 exporter = NULL; 2922 class = NULL; 2923 break; 2924 default: 2925 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 2926 break; 2927 } 2928 } 2929 kobj_close_file(file); 2930 } 2931 2932 /* 2933 * Given par_list, get a list of parent major number 2934 */ 2935 int 2936 impl_parlist_to_major(struct par_list *pl, char parents[]) 2937 { 2938 struct hwc_spec *hwcp; 2939 struct hwc_class *hcl; 2940 major_t major; 2941 int nmajor = 0; 2942 extern int devcnt; 2943 2944 for (; pl != NULL; pl = pl->par_next) { 2945 if ((pl->par_major < devcnt) && (parents[pl->par_major] == 0)) { 2946 parents[pl->par_major] = 1; 2947 nmajor++; 2948 continue; 2949 } 2950 2951 /* parent specs cannot be mapped to a driver */ 2952 if (pl->par_major != DDI_MAJOR_T_NONE) 2953 continue; 2954 2955 /* class spec */ 2956 hwcp = pl->par_specs; 2957 ASSERT(hwcp->hwc_class_name); 2958 ASSERT(hwcp->hwc_parent_name == NULL); 2959 2960 for (hcl = hcl_head; hcl != NULL; hcl = hcl->class_next) { 2961 if (strcmp(hwcp->hwc_class_name, hcl->class_name) != 0) 2962 continue; 2963 major = ddi_name_to_major(hcl->class_exporter); 2964 ASSERT(major != DDI_MAJOR_T_NONE); 2965 if (parents[major] == 0) { 2966 parents[major] = 1; 2967 nmajor++; 2968 } 2969 } 2970 } 2971 return (nmajor); 2972 } 2973 2974 /* 2975 * delete a parent list and all its hwc specs 2976 */ 2977 void 2978 impl_delete_par_list(struct par_list *pl) 2979 { 2980 struct par_list *saved_pl; 2981 struct hwc_spec *hp, *hp1; 2982 2983 while (pl) { 2984 hp = pl->par_specs; 2985 while (hp) { 2986 hp1 = hp; 2987 hp = hp->hwc_next; 2988 hwc_free(hp1); 2989 } 2990 saved_pl = pl; 2991 pl = pl->par_next; 2992 kmem_free(saved_pl, sizeof (*saved_pl)); 2993 } 2994 } 2995 2996 #if defined(_PSM_MODULES) 2997 void 2998 open_mach_list(void) 2999 { 3000 struct _buf *file; 3001 char tokbuf[MAXNAMELEN]; 3002 token_t token; 3003 struct psm_mach *machp; 3004 3005 if ((file = kobj_open_file(mach_file)) == (struct _buf *)-1) 3006 return; 3007 3008 while ((token = kobj_lex(file, tokbuf, sizeof (tokbuf))) != EOF) { 3009 switch (token) { 3010 case POUND: 3011 /* 3012 * Skip comments. 3013 */ 3014 kobj_find_eol(file); 3015 break; 3016 case NAME: 3017 case STRING: 3018 machp = kmem_alloc((sizeof (struct psm_mach) + 3019 strlen(tokbuf) + 1), KM_SLEEP); 3020 machp->m_next = pmach_head; 3021 machp->m_machname = (char *)(machp + 1); 3022 (void) strcpy(machp->m_machname, tokbuf); 3023 pmach_head = machp; 3024 break; 3025 case NEWLINE: 3026 kobj_newline(file); 3027 break; 3028 default: 3029 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 3030 break; 3031 } 3032 } 3033 kobj_close_file(file); 3034 } 3035 3036 void * 3037 get_next_mach(void *handle, char *buf) 3038 { 3039 struct psm_mach *machp; 3040 3041 machp = (struct psm_mach *)handle; 3042 if (machp) 3043 machp = machp->m_next; 3044 else 3045 machp = pmach_head; 3046 if (machp) 3047 (void) strcpy(buf, machp->m_machname); 3048 return (machp); 3049 } 3050 3051 void 3052 close_mach_list(void) 3053 { 3054 struct psm_mach *machp; 3055 3056 while (pmach_head) { 3057 machp = pmach_head; 3058 pmach_head = machp->m_next; 3059 kmem_free(machp, sizeof (struct psm_mach) + 3060 strlen(machp->m_machname) + 1); 3061 } 3062 } 3063 #endif /* _PSM_MODULES */ 3064 3065 #if defined(_RTC_CONFIG) 3066 /* 3067 * Read in the 'zone_lag' value from the rtc configuration file, 3068 * and return the value to the caller. Note that there is other information 3069 * in this file (zone_info), so we ignore unknown values. We do spit out 3070 * warnings if the line doesn't begin with an identifier, or if we don't find 3071 * exactly "zone_lag=value". No one should be editing this file by hand 3072 * (use the rtc command instead), but it's better to be careful. 3073 */ 3074 long 3075 process_rtc_config_file(void) 3076 { 3077 enum { 3078 R_NEW, R_NAME, R_EQUALS, R_VALUE 3079 } state; 3080 struct _buf *file; 3081 char tokbuf[MAXNAMELEN]; 3082 token_t token; 3083 long zone_lag = 0; 3084 u_longlong_t tmp; 3085 int done = 0; 3086 3087 if ((file = kobj_open_file(rtc_config_file)) == (struct _buf *)-1) 3088 return (0); 3089 3090 state = R_NEW; 3091 3092 while (!done) { 3093 token = kobj_lex(file, tokbuf, sizeof (tokbuf)); 3094 3095 switch (token) { 3096 case POUND: 3097 /* 3098 * Skip comments. 3099 */ 3100 kobj_find_eol(file); 3101 break; 3102 case NAME: 3103 case STRING: 3104 if (state == R_NEW) { 3105 if (strcmp(tokbuf, "zone_lag") == 0) 3106 state = R_NAME; 3107 else 3108 kobj_find_eol(file); /* Ignore */ 3109 } else 3110 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 3111 break; 3112 case EQUALS: 3113 if (state == R_NAME) 3114 state = R_EQUALS; 3115 else 3116 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 3117 break; 3118 case DECVAL: 3119 if (state == R_EQUALS) { 3120 if (kobj_getvalue(tokbuf, &tmp) != 0) 3121 kobj_file_err(CE_WARN, file, 3122 "Bad value %s for zone_lag", 3123 tokbuf); 3124 else 3125 zone_lag = (long)tmp; 3126 state = R_VALUE; 3127 } else 3128 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 3129 break; 3130 case EOF: 3131 done = 1; 3132 /*FALLTHROUGH*/ 3133 case NEWLINE: 3134 if (state != R_NEW && state != R_VALUE) 3135 kobj_file_err(CE_WARN, file, 3136 "Partial zone_lag entry ignored"); 3137 kobj_newline(file); 3138 state = R_NEW; 3139 break; 3140 default: 3141 kobj_file_err(CE_WARN, file, tok_err, tokbuf); 3142 break; 3143 } 3144 } 3145 kobj_close_file(file); 3146 return (zone_lag); 3147 } 3148 #endif /* _RTC_CONFIG */ 3149 3150 3151 /* 3152 * Append node spec to the end of par_list 3153 */ 3154 static void 3155 append(struct hwc_spec *spec, struct par_list *par) 3156 { 3157 struct hwc_spec *hwc, *last = NULL; 3158 3159 ASSERT(par->par_specs); 3160 for (hwc = par->par_specs; hwc; hwc = hwc->hwc_next) 3161 last = hwc; 3162 3163 if (last != NULL) 3164 last->hwc_next = spec; 3165 } 3166 3167 /* 3168 * Given a parent=/full-pathname, see if the platform 3169 * can resolve the pathname to driver, otherwise, try 3170 * the leaf node name. 3171 */ 3172 static major_t 3173 get_major(char *parent) 3174 { 3175 major_t major = DDI_MAJOR_T_NONE; 3176 char *tmp, *driver = NULL; 3177 3178 if (*parent == '/') 3179 major = path_to_major(parent); 3180 3181 if (major != DDI_MAJOR_T_NONE) 3182 return (major); 3183 3184 /* extract the name between '/' and '@' */ 3185 if (*parent == '/') 3186 driver = strrchr(parent, '/') + 1; 3187 else 3188 driver = parent; 3189 if ((tmp = strchr(driver, '@')) != NULL) 3190 *tmp = '\0'; 3191 major = ddi_name_to_major(driver); 3192 if (tmp) 3193 *tmp = '@'; 3194 return (major); 3195 } 3196 3197 /* 3198 * Chain together specs whose parent's module name is the same. 3199 */ 3200 static void 3201 add_spec(struct hwc_spec *spec, struct par_list **par) 3202 { 3203 major_t maj; 3204 struct par_list *pl, *par_last = NULL; 3205 char *parent = spec->hwc_parent_name; 3206 char *class = spec->hwc_class_name; 3207 3208 ASSERT(parent || class); 3209 3210 /* 3211 * If given a parent=/full-pathname, see if the platform 3212 * can resolve the pathname to driver, otherwise, try 3213 * the leaf node name. 3214 * 3215 * If parent=/full-pathname doesn't resolve to a driver, 3216 * this could be cause by DR removal of the device. 3217 * We put it on the major=-2 list in case the device 3218 * is brought back into the system by DR. 3219 */ 3220 if (parent) { 3221 maj = get_major(parent); 3222 if (maj == DDI_MAJOR_T_NONE) { 3223 if ((*parent == '/') && 3224 (strncmp(parent, "/pseudo", 7) != 0)) { 3225 maj = (major_t)-2; 3226 } else { 3227 cmn_err(CE_WARN, 3228 "add_spec: No major number for %s", 3229 parent); 3230 hwc_free(spec); 3231 return; 3232 } 3233 } 3234 } else 3235 maj = DDI_MAJOR_T_NONE; 3236 3237 /* 3238 * Scan the list looking for a matching parent. When parent is 3239 * not NULL, we match the parent by major. If parent is NULL but 3240 * class is not NULL, we mache the pl by class name. 3241 */ 3242 for (pl = *par; pl; pl = pl->par_next) { 3243 if ((parent && (maj == pl->par_major)) || ((parent == NULL) && 3244 class && pl->par_specs->hwc_class_name && (strncmp(class, 3245 pl->par_specs->hwc_class_name, strlen(class)) == 0))) { 3246 append(spec, pl); 3247 return; 3248 } 3249 par_last = pl; 3250 } 3251 3252 /* 3253 * Didn't find a match on the list. Make a new parent list. 3254 */ 3255 pl = kmem_zalloc(sizeof (*pl), KM_SLEEP); 3256 pl->par_major = maj; 3257 pl->par_specs = spec; 3258 if (*par == NULL) { /* null par list */ 3259 *par = pl; 3260 return; 3261 } 3262 /* put "class=" entries last (lower pri if dups) */ 3263 if (maj == DDI_MAJOR_T_NONE) { 3264 par_last->par_next = pl; 3265 return; 3266 } 3267 3268 /* ensure unresolved "parent=/full-path" goes first */ 3269 if ((maj != (major_t)-2) && ((*par)->par_major == (major_t)-2)) 3270 par = &(*par)->par_next; 3271 pl->par_next = *par; 3272 *par = pl; 3273 } 3274 3275 /* 3276 * Add property spec to property list in original order 3277 */ 3278 static void 3279 add_props(struct hwc_spec *spec, ddi_prop_t **props) 3280 { 3281 ASSERT(spec->hwc_devi_name == NULL); 3282 3283 if (spec->hwc_devi_sys_prop_ptr) { 3284 while (*props) 3285 props = &(*props)->prop_next; 3286 *props = spec->hwc_devi_sys_prop_ptr; 3287 3288 /* remove these properties from the spec */ 3289 spec->hwc_devi_sys_prop_ptr = NULL; 3290 } 3291 hwc_free(spec); 3292 } 3293