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