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