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