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