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