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