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