xref: /illumos-gate/usr/src/uts/common/os/modsysfile.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright 2016 Nexenta Systems, Inc.
26  */
27 
28 #include <sys/types.h>
29 #include <sys/inttypes.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/user.h>
33 #include <sys/disp.h>
34 #include <sys/conf.h>
35 #include <sys/bootconf.h>
36 #include <sys/sysconf.h>
37 #include <sys/sunddi.h>
38 #include <sys/esunddi.h>
39 #include <sys/ddi_impldefs.h>
40 #include <sys/kmem.h>
41 #include <sys/vmem.h>
42 #include <sys/fs/ufs_fsdir.h>
43 #include <sys/hwconf.h>
44 #include <sys/modctl.h>
45 #include <sys/cmn_err.h>
46 #include <sys/kobj.h>
47 #include <sys/kobj_lex.h>
48 #include <sys/errno.h>
49 #include <sys/debug.h>
50 #include <sys/autoconf.h>
51 #include <sys/callb.h>
52 #include <sys/sysmacros.h>
53 #include <sys/dacf.h>
54 #include <vm/seg_kmem.h>
55 
56 struct hwc_class *hcl_head;	/* head of list of classes */
57 static kmutex_t hcl_lock;	/* for accessing list of classes */
58 
59 #define	DAFILE		"/etc/driver_aliases"
60 #define	CLASSFILE	"/etc/driver_classes"
61 #define	DACFFILE	"/etc/dacf.conf"
62 
63 static char class_file[] = CLASSFILE;
64 static char dafile[] = DAFILE;
65 static char dacffile[] = DACFFILE;
66 
67 char *systemfile = "/etc/system";	/* name of ascii system file */
68 
69 static struct sysparam *sysparam_hd;	/* head of parameters list */
70 static struct sysparam *sysparam_tl;	/* tail of parameters list */
71 static vmem_t *mod_sysfile_arena;	/* parser memory */
72 
73 char obp_bootpath[BO_MAXOBJNAME];	/* bootpath from obp */
74 
75 #if defined(_PSM_MODULES)
76 
77 struct psm_mach {
78 	struct psm_mach *m_next;
79 	char		*m_machname;
80 };
81 
82 static struct psm_mach *pmach_head;	/* head of list of classes */
83 
84 #define	MACHFILE	"/etc/mach"
85 static char mach_file[] = MACHFILE;
86 
87 #endif	/* _PSM_MODULES */
88 
89 #if defined(_RTC_CONFIG)
90 static char rtc_config_file[] = "/etc/rtc_config";
91 #endif
92 
93 static void sys_set_var(int, struct sysparam *, void *);
94 
95 static void setparams(void);
96 
97 /*
98  * driver.conf parse thread control structure
99  */
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 */
106 };
107 
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 **);
115 
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);
121 
122 #ifdef DEBUG
123 static int parse_debug_on = 0;
124 
125 /*VARARGS1*/
126 static void
127 parse_debug(struct _buf *file, char *fmt, ...)
128 {
129 	va_list adx;
130 
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);
138 	}
139 }
140 #endif /* DEBUG */
141 
142 #define	FE_BUFLEN 256
143 
144 /*PRINTFLIKE3*/
145 void
146 kobj_file_err(int type,  struct _buf *file, char *fmt, ...)
147 {
148 	va_list ap;
149 	/*
150 	 * If we're in trouble, we might be short on stack... be paranoid
151 	 */
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';
156 
157 	va_start(ap, fmt);
158 	if (strchr("^!?", fmt[0]) != NULL) {
159 		prefix = fmt[0];
160 		fmt++;
161 	}
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));
166 
167 	/*
168 	 * If prefixed with !^?, prepend that character
169 	 */
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);
174 	}
175 
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);
180 }
181 
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"
201 };
202 #endif /* DEBUG */
203 
204 token_t
205 kobj_lex(struct _buf *file, char *val, size_t size)
206 {
207 	char	*cp;
208 	int	ch, oval, badquote;
209 	size_t	remain;
210 	token_t token = UNEXPECTED;
211 
212 	if (size < 2)
213 		return (token);	/* this token is UNEXPECTED */
214 
215 	cp = val;
216 	while ((ch = kobj_getc(file)) == ' ' || ch == '\t')
217 		;
218 
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;
257 			}
258 			*cp++ = (char)ch;
259 		}
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;
283 
284 			case '\\':
285 				if (--remain == 0) {
286 					token = UNEXPECTED;
287 					goto out;
288 				}
289 				ch = (char)kobj_getc(file);
290 				if (!isdigit(ch)) {
291 					/* escape the character */
292 					*cp++ = (char)ch;
293 					break;
294 				}
295 				oval = 0;
296 				while (ch >= '0' && ch <= '7') {
297 					ch -= '0';
298 					oval = (oval << 3) + ch;
299 					ch = (char)kobj_getc(file);
300 				}
301 				(void) kobj_ungetc(file);
302 				/* check for character overflow? */
303 				if (oval > 127) {
304 					cmn_err(CE_WARN,
305 					    "Character "
306 					    "overflow detected.");
307 				}
308 				*cp++ = (char)oval;
309 				break;
310 			default:
311 				if (--remain == 0) {
312 					token = UNEXPECTED;
313 					goto out;
314 				}
315 				*cp++ = (char)ch;
316 				break;
317 			}
318 		}
319 		token = STRING;
320 		break;
321 
322 	case -1:
323 		token = EOF;
324 		break;
325 
326 	default:
327 		/*
328 		 * detect a lone '-' (including at the end of a line), and
329 		 * identify it as a 'name'
330 		 */
331 		if (ch == '-') {
332 			if (--remain == 0) {
333 				token = UNEXPECTED;
334 				goto out;
335 			}
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;
343 			}
344 		} else if (isunary(ch)) {
345 			if (--remain == 0) {
346 				token = UNEXPECTED;
347 				goto out;
348 			}
349 			*cp++ = (char)(ch = kobj_getc(file));
350 		}
351 
352 
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;
359 					}
360 					*cp++ = (char)ch;
361 					ch = kobj_getc(file);
362 					while (isxdigit(ch)) {
363 						if (--remain == 0) {
364 							token = UNEXPECTED;
365 							goto out;
366 						}
367 						*cp++ = (char)ch;
368 						ch = kobj_getc(file);
369 					}
370 					(void) kobj_ungetc(file);
371 					token = HEXVAL;
372 				} else {
373 					goto digit;
374 				}
375 			} else {
376 				ch = kobj_getc(file);
377 digit:
378 				while (isdigit(ch)) {
379 					if (--remain == 0) {
380 						token = UNEXPECTED;
381 						goto out;
382 					}
383 					*cp++ = (char)ch;
384 					ch = kobj_getc(file);
385 				}
386 				(void) kobj_ungetc(file);
387 				token = DECVAL;
388 			}
389 		} else if (isalpha(ch) || ch == '\\' || ch == '_') {
390 			if (ch != '\\') {
391 				ch = kobj_getc(file);
392 			} else {
393 				/*
394 				 * if the character was a backslash,
395 				 * back up so we can overwrite it with
396 				 * the next (i.e. escaped) character.
397 				 */
398 				remain++;
399 				cp--;
400 			}
401 			while (isnamechar(ch) || ch == '\\') {
402 				if (ch == '\\')
403 					ch = kobj_getc(file);
404 				if (--remain == 0) {
405 					token = UNEXPECTED;
406 					goto out;
407 				}
408 				*cp++ = (char)ch;
409 				ch = kobj_getc(file);
410 			}
411 			(void) kobj_ungetc(file);
412 			token = NAME;
413 		} else {
414 			token = UNEXPECTED;
415 		}
416 		break;
417 	}
418 out:
419 	*cp = '\0';
420 
421 #ifdef DEBUG
422 	/*
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.
426 	 */
427 	parse_debug(NULL, "kobj_lex: token %s value '%s'\n",
428 	    tokennames[token+1], val);
429 #endif
430 	return (token);
431 }
432 
433 /*
434  * Leave NEWLINE as the next character.
435  */
436 
437 void
438 kobj_find_eol(struct _buf *file)
439 {
440 	int ch;
441 
442 	while ((ch = kobj_getc(file)) != -1) {
443 		if (isnewline(ch)) {
444 			(void) kobj_ungetc(file);
445 			break;
446 		}
447 	}
448 }
449 
450 /*
451  * The ascii system file is read and processed.
452  *
453  * The syntax of commands is as follows:
454  *
455  * '*' in column 1 is a comment line.
456  * <command> : <value>
457  *
458  * command is EXCLUDE, INCLUDE, FORCELOAD, ROOTDEV, ROOTFS,
459  *	SWAPDEV, SWAPFS, MODDIR, SET
460  *
461  * value is an ascii string meaningful for the command.
462  */
463 
464 /*
465  * Table of commands
466  */
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	}
491 };
492 
493 
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";
499 
500 static struct sysparam *
501 do_sysfile_cmd(struct _buf *file, const char *cmd)
502 {
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];
510 
511 	for (mcp = modcmd; mcp->mc_cmdname != NULL; mcp++) {
512 		if (strcmp(mcp->mc_cmdname, cmd) == 0)
513 			break;
514 	}
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 */
519 
520 	switch (sysp->sys_type = mcp->mc_type) {
521 	case MOD_INCLUDE:
522 	case MOD_EXCLUDE:
523 	case MOD_FORCELOAD:
524 		/*
525 		 * Are followed by colon.
526 		 */
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);
533 		}
534 		if (token != NAME) {
535 			kobj_file_err(CE_WARN, file, "value expected");
536 			goto bad;
537 		}
538 
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;
545 			}
546 			*cp++ = (char)ch;
547 		}
548 		*cp = '\0';
549 
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:
561 	{
562 		char *var;
563 		token_t tok3;
564 
565 		if (kobj_lex(file, tok1, sizeof (tok1)) != NAME) {
566 			kobj_file_err(CE_WARN, file, "value expected");
567 			goto bad;
568 		}
569 
570 		/*
571 		 * If the next token is a colon (:),
572 		 * we have the <modname>:<variable> construct.
573 		 */
574 		if ((token = kobj_lex(file, tok2, sizeof (tok2))) == COLON) {
575 			if ((token = kobj_lex(file, tok2,
576 			    sizeof (tok2))) == NAME) {
577 				var = tok2;
578 				/*
579 				 * Save the module name.
580 				 */
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;
588 			}
589 		} else {
590 			/* otherwise, it was the op */
591 			var = tok1;
592 			op = token;
593 		}
594 		/*
595 		 * kernel param - place variable name in sys_ptr.
596 		 */
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;
619 		}
620 
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;
627 			}
628 			if (kobj_get_string(&sysp->sys_info, tok1) == 0) {
629 				kobj_file_err(CE_WARN, file, "string garbled");
630 				goto bad;
631 			}
632 			/*
633 			 * Set SYSPARAM_STR_TOKEN in sys_flags to notify
634 			 * sysparam_print_warning() that this is a string
635 			 * token.
636 			 */
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;
645 			}
646 
647 			/*
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.
651 			 */
652 			if (tok3 == HEXVAL) {
653 				sysp->sys_flags |= SYSPARAM_HEX_TOKEN;
654 			} else {
655 				sysp->sys_flags |= SYSPARAM_DEC_TOKEN;
656 			}
657 			break;
658 		default:
659 			kobj_file_err(CE_WARN, file, "bad rvalue '%s'", tok1);
660 			goto bad;
661 		} /* end switch */
662 
663 		/*
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)
667 		 */
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;
677 	}
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;
682 		}
683 
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;
690 			}
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;
698 				}
699 				*cp++ = (char)ch;
700 			}
701 			*cp++ = ' ';
702 			if (isnewline(ch)) {
703 				cp--;
704 				(void) kobj_ungetc(file);
705 			}
706 		}
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;
713 
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;
719 		}
720 		while ((ch = kobj_getc(file)) == ' ' || ch == '\t')
721 			;
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;
727 			}
728 
729 			*cp++ = (char)ch;
730 			ch = kobj_getc(file);
731 		}
732 		if (ch != -1)
733 			(void) kobj_ungetc(file);
734 		*cp = '\0';
735 
736 		sysp->sys_ptr = vmem_alloc(mod_sysfile_arena, strlen(tok1) + 1,
737 		    VM_SLEEP);
738 		(void) strcpy(sysp->sys_ptr, tok1);
739 		break;
740 
741 	case MOD_UNKNOWN:
742 	default:
743 		kobj_file_err(CE_WARN, file, "unknown command '%s'", cmd);
744 		goto bad;
745 	}
746 
747 	return (sysp);
748 
749 bad:
750 	kobj_find_eol(file);
751 	return (NULL);
752 }
753 
754 void
755 mod_read_system_file(int ask)
756 {
757 	register struct sysparam *sp;
758 	register struct _buf *file;
759 	register token_t token, last_tok;
760 	char tokval[MAXLINESIZE];
761 
762 	mod_sysfile_arena = vmem_create("mod_sysfile", NULL, 0, 8,
763 	    segkmem_alloc, segkmem_free, heap_arena, 0, VM_SLEEP);
764 
765 	if (ask)
766 		mod_askparams();
767 
768 	if (systemfile != NULL) {
769 
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;
776 
777 			last_tok = NEWLINE;
778 			while ((token = kobj_lex(file, tokval,
779 			    sizeof (tokval))) != EOF) {
780 				switch (token) {
781 				case STAR:
782 				case POUND:
783 					/*
784 					 * Skip comments.
785 					 */
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;
802 					}
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;
810 				}
811 			}
812 			kobj_close_file(file);
813 		}
814 	}
815 
816 	/*
817 	 * Sanity check of /etc/system.
818 	 */
819 	check_system_file();
820 
821 	param_preset();
822 	(void) mod_sysctl(SYS_SET_KVAR, NULL);
823 	param_check();
824 
825 	if (ask == 0)
826 		setparams();
827 }
828 
829 /*
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.
834  *
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).
837  */
838 int
839 mod_sysvar(const char *module, const char *name, u_longlong_t *value)
840 {
841 	struct sysparam	*sysp;
842 	int cnt = 0; /* dummy */
843 
844 	ASSERT(name != NULL);
845 	ASSERT(value != NULL);
846 	for (sysp = sysparam_hd; sysp != NULL; sysp = sysp->sys_next) {
847 
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)))) {
852 
853 			ASSERT(sysp->sys_ptr != NULL);
854 
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;
860 			}
861 		}
862 	}
863 	ASSERT(cnt == 0);
864 	return (0);
865 }
866 
867 /*
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.
873  */
874 static void
875 check_system_file(void)
876 {
877 	struct sysparam	*sysp;
878 
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;
883 		/*
884 		 * If the entry is already checked, skip it.
885 		 */
886 		if ((sysp->sys_flags & SYSPARAM_DUP) != 0)
887 			continue;
888 		/*
889 		 * Check if there is a duplicate entry by doing a linear
890 		 * search.
891 		 */
892 		final = sysp;
893 		for (entry = sysp->sys_next; entry != NULL;
894 		    entry = entry->sys_next) {
895 			/*
896 			 * Check the entry. if it's different, skip this.
897 			 */
898 			if (sysparam_compare_entry(sysp, entry) != 0)
899 				continue;
900 			/*
901 			 * Count the entry and put the mark.
902 			 */
903 			sysparam_count_entry(entry, &cnt, &value);
904 			entry->sys_flags |= SYSPARAM_DUP;
905 			final = entry;
906 		}
907 		final->sys_flags |= SYSPARAM_TERM;
908 		/*
909 		 * Print the warning if it's duplicated.
910 		 */
911 		if (cnt >= 2)
912 			sysparam_print_warning(final, value);
913 	}
914 }
915 
916 /*
917  * Compare the sysparam records.
918  * Return 0 if they are the same, return 1 if not.
919  */
920 static int
921 sysparam_compare_entry(struct sysparam *sysp, struct sysparam *entry)
922 {
923 	ASSERT(sysp->sys_ptr != NULL && entry->sys_ptr != NULL);
924 
925 	/*
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.
931 	 */
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;
941 	}
942 
943 	if (sysp->sys_type != entry->sys_type)
944 		return (1);
945 
946 	if (sysp->sys_modnam != NULL && entry->sys_modnam == NULL)
947 		return (1);
948 
949 	if (sysp->sys_modnam == NULL && entry->sys_modnam != NULL)
950 		return (1);
951 
952 	if (sysp->sys_modnam != NULL && entry->sys_modnam != NULL &&
953 	    strcmp(sysp->sys_modnam, entry->sys_modnam) != 0)
954 		return (1);
955 
956 	return (strcmp(sysp->sys_ptr, entry->sys_ptr));
957 }
958 
959 /*
960  * Translate a sysparam type value to a string.
961  */
962 static char *
963 sysparam_type_to_str(int type)
964 {
965 	struct modcmd *mcp;
966 
967 	for (mcp = modcmd; mcp->mc_cmdname != NULL; mcp++) {
968 		if (mcp->mc_type == type)
969 			break;
970 	}
971 	ASSERT(mcp->mc_type == type);
972 
973 	if (type != MOD_UNKNOWN)
974 		return ((++mcp)->mc_cmdname); /* lower case */
975 	else
976 		return ("");	/* MOD_UNKNOWN */
977 }
978 
979 /*
980  * Check the entry and accumulate the number of entries.
981  */
982 static void
983 sysparam_count_entry(struct sysparam *sysp, int *cnt, u_longlong_t *value)
984 {
985 	u_longlong_t ul = sysp->sys_info;
986 
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;
1001 	}
1002 }
1003 
1004 /*
1005  * Print out the warning if multiple entries are found in the system file.
1006  */
1007 static void
1008 sysparam_print_warning(struct sysparam *sysp, u_longlong_t value)
1009 {
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"
1018 
1019 	ASSERT(varnam != NULL);
1020 
1021 	if (type == MOD_SET) {
1022 		/*
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.
1027 		 */
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);
1044 			}
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);
1062 			}
1063 		}
1064 	} else {
1065 		/*
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.
1070 		 */
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;
1084 		}
1085 	}
1086 }
1087 
1088 /*
1089  * Process the system file commands.
1090  */
1091 int
1092 mod_sysctl(int fcn, void *p)
1093 {
1094 	static char wmesg[] = "forceload of %s failed";
1095 	struct sysparam *sysp;
1096 	char *name;
1097 	struct modctl *modp;
1098 
1099 	if (sysparam_hd == NULL)
1100 		return (0);
1101 
1102 	for (sysp = sysparam_hd; sysp != NULL; sysp = sysp->sys_next) {
1103 
1104 		switch (fcn) {
1105 
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);
1111 			/*
1112 			 * The following works because it
1113 			 * runs before autounloading is started!!
1114 			 */
1115 			modp = mod_find_by_filename(NULL, name);
1116 			if (modp != NULL)
1117 				modp->mod_loadflags |= MOD_NOAUTOUNLOAD;
1118 			/*
1119 			 * For drivers, attempt to install it.
1120 			 */
1121 			if (strncmp(sysp->sys_ptr, "drv", 3) == 0) {
1122 				(void) ddi_install_driver(name + 4);
1123 			}
1124 		}
1125 		break;
1126 
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;
1132 
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);
1139 			}
1140 		}
1141 	}
1142 
1143 	return (0);
1144 }
1145 
1146 /*
1147  * Process the system file commands, by type.
1148  */
1149 int
1150 mod_sysctl_type(int type, int (*func)(struct sysparam *, void *), void *p)
1151 {
1152 	struct sysparam *sysp;
1153 	int	err;
1154 
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);
1160 }
1161 
1162 
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";
1166 
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 *);
1171 
1172 static void
1173 sys_set_var(int fcn, struct sysparam *sysp, void *p)
1174 {
1175 	uintptr_t symaddr;
1176 	int size;
1177 
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 == (char *)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;
1189 
1190 	if (symaddr != 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;
1210 		}
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");
1218 	}
1219 }
1220 
1221 static void
1222 set_int8_var(uintptr_t symaddr, struct sysparam *sysp)
1223 {
1224 	uint8_t uc = (uint8_t)sysp->sys_info;
1225 
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);
1230 
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;
1241 	}
1242 
1243 	if (moddebug & MODDEBUG_LOADMSG)
1244 		printf("now it is set to '0x%" PRIx8 "'.\n",
1245 		    *(uint8_t *)symaddr);
1246 }
1247 
1248 static void
1249 set_int16_var(uintptr_t symaddr, struct sysparam *sysp)
1250 {
1251 	uint16_t us = (uint16_t)sysp->sys_info;
1252 
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);
1257 
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;
1268 	}
1269 
1270 	if (moddebug & MODDEBUG_LOADMSG)
1271 		printf("now it is set to '0x%" PRIx16 "'.\n",
1272 		    *(uint16_t *)symaddr);
1273 }
1274 
1275 static void
1276 set_int32_var(uintptr_t symaddr, struct sysparam *sysp)
1277 {
1278 	uint32_t ui = (uint32_t)sysp->sys_info;
1279 
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);
1284 
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;
1295 	}
1296 
1297 	if (moddebug & MODDEBUG_LOADMSG)
1298 		printf("now it is set to '0x%" PRIx32 "'.\n",
1299 		    *(uint32_t *)symaddr);
1300 }
1301 
1302 static void
1303 set_int64_var(uintptr_t symaddr, struct sysparam *sysp)
1304 {
1305 	uint64_t ul = sysp->sys_info;
1306 
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);
1311 
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;
1322 	}
1323 
1324 	if (moddebug & MODDEBUG_LOADMSG)
1325 		printf("now it is set to '0x%" PRIx64 "'.\n",
1326 		    *(uint64_t *)symaddr);
1327 }
1328 
1329 /*
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.
1333  */
1334 int
1335 kobj_get_string(u_longlong_t *llptr, char *tchar)
1336 {
1337 	char *cp;
1338 	char *start = (char *)0;
1339 	int len = 0;
1340 
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;
1373 			}
1374 		} else
1375 			*cp++ = *start++;
1376 	}
1377 	*cp = '\0';
1378 	return (1);
1379 }
1380 
1381 
1382 /*
1383  * this function frees the memory allocated by kobj_get_string
1384  */
1385 void
1386 kobj_free_string(void *ptr, int len)
1387 {
1388 	vmem_free(mod_sysfile_arena, ptr, len);
1389 }
1390 
1391 
1392 /*
1393  * get a decimal octal or hex number. Handle '~' for one's complement.
1394  */
1395 int
1396 kobj_getvalue(const char *token, u_longlong_t *valuep)
1397 {
1398 	int radix;
1399 	u_longlong_t retval = 0;
1400 	int onescompl = 0;
1401 	int negate = 0;
1402 	char c;
1403 
1404 	if (*token == '~') {
1405 		onescompl++; /* perform one's complement on result */
1406 		token++;
1407 	} else if (*token == '-') {
1408 		negate++;
1409 		token++;
1410 	}
1411 	if (*token == '0') {
1412 		token++;
1413 		c = *token;
1414 
1415 		if (c == '\0') {
1416 			*valuep = 0;	/* value is 0 */
1417 			return (0);
1418 		}
1419 
1420 		if (c == 'x' || c == 'X') {
1421 			radix = 16;
1422 			token++;
1423 		} else
1424 			radix = 8;
1425 	} else
1426 		radix = 10;
1427 
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;
1455 		}
1456 	}
1457 	if (onescompl)
1458 		retval = ~retval;
1459 	if (negate)
1460 		retval = -retval;
1461 	*valuep = retval;
1462 	return (0);
1463 }
1464 
1465 /*
1466  * Path to the root device and root filesystem type from
1467  * property information derived from the boot subsystem
1468  */
1469 void
1470 setbootpath(char *path)
1471 {
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));
1475 }
1476 
1477 void
1478 setbootfstype(char *fstype)
1479 {
1480 	(void) copystr(fstype, rootfs.bo_fstype, BO_MAXFSNAME, NULL);
1481 	BMDPRINTF(("rootfs fstype: %s\n", rootfs.bo_fstype));
1482 }
1483 
1484 /*
1485  * set parameters that can be set early during initialization.
1486  */
1487 static void
1488 setparams()
1489 {
1490 	struct sysparam *sysp;
1491 	struct bootobj *bootobjp;
1492 
1493 	for (sysp = sysparam_hd; sysp != NULL; sysp = sysp->sys_next) {
1494 
1495 		if (sysp->sys_type == MOD_MODDIR) {
1496 			default_path = sysp->sys_ptr;
1497 			continue;
1498 		}
1499 
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;
1505 
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;
1521 		}
1522 	}
1523 }
1524 
1525 /*
1526  * clean up after an error.
1527  */
1528 static void
1529 hwc_free(struct hwc_spec *hwcp)
1530 {
1531 	char *name;
1532 
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));
1541 }
1542 
1543 /*
1544  * Free a list of specs
1545  */
1546 void
1547 hwc_free_spec_list(struct hwc_spec *list)
1548 {
1549 	while (list) {
1550 		struct hwc_spec *tmp = list;
1551 		list = tmp->hwc_next;
1552 		hwc_free(tmp);
1553 	}
1554 }
1555 
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;
1567 };
1568 
1569 static struct val_list *
1570 add_val(struct val_list **val_listp, struct val_list *tail,
1571     int val_type, caddr_t val)
1572 {
1573 	struct val_list *new_val;
1574 #ifdef DEBUG
1575 	struct val_list *listp = *val_listp;
1576 #endif
1577 
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;
1587 	}
1588 
1589 	ASSERT((listp == NULL && tail == NULL) ||
1590 	    (listp != NULL && tail != NULL));
1591 
1592 	if (tail != NULL) {
1593 		ASSERT(tail->val_next == NULL);
1594 		tail->val_next = new_val;
1595 	} else {
1596 		*val_listp = new_val;
1597 	}
1598 
1599 	return (new_val);
1600 }
1601 
1602 static void
1603 free_val_list(struct val_list *head)
1604 {
1605 	struct val_list *tval_list;
1606 
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));
1613 	}
1614 }
1615 
1616 /*
1617  * make sure there are no reserved IEEE 1275 characters (except
1618  * for uppercase characters).
1619  */
1620 static int
1621 valid_prop_name(char *name)
1622 {
1623 	int i;
1624 	int len = strlen(name);
1625 
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);
1635 	}
1636 	return (1);
1637 }
1638 
1639 static void
1640 make_prop(struct _buf *file, dev_info_t *devi, char *name, struct val_list *val)
1641 {
1642 	int propcnt = 0, val_type;
1643 	struct val_list *vl, *tvl;
1644 	caddr_t valbuf = NULL;
1645 	char **valsp;
1646 	int *valip;
1647 
1648 	if (name == NULL)
1649 		return;
1650 
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;
1657 	}
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;
1663 			}
1664 			propcnt++;
1665 		}
1666 
1667 		vl = val;
1668 
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++;
1681 			}
1682 			/* restore valip */
1683 			valip = (int *)valbuf;
1684 
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);
1690 			}
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++;
1705 			}
1706 			/* terminate array with NULL */
1707 			*valsp = NULL;
1708 
1709 			/* restore valsp */
1710 			valsp = (char **)valbuf;
1711 
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);
1718 			}
1719 			/* Clean up */
1720 			kmem_free(valsp, ((propcnt + 1) * sizeof (char *)));
1721 		} else {
1722 			cmn_err(CE_WARN, "Invalid property type");
1723 			return;
1724 		}
1725 	} else {
1726 		/*
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.
1730 		 */
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);
1738 		}
1739 	}
1740 }
1741 
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";
1746 
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;
1755 
1756 static struct hwc_spec *
1757 get_hwc_spec(struct _buf *file, char *tokbuf, size_t linesize)
1758 {
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;
1766 
1767 	hwcp = kmem_zalloc(sizeof (*hwcp), KM_SLEEP);
1768 	devi = kmem_zalloc(sizeof (*devi), KM_SLEEP);
1769 
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;
1791 				}
1792 				if (val_list) {
1793 					free_val_list(val_list);
1794 					val_list = NULL;
1795 				}
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);
1816 				}
1817 				break;
1818 			default:
1819 				kobj_file_err(CE_WARN, file, tok_err, tokbuf);
1820 			}
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);
1838 			}
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;
1847 				}
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;
1858 				}
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;
1868 				}
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);
1881 			}
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);
1895 			}
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);
1907 			}
1908 			break;
1909 		case NEWLINE:
1910 			kobj_newline(file);
1911 			break;
1912 		case POUND:
1913 			/*
1914 			 * Skip comments.
1915 			 */
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;
1924 		}
1925 	} while ((token = kobj_lex(file, tokbuf, linesize)) != SEMICOLON);
1926 
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;
1934 
1935 	case hwc_begin:
1936 		break;
1937 	default:
1938 		kobj_file_err(CE_WARN, file, "Unexpected end of line");
1939 		break;
1940 	}
1941 
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;
1945 
1946 	if (prop_name)
1947 		kmem_free(prop_name, strlen(prop_name) + 1);
1948 	if (val_list)
1949 		free_val_list(val_list);
1950 
1951 	kmem_free(devi, sizeof (struct dev_info));
1952 
1953 	return (hwcp);
1954 
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);
1960 
1961 	hwc_free(hwcp);
1962 
1963 	if (devi->devi_name)
1964 		kmem_free(devi->devi_name, strlen(devi->devi_name) + 1);
1965 
1966 	kmem_free(devi, sizeof (struct dev_info));
1967 
1968 	return (NULL);
1969 }
1970 
1971 /*
1972  * This is the primary kernel interface to parse driver.conf files.
1973  *
1974  * Yet another bigstk thread handoff due to deep kernel stacks when booting
1975  * cache-only-clients.
1976  */
1977 int
1978 hwc_parse(char *fname, struct par_list **pl, ddi_prop_t **props)
1979 {
1980 	int ret;
1981 	struct hwc_parse_mt *pltp = hwc_parse_mtalloc(fname, pl, props);
1982 
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);
1989 	}
1990 	ret = pltp->rv;
1991 	hwc_parse_mtfree(pltp);
1992 	return (ret);
1993 }
1994 
1995 /*
1996  * Calls to hwc_parse() are handled off to this routine in a separate
1997  * thread.
1998  */
1999 static void
2000 hwc_parse_thread(struct hwc_parse_mt *pltp)
2001 {
2002 	kmutex_t	cpr_lk;
2003 	callb_cpr_t	cpr_i;
2004 
2005 	mutex_init(&cpr_lk, NULL, MUTEX_DEFAULT, NULL);
2006 	CALLB_CPR_INIT(&cpr_i, &cpr_lk, callb_generic_cpr, "hwc_parse");
2007 
2008 	/*
2009 	 * load and parse the .conf file
2010 	 * return the hwc_spec list (if any) to the creator of this thread
2011 	 */
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();
2018 }
2019 
2020 /*
2021  * allocate and initialize a hwc_parse thread control structure
2022  */
2023 static struct hwc_parse_mt *
2024 hwc_parse_mtalloc(char *name, struct par_list **pl, ddi_prop_t **props)
2025 {
2026 	struct hwc_parse_mt *pltp = kmem_zalloc(sizeof (*pltp), KM_SLEEP);
2027 
2028 	ASSERT(name != NULL);
2029 
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;
2034 
2035 	sema_init(&pltp->sema, 0, NULL, SEMA_DEFAULT, NULL);
2036 	return (pltp);
2037 }
2038 
2039 /*
2040  * free a hwc_parse thread control structure
2041  */
2042 static void
2043 hwc_parse_mtfree(struct hwc_parse_mt *pltp)
2044 {
2045 	sema_destroy(&pltp->sema);
2046 
2047 	kmem_free(pltp->name, strlen(pltp->name) + 1);
2048 	kmem_free(pltp, sizeof (*pltp));
2049 }
2050 
2051 /*
2052  * hwc_parse -- parse an hwconf file.  Ignore error lines and parse
2053  * as much as possible.
2054  */
2055 static int
2056 hwc_parse_now(char *fname, struct par_list **pl, ddi_prop_t **props)
2057 {
2058 	struct _buf *file;
2059 	struct hwc_spec *hwcp;
2060 	char *tokval;
2061 	token_t token;
2062 
2063 	/*
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.
2067 	 */
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);
2072 	}
2073 
2074 	/*
2075 	 * Initialize variables
2076 	 */
2077 	tokval = kmem_alloc(MAX_HWC_LINESIZE, KM_SLEEP);
2078 
2079 	while ((token = kobj_lex(file, tokval, MAX_HWC_LINESIZE)) != EOF) {
2080 		switch (token) {
2081 		case POUND:
2082 			/*
2083 			 * Skip comments.
2084 			 */
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;
2091 			/*
2092 			 * No devi_name indicates global property.
2093 			 * Make sure parent and class not NULL.
2094 			 */
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;
2102 				}
2103 				/* Add to global property list */
2104 				add_props(hwcp, props);
2105 				break;
2106 			}
2107 
2108 			/*
2109 			 * This is a node spec, either parent or class
2110 			 * must be specified.
2111 			 */
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;
2118 			}
2119 
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;
2129 		}
2130 	}
2131 	/*
2132 	 * XXX - Check for clean termination.
2133 	 */
2134 	kmem_free(tokval, MAX_HWC_LINESIZE);
2135 	kobj_close_file(file);
2136 	return (0);	/* always return success */
2137 }
2138 
2139 void
2140 make_aliases(struct bind **bhash)
2141 {
2142 	enum {
2143 		AL_NEW, AL_DRVNAME, AL_DRVNAME_COMMA, AL_ALIAS, AL_ALIAS_COMMA
2144 	} state;
2145 
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.";
2154 
2155 	if ((file = kobj_open_file(dafile)) == (struct _buf *)-1)
2156 		return;
2157 
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:
2164 			/*
2165 			 * Skip comments.
2166 			 */
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;
2192 				}
2193 				break;
2194 			case AL_ALIAS:
2195 				if (make_mbind(drvbuf, major, NULL, bhash)
2196 				    != 0) {
2197 					cmn_err(CE_WARN, dupwarn, drvbuf);
2198 				}
2199 				/*
2200 				 * copy this token just in case that there
2201 				 * are multiple names on the same line.
2202 				 */
2203 				(void) strcpy(drvbuf, tokbuf);
2204 				break;
2205 			}
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);
2218 			}
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);
2228 				}
2229 			} else if (state != AL_NEW) {
2230 				kobj_file_err(CE_WARN, file,
2231 				    "Missing alias for %s", drvbuf);
2232 			}
2233 
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);
2240 		}
2241 	}
2242 
2243 	kobj_close_file(file);
2244 }
2245 
2246 
2247 /*
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".
2256  */
2257 int
2258 read_binding_file(char *bindfile, struct bind **hashtab,
2259     int (*line_parser)(char *, int, char *, struct bind **))
2260 {
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;
2271 
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";
2275 
2276 	if (hashtab != NULL) {
2277 		clear_binding_hash(hashtab);
2278 	}
2279 
2280 	if ((file = kobj_open_file(bindfile)) == (struct _buf *)-1)
2281 		panic("read_binding_file: %s file not found", bindfile);
2282 
2283 	state = B_NEW;
2284 
2285 	while (!done) {
2286 		token = kobj_lex(file, tokbuf, sizeof (tokbuf));
2287 
2288 		switch (token) {
2289 		case POUND:
2290 			/*
2291 			 * Skip comments.
2292 			 */
2293 			kobj_find_eol(file);
2294 			break;
2295 		case NAME:
2296 		case STRING:
2297 			switch (state) {
2298 			case B_NEW:
2299 				/*
2300 				 * This case is for the first name and
2301 				 * possibly only name in an entry.
2302 				 */
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:
2309 				/*
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.
2313 				 */
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);
2322 			}
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;
2330 			}
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;
2337 			}
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;
2349 
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);
2357 				}
2358 			} else if (state != B_NEW)
2359 				kobj_file_err(CE_WARN, file, "Syntax error?");
2360 
2361 			if (name) {
2362 				kmem_free(name, strlen(name) + 1);
2363 				name = NULL;
2364 			}
2365 			if (bind_name) {
2366 				kmem_free(bind_name, strlen(bind_name) + 1);
2367 				bind_name = NULL;
2368 			}
2369 			state = B_NEW;
2370 			kobj_newline(file);
2371 			break;
2372 		default:
2373 			kobj_file_err(CE_WARN, file, "Missing name/number?");
2374 			break;
2375 		}
2376 	}
2377 
2378 	ASSERT(name == NULL);		/* any leaks? */
2379 	ASSERT(bind_name == NULL);
2380 
2381 	kobj_close_file(file);
2382 	return (maxnum);
2383 }
2384 
2385 /*
2386  * read_dacf_binding_file()
2387  * 	Read the /etc/dacf.conf file and build the dacf_rule_t database from it.
2388  *
2389  * The syntax of a line in the dacf.conf file is:
2390  *   dev-spec 	[module:]op-set	operation options 	[config-args];
2391  *
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"
2397  */
2398 int
2399 read_dacf_binding_file(char *filename)
2400 {
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;
2415 
2416 	struct _buf *file;
2417 	char *fname;
2418 	token_t token;
2419 
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];
2425 
2426 	uint_t opts = 0;
2427 	dacf_devspec_t nt_spec_type = DACF_DS_ERROR;
2428 
2429 	dacf_arg_t *arg_list = NULL;
2430 	dacf_opid_t opid = DACF_OPID_ERROR;
2431 	int done = 0;
2432 
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";
2444 
2445 	if (filename == NULL) {
2446 		fname = dacffile;	/* default binding file */
2447 	} else {
2448 		fname = filename;	/* user specified */
2449 	}
2450 
2451 	if ((file = kobj_open_file(fname)) == (struct _buf *)-1) {
2452 		return (ENOENT);
2453 	}
2454 
2455 	if (dacfdebug & DACF_DBG_MSGS) {
2456 		printf("dacf debug: clearing rules database\n");
2457 	}
2458 
2459 	mutex_enter(&dacf_lock);
2460 	dacf_clear_rules();
2461 
2462 	if (dacfdebug & DACF_DBG_MSGS) {
2463 		printf("dacf debug: parsing %s\n", fname);
2464 	}
2465 
2466 	while (!done) {
2467 		token = kobj_lex(file, tokbuf, sizeof (tokbuf));
2468 
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;
2475 			}
2476 			state = DACF_COMMENT;
2477 			kobj_find_eol(file);
2478 			break;
2479 
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;
2491 			}
2492 			break;
2493 
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;
2503 				}
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:
2513 				/*
2514 				 * This handles the 'optional' modname.
2515 				 * What we thought was the modname is really
2516 				 * the op-set.  So it is copied over.
2517 				 */
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;
2523 				/*
2524 				 * Now, the token we just read is the opset,
2525 				 * so look that up and fill in opid
2526 				 */
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;
2533 				}
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;
2549 				}
2550 				state = DACF_OP_NAME;
2551 				break;
2552 			case DACF_OP_NAME:
2553 				/*
2554 				 * This case is just like DACF_OPT_COMMA below,
2555 				 * but we check for the sole '-' argument
2556 				 */
2557 				if (strcmp(tokbuf, "-") == 0) {
2558 					state = DACF_OPT_END;
2559 					break;
2560 				}
2561 				/*FALLTHROUGH*/
2562 			case DACF_OPT_COMMA:
2563 				/*
2564 				 * figure out what option was given, but don't
2565 				 * make a federal case if invalid, just skip it
2566 				 */
2567 				if (dacf_getopt(tokbuf, &opts) != 0) {
2568 					kobj_file_err(CE_WARN, file, w_badopt,
2569 					    tokbuf);
2570 				}
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:
2581 				/*
2582 				 * Add the arg.  Warn if it's a duplicate
2583 				 */
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);
2588 				}
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;
2595 			}
2596 			break;
2597 
2598 		case STRING:
2599 			/*
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.
2604 			 */
2605 			if (strchr(tokbuf, '\n')) {
2606 				state = DACF_ERR;
2607 				break;
2608 			}
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;
2616 				}
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:
2623 				/*
2624 				 * Add the arg.  Warn if it's a duplicate
2625 				 */
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);
2630 				}
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;
2637 			}
2638 			break;
2639 
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;
2649 			}
2650 			break;
2651 
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;
2658 			}
2659 			break;
2660 
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;
2669 			}
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);
2674 				/*
2675 				 * We can't just do DACF_ERR here, since we'll
2676 				 * wind up eating the _next_ newline if so.
2677 				 */
2678 				state = DACF_ERR_NEWLINE;
2679 				kobj_newline(file);
2680 				break;
2681 			}
2682 
2683 			/*
2684 			 * insert the rule.
2685 			 */
2686 			if (dacf_rule_insert(nt_spec_type, nt_datap,
2687 			    mn_modnamep, mn_opsetp, opid, opts, arg_list) < 0) {
2688 				/*
2689 				 * We can't just do DACF_ERR here, since we'll
2690 				 * wind up eating the _next_ newline if so.
2691 				 */
2692 				kobj_file_err(CE_WARN, file, w_insert);
2693 				state = DACF_ERR_NEWLINE;
2694 				kobj_newline(file);
2695 				break;
2696 			}
2697 
2698 			state = DACF_BEGIN;
2699 			kobj_newline(file);
2700 			break;
2701 
2702 		default:
2703 			kobj_file_err(CE_WARN, file, w_syntax, tokbuf);
2704 			break;
2705 		} /* switch */
2706 
2707 		/*
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).
2712 		 */
2713 		if (state == DACF_ERR) {
2714 			kobj_find_eol(file);
2715 		}
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;
2725 		}
2726 	} /* while */
2727 
2728 	if (dacfdebug & DACF_DBG_MSGS) {
2729 		printf("\ndacf debug: done!\n");
2730 	}
2731 
2732 	mutex_exit(&dacf_lock);
2733 
2734 	kobj_close_file(file);
2735 	return (0);
2736 }
2737 
2738 void
2739 lock_hw_class_list()
2740 {
2741 	mutex_enter(&hcl_lock);
2742 }
2743 
2744 void
2745 unlock_hw_class_list()
2746 {
2747 	mutex_exit(&hcl_lock);
2748 }
2749 
2750 void
2751 add_class(char *exporter, char *class)
2752 {
2753 	struct hwc_class *hcl;
2754 
2755 	/*
2756 	 * If exporter's major is not registered in /etc/name_to_major,
2757 	 * don't update hwc_class, but just return here.
2758 	 */
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;
2763 	}
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();
2773 }
2774 
2775 /*
2776  * Return the number of classes exported. If buf is not NULL, fill in
2777  * the array of the class names as well.
2778  *
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.
2782  */
2783 int
2784 get_class(const char *exporter, char **buf)
2785 {
2786 	int n = 0;
2787 	struct hwc_class *hcl;
2788 
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;
2795 		}
2796 	}
2797 
2798 	return (n);
2799 }
2800 
2801 void
2802 read_class_file(void)
2803 {
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;
2813 
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));
2822 		}
2823 		hcl_head = NULL;
2824 	}
2825 
2826 	if ((file = kobj_open_file(class_file)) == (struct _buf *)-1)
2827 		return;
2828 
2829 	state = C_BEGIN;
2830 	while (!done) {
2831 		token = kobj_lex(file, tokbuf, sizeof (tokbuf));
2832 
2833 		switch (token) {
2834 		case POUND:
2835 			/*
2836 			 * Skip comments.
2837 			 */
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;
2881 		}
2882 	}
2883 	kobj_close_file(file);
2884 }
2885 
2886 /*
2887  * Given par_list, get a list of parent major number
2888  */
2889 int
2890 impl_parlist_to_major(struct par_list *pl, char parents[])
2891 {
2892 	struct hwc_spec *hwcp;
2893 	struct hwc_class *hcl;
2894 	major_t major;
2895 	int nmajor = 0;
2896 	extern int devcnt;
2897 
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;
2903 		}
2904 
2905 		/* parent specs cannot be mapped to a driver */
2906 		if (pl->par_major != DDI_MAJOR_T_NONE)
2907 			continue;
2908 
2909 		/* class spec */
2910 		hwcp = pl->par_specs;
2911 		ASSERT(hwcp->hwc_class_name);
2912 		ASSERT(hwcp->hwc_parent_name == NULL);
2913 
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++;
2922 			}
2923 		}
2924 	}
2925 	return (nmajor);
2926 }
2927 
2928 /*
2929  * delete a parent list and all its hwc specs
2930  */
2931 void
2932 impl_delete_par_list(struct par_list *pl)
2933 {
2934 	struct par_list *saved_pl;
2935 	struct hwc_spec *hp, *hp1;
2936 
2937 	while (pl) {
2938 		hp = pl->par_specs;
2939 		while (hp) {
2940 			hp1 = hp;
2941 			hp = hp->hwc_next;
2942 			hwc_free(hp1);
2943 		}
2944 		saved_pl = pl;
2945 		pl = pl->par_next;
2946 		kmem_free(saved_pl, sizeof (*saved_pl));
2947 	}
2948 }
2949 
2950 #if defined(_PSM_MODULES)
2951 void
2952 open_mach_list(void)
2953 {
2954 	struct _buf *file;
2955 	char tokbuf[MAXNAMELEN];
2956 	token_t token;
2957 	struct psm_mach *machp;
2958 
2959 	if ((file = kobj_open_file(mach_file)) == (struct _buf *)-1)
2960 		return;
2961 
2962 	while ((token = kobj_lex(file, tokbuf, sizeof (tokbuf))) != EOF) {
2963 		switch (token) {
2964 		case POUND:
2965 			/*
2966 			 * Skip comments.
2967 			 */
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;
2985 		}
2986 	}
2987 	kobj_close_file(file);
2988 }
2989 
2990 void *
2991 get_next_mach(void *handle, char *buf)
2992 {
2993 	struct psm_mach *machp;
2994 
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);
3003 }
3004 
3005 void
3006 close_mach_list(void)
3007 {
3008 	struct psm_mach *machp;
3009 
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);
3015 	}
3016 }
3017 #endif	/* _PSM_MODULES */
3018 
3019 #if defined(_RTC_CONFIG)
3020 /*
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.
3027  */
3028 long
3029 process_rtc_config_file(void)
3030 {
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;
3040 
3041 	if ((file = kobj_open_file(rtc_config_file)) == (struct _buf *)-1)
3042 		return (0);
3043 
3044 	state = R_NEW;
3045 
3046 	while (!done) {
3047 		token = kobj_lex(file, tokbuf, sizeof (tokbuf));
3048 
3049 		switch (token) {
3050 		case POUND:
3051 			/*
3052 			 * Skip comments.
3053 			 */
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;
3097 		}
3098 	}
3099 	kobj_close_file(file);
3100 	return (zone_lag);
3101 }
3102 #endif /* _RTC_CONFIG */
3103 
3104 
3105 /*
3106  * Append node spec to the end of par_list
3107  */
3108 static void
3109 append(struct hwc_spec *spec, struct par_list *par)
3110 {
3111 	struct hwc_spec *hwc, *last;
3112 
3113 	ASSERT(par->par_specs);
3114 	for (hwc = par->par_specs; hwc; hwc = hwc->hwc_next)
3115 		last = hwc;
3116 	last->hwc_next = spec;
3117 }
3118 
3119 /*
3120  * Given a parent=/full-pathname, see if the platform
3121  * can resolve the pathname to driver, otherwise, try
3122  * the leaf node name.
3123  */
3124 static major_t
3125 get_major(char *parent)
3126 {
3127 	major_t major = DDI_MAJOR_T_NONE;
3128 	char *tmp, *driver = NULL;
3129 
3130 	if (*parent == '/')
3131 		major = path_to_major(parent);
3132 
3133 	if (major != DDI_MAJOR_T_NONE)
3134 		return (major);
3135 
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);
3147 }
3148 
3149 /*
3150  * Chain together specs whose parent's module name is the same.
3151  */
3152 static void
3153 add_spec(struct hwc_spec *spec, struct par_list **par)
3154 {
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;
3159 
3160 	ASSERT(parent || class);
3161 
3162 	/*
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.
3166 	 *
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.
3171 	 */
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;
3184 			}
3185 		}
3186 	} else
3187 		maj = DDI_MAJOR_T_NONE;
3188 
3189 	/*
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.
3193 	 */
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;
3200 		}
3201 		par_last = pl;
3202 	}
3203 
3204 	/*
3205 	 * Didn't find a match on the list.  Make a new parent list.
3206 	 */
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;
3213 	}
3214 	/* put "class=" entries last (lower pri if dups) */
3215 	if (maj == DDI_MAJOR_T_NONE) {
3216 		par_last->par_next = pl;
3217 		return;
3218 	}
3219 
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;
3225 }
3226 
3227 /*
3228  * Add property spec to property list in original order
3229  */
3230 static void
3231 add_props(struct hwc_spec *spec, ddi_prop_t **props)
3232 {
3233 	ASSERT(spec->hwc_devi_name == NULL);
3234 
3235 	if (spec->hwc_devi_sys_prop_ptr) {
3236 		while (*props)
3237 			props = &(*props)->prop_next;
3238 		*props = spec->hwc_devi_sys_prop_ptr;
3239 
3240 		/* remove these properties from the spec */
3241 		spec->hwc_devi_sys_prop_ptr = NULL;
3242 	}
3243 	hwc_free(spec);
3244 }
3245