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