xref: /titanic_50/usr/src/uts/intel/io/acpica/master_ops.c (revision c533a883a71cff9ff32df1c53c31201e1cbf371f)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/kobj.h>
29 #include <sys/kobj_lex.h>
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/sunndi.h>
33 
34 #define	masterfile "/boot/solaris/devicedb/master"
35 
36 /* Maximum size of a master line */
37 #define	MASTER_LINE_MAX (1024*2)
38 static char *one_master_line;
39 static int one_master_line_cur_index = 0, one_master_line_max = 0;
40 struct master_line {
41 	char *column[10];
42 	char *text; /* to be kmem alloc'd */
43 	int line_size;
44 	struct master_line *next, *prev;
45 };
46 static struct master_line *incore_master_head = NULL,
47 	*incore_master_tail = NULL;
48 /* same order as columns in /boot/solaris/devicedb/master */
49 static int mf_column = 0;
50 static int incore_master_table_line_count = 0;
51 
52 #define	MASTER_OPS_DEBUG_PRINT_INCORE	0x0001
53 #define	MASTER_OPS_DEBUG_PROCESS	0x0002
54 #define	MASTER_OPS_DEBUG_LOOKUP		0x0004
55 #define	MASTER_OPS_DEBUG_CID_FOUND	0x2000
56 static long master_ops_debug = 0x0;
57 #define	EOL 0xA
58 #define	FILE_T struct _buf
59 
60 static void
61 print_incore_master_table() {
62 	struct master_line *ptr = incore_master_head;
63 	int i;
64 
65 	if (master_ops_debug & MASTER_OPS_DEBUG_PRINT_INCORE) {
66 		for (i = 0; i < incore_master_table_line_count; i++) {
67 			printf("1)%s, 2)%s, 3)%s, 4)%s, 5)%s, 6)%s, ",
68 			    ptr->column[0],
69 			    ptr->column[1],
70 			    ptr->column[2],
71 			    ptr->column[3],
72 			    ptr->column[4],
73 			    ptr->column[5]);
74 			if (ptr->column[6] != NULL) {
75 			    printf("7)%s, ", ptr->column[6]);
76 			}
77 			if (ptr->column[7] != NULL) {
78 				printf("8)%s", ptr->column[7]);
79 			}
80 			printf("\n");
81 			ptr = ptr->next;
82 			if (ptr == NULL) {
83 				i++;
84 				break;
85 			}
86 		}
87 		printf("There are %d lines\n", i);
88 	}
89 }
90 
91 /*
92  * parses one_master_line[] from index pointed by one_master_line_cur_index
93  * returns the following tokens
94  * POUND -- if a comment line
95  * NEWLINE -- if an empty line
96  * NAME -- return a string from one_master_line separated by " or blank
97  * EOL -- End of line
98  */
99 static int
100 master_lex(char *val) {
101 	char *cp;
102 	int	ch;
103 	int token;
104 
105 	cp = val;
106 	/* skip leading blanks */
107 	while (((ch = one_master_line[one_master_line_cur_index++]) == ' ' ||
108 	    (ch == '\t')) && (one_master_line_cur_index < one_master_line_max))
109 		;
110 	if ((ch == 0) || (one_master_line_cur_index >= one_master_line_max)) {
111 		val = 0;
112 		return (EOL);
113 	}
114 	*cp++ = (char)ch;
115 	switch (ch) {
116 	case '#':
117 		token = POUND;
118 		break;
119 	case '\n':
120 	case '\r':
121 		token = NEWLINE;
122 		break;
123 	case '"':
124 		cp--;
125 		while (((ch  = one_master_line[one_master_line_cur_index++])
126 		    != '"') && (one_master_line_cur_index <=
127 		    one_master_line_max)) {
128 			*cp++ = (char)ch;
129 		}
130 		token = NAME;
131 		break;
132 	default:
133 		ch = one_master_line[one_master_line_cur_index++];
134 		while ((ch != ' ') && (ch != '\t') && (ch != '\n') &&
135 		    (ch != '\r') && (one_master_line_cur_index <=
136 		    one_master_line_max)) {
137 			*cp++ = (char)ch;
138 			ch = one_master_line[one_master_line_cur_index++];
139 		}
140 		token = NAME;
141 		break;
142 	}
143 	*cp = '\0';
144 	return (token);
145 }
146 
147 /*
148  * read a line from devicedb/master file and put it to one_master_line[] buffer
149  * one_master_line_max -- size of data in one_master_line[]
150  * one_master_line_cur_index -- reset to zero
151  */
152 static int
153 master_get_a_line(FILE_T *file) {
154 	int ch;
155 	one_master_line_max = 0;
156 	one_master_line_cur_index = 0; /* used by master_lex() */
157 	while (((ch = kobj_getc(file)) != '\n') && (ch != '\r')) {
158 		if (ch == -1) {
159 			if (one_master_line_max == 0) {
160 				one_master_line[0] = 0;
161 				return (EOF);
162 			} else {
163 				return (one_master_line_max);
164 			}
165 		}
166 		one_master_line[one_master_line_max++] = ch;
167 		if (one_master_line_max >= MASTER_LINE_MAX) {
168 			cmn_err(CE_WARN, "!master file line too long:");
169 			cmn_err(CE_CONT, "%s", one_master_line);
170 		}
171 	}
172 	one_master_line[one_master_line_max] = 0;
173 	return (one_master_line_max);
174 }
175 
176 /*
177  * skip a line
178  */
179 static void
180 master_skip(FILE_T *file) {
181 	char ch;
182 	while (((ch = kobj_getc(file)) != '\n') && (ch != '\r'))
183 		;
184 }
185 
186 /*
187  * return NULL if no bar found
188  * if bar found, return pointer after the bar
189  * plus, change character '|' (bar) to null as a delimiter
190  */
191 static char *
192 find_bar(char *target) {
193 	if (target == NULL) {
194 		return (NULL);
195 	}
196 	while ((*target != '|') && (*target != ' ') && (*target != 0)) {
197 		target++;
198 	}
199 	if (*target == '|') {
200 		*target = 0;
201 		return (++target);
202 	}
203 	return (NULL);
204 }
205 
206 /*
207  * If column 0 has | (bars) as device separators, we need make (dup)
208  * more lines for each device.
209  */
210 static void
211 dup_devices() {
212 	struct master_line *last, *ptr = incore_master_tail;
213 	char *token;
214 	int i;
215 
216 	if (ptr == NULL || ptr->column == NULL || ptr->column[0] == NULL) {
217 		return;
218 	}
219 	token = incore_master_tail->column[0];
220 	while ((token = find_bar(token)) != NULL) {
221 		last = (struct master_line *)kmem_zalloc(
222 		    sizeof (struct master_line), KM_SLEEP);
223 		for (i = 0; i < 10; i++) {
224 		    last->column[i] = ptr->column[i];
225 		}
226 		last->text = ptr->text;
227 		last->line_size = 0; /* 'cause we re-use the same line */
228 		last->column[0] = token;
229 		ptr->next = last;
230 		last->prev = ptr;
231 		last->next = NULL;
232 		ptr = incore_master_tail = last;
233 		incore_master_table_line_count++;
234 	}
235 }
236 
237 /*
238  * sets master_ops_debug flag from propertyu passed by the boot
239  */
240 static void
241 set_master_ops_debug_flags()
242 {
243 	char *prop;
244 	long flags;
245 
246 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
247 	    DDI_PROP_DONTPASS, "master_ops_debug", &prop) == DDI_PROP_SUCCESS) {
248 		long data;
249 		if (ddi_strtol(prop, NULL, 0, &data) == 0) {
250 			master_ops_debug = (unsigned long)data;
251 			e_ddi_prop_remove(DDI_DEV_T_NONE, ddi_root_node(),
252 			    "master_ops_debug");
253 			e_ddi_prop_update_int(DDI_DEV_T_NONE, ddi_root_node(),
254 			    "master_ops_debug", data);
255 		}
256 		ddi_prop_free(prop);
257 	}
258 }
259 
260 /*
261  * open / read / parse / close devicedb/master file
262  */
263 void
264 process_master_file() {
265 	FILE_T *file;
266 	char tokbuf[MASTER_LINE_MAX];
267 	int token;
268 	int done = 0;
269 	int line_begin;
270 	int x = 0;
271 	int total_line_processed = 0;
272 
273 	set_master_ops_debug_flags();
274 
275 	incore_master_head = incore_master_tail = NULL;
276 	if ((file = kobj_open_file(masterfile)) == (struct _buf *)-1) {
277 		cmn_err(CE_WARN, "!cannot open master file: %s", masterfile);
278 		return;
279 	}
280 	one_master_line = (char *)kmem_zalloc(MASTER_LINE_MAX, KM_SLEEP);
281 	master_skip(file); /* skip the first line which is "version" */
282 	mf_column = 0;
283 	while (!done) {
284 		if (mf_column == 0) {
285 			x = master_get_a_line(file);
286 			total_line_processed++;
287 			if (x == EOF) { /* end of file */
288 				done = 1;
289 				continue;
290 			}
291 			if (x == 0) { /* blank line */
292 				continue;
293 			}
294 		}
295 		token = master_lex(tokbuf);
296 		switch (token) {
297 		case POUND: /* ignore comment line */
298 			if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) {
299 				printf("master_ops: # found skip this line\n");
300 			}
301 			mf_column = 0;
302 			break;
303 		case NAME: /* found actual string, parse and keep it */
304 			if (mf_column == 0) {
305 				if (incore_master_tail == NULL) {
306 					/* very 1st line */
307 					incore_master_head =
308 					incore_master_tail = (struct
309 					    master_line *) kmem_zalloc(
310 					    sizeof (struct master_line),
311 					    KM_SLEEP);
312 					incore_master_head->text = (char *)
313 					    kmem_zalloc(one_master_line_max,
314 					    KM_SLEEP);
315 					incore_master_head->line_size =
316 					    one_master_line_max;
317 				} else {
318 					incore_master_tail->next = (struct
319 					    master_line *)kmem_zalloc(
320 					    sizeof (struct master_line),
321 					    KM_SLEEP);
322 					incore_master_tail =
323 					    incore_master_tail->next;
324 					incore_master_tail->text = (char *)
325 					    kmem_zalloc(one_master_line_max,
326 					    KM_SLEEP);
327 					incore_master_tail->line_size =
328 					    one_master_line_max;
329 				}
330 				line_begin = 0;
331 				incore_master_table_line_count++;
332 			}
333 			if ((line_begin + strlen(tokbuf) + 1) >
334 			    MASTER_LINE_MAX) {
335 				mf_column = 0;
336 				cmn_err(CE_WARN, "!master file line too long");
337 				cmn_err(CE_CONT, "line data: \"%s\"",
338 				    one_master_line);
339 				master_skip(file); /* skip this line */
340 				break;
341 			}
342 			(void) strcpy(incore_master_tail->text + line_begin,
343 			    tokbuf);
344 			incore_master_tail->column[mf_column] = line_begin +
345 			    incore_master_tail->text;
346 			if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) {
347 				printf("master_ops: line=%d column[%x] found:"\
348 				    " \"%s\"\n",
349 				    incore_master_table_line_count, mf_column,
350 				    incore_master_tail->column[mf_column]);
351 			}
352 			line_begin += strlen(tokbuf) + 1;
353 			mf_column++;
354 			break;
355 		case EOF: /* end of file */
356 			if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) {
357 				printf("master_ops: EOF found. We're done.\n");
358 			}
359 			done = 1;
360 			break;
361 		case EOL: /* end of line */
362 			if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) {
363 				printf("master_ops: EOL found.\n");
364 			}
365 			mf_column = 0;
366 			one_master_line_max = 0;
367 			dup_devices();
368 			break;
369 		default: /* something went wrong */
370 			cmn_err(CE_WARN, "!master_ops: something went wrong "\
371 			    "parsing master file: %s", tokbuf);
372 		}
373 	}
374 	kobj_close_file(file);
375 
376 	if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) {
377 		printf("master_ops: incore line count: %d\n",
378 		    incore_master_table_line_count);
379 		printf("master_ops: total line processed: %d\n",
380 		    total_line_processed);
381 	}
382 	print_incore_master_table();
383 }
384 
385 /*
386  * Loop and free all per line master data, including line text
387  */
388 void
389 free_master_data() {
390 	int i;
391 	struct master_line *next, *cur = incore_master_head;
392 	for (i = 0; i < incore_master_table_line_count; i++) {
393 		next = cur->next;
394 		if ((cur->text != NULL) && (cur->line_size != 0)) {
395 			kmem_free(cur->text, cur->line_size);
396 		}
397 		kmem_free(cur, sizeof (struct master_line));
398 		if (next == NULL) {
399 			break; /* we're done */
400 		}
401 			cur = next;
402 	}
403 	incore_master_head = incore_master_tail = NULL;
404 	if (one_master_line) {
405 		kmem_free(one_master_line, MASTER_LINE_MAX);
406 	}
407 }
408 
409 /*
410  *  To match pnpid with master table entries
411  *  returns 0 if no matching device found in master file
412  *          1 if a matching device is in master file
413  *            devname -- device node name
414  *            description -- device description string
415  *            properties -- device attributes (e.g. compatibility="kb8042")
416  *                          (could be NULL)
417  */
418 int
419 master_file_lookup(char *pnpid, char **devname, char **description,
420     char **properties)
421 {
422 	struct master_line *cur = incore_master_head;
423 
424 	ASSERT(pnpid != NULL);
425 
426 	if (master_ops_debug & MASTER_OPS_DEBUG_LOOKUP)
427 		printf("master_ops: Looking for %s: ", pnpid);
428 
429 	while (cur != NULL) {
430 		if (strcmp(pnpid, cur->column[0]) == 0) {
431 
432 			*devname = kmem_zalloc(strlen(cur->column[1]) + 1,
433 			    KM_SLEEP);
434 			(void) strcpy(*devname, cur->column[1]);
435 			*description = kmem_zalloc(strlen(cur->column[5]) + 1,
436 			    KM_SLEEP);
437 			(void) strcpy(*description, cur->column[5]);
438 			if (cur->column[6] != NULL) {
439 				*properties = kmem_zalloc(
440 				    strlen(cur->column[6]) + 1, KM_SLEEP);
441 				(void) strcpy(*properties, cur->column[6]);
442 			} else
443 				*properties = NULL;
444 
445 			if (master_ops_debug & MASTER_OPS_DEBUG_LOOKUP) {
446 				printf("FOUND. dev node name: \"%s\"\n",
447 				    *devname);
448 				printf("description: \"%s\"", *description);
449 				if (*properties != NULL) {
450 					printf(" properties: \"%s\"\n",
451 					    *properties);
452 				} else {
453 					printf("\n");
454 				}
455 			}
456 			return (1);
457 		}
458 		cur = cur->next;
459 	}
460 	/* XXX: for the devices not found, they should go to used resources?? */
461 	if (master_ops_debug & MASTER_OPS_DEBUG_LOOKUP) {
462 		printf("NOT FOUND!!!\n");
463 	}
464 	return (0);
465 }
466 
467 /*
468  * master_file_lookups() -- processes multiple pnp IDs (CIDs)
469  * 	return 1 if a PNP id is matched
470  * 	else return 0
471  */
472 int
473 master_file_lookups(char *pnpid, char **devname, char **description,
474     char **properties, int pnpid_size)
475 {
476 	char *tmp = pnpid;
477 
478 	/*
479 	 * terminate the loop based on pnpid_size.  If pnpid_size
480 	 * is 0, the loop terminates without ever calling
481 	 * master_file_lookup(); this happens if there's no
482 	 * _CID object present
483 	 */
484 	while (tmp < pnpid + pnpid_size) {
485 		int ret = master_file_lookup(tmp, devname, description,
486 		    properties);
487 		if (ret == 1) {
488 			if (master_ops_debug & MASTER_OPS_DEBUG_CID_FOUND) {
489 				cmn_err(CE_NOTE, "CID found: %s", tmp);
490 			}
491 			return (ret); /* a CID is found */
492 		}
493 		tmp += strlen(tmp) + 1; /* move on to the next one */
494 	}
495 	return (0);
496 }
497