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