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