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