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