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