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