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