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