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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include <sys/kobj.h>
26 #include <sys/kobj_lex.h>
27 #include <sys/ddi.h>
28 #include <sys/sunddi.h>
29 #include <sys/sunndi.h>
30 #include <sys/acpi/acpi.h>
31 #include <sys/acpica.h>
32
33 #define masterfile "/boot/solaris/devicedb/master"
34
35 /*
36 * Internal definitions
37 */
38
39 typedef enum {
40 MF_UNEXPECTED = -1,
41 MF_IDENT,
42 MF_STRING,
43 MF_EOF,
44 MF_NEWLINE,
45 MF_EQUALS,
46 MF_BIT_OR
47 } mftoken_t;
48
49 typedef enum {
50 MF_INIT,
51 MF_DEVID,
52 MF_NAME,
53 MF_DEVTYPE,
54 MF_BUSTYPE,
55 MF_BEFNAME,
56 MF_DESCRIPTION,
57 MF_PROPNAME,
58 MF_PROPASSIGN,
59 MF_PROPVAL,
60 MF_VERSION_DONE,
61 MF_VALID_DONE,
62 MF_ERROR_DONE
63 } mfparse_t;
64
65
66 static master_rec_t *master_list = NULL;
67
68 device_id_t *
mf_alloc_device_id()69 mf_alloc_device_id()
70 {
71 return ((device_id_t *)kmem_zalloc(sizeof (device_id_t), KM_SLEEP));
72 }
73
74 void
mf_free_device_id(device_id_t * d)75 mf_free_device_id(device_id_t *d)
76 {
77 if (d->id != NULL)
78 strfree(d->id);
79
80 kmem_free(d, sizeof (device_id_t));
81 }
82
83 static property_t *
mf_alloc_property()84 mf_alloc_property()
85 {
86 return ((property_t *)kmem_zalloc(sizeof (property_t), KM_SLEEP));
87 }
88
89 static void
mf_free_property(property_t * p)90 mf_free_property(property_t *p)
91 {
92 if (p->name != NULL)
93 strfree(p->name);
94
95 if (p->value != NULL)
96 strfree(p->value);
97
98 kmem_free(p, sizeof (property_t));
99 }
100
101 static master_rec_t *
mf_alloc_master_rec()102 mf_alloc_master_rec()
103 {
104 return ((master_rec_t *)kmem_zalloc(sizeof (master_rec_t), KM_SLEEP));
105 }
106
107 static void
mf_free_master_rec(master_rec_t * m)108 mf_free_master_rec(master_rec_t *m)
109 {
110 device_id_t *d;
111 property_t *p;
112
113 if (m->name != NULL)
114 strfree(m->name);
115
116 if (m->description != NULL)
117 strfree(m->description);
118
119 d = m->device_ids;
120 while (d != NULL) {
121 device_id_t *next;
122
123 next = d->next;
124 mf_free_device_id(d);
125 d = next;
126 }
127
128 p = m->properties;
129 while (p != NULL) {
130 property_t *next;
131
132 next = p->next;
133 mf_free_property(p);
134 p = next;
135 }
136
137 kmem_free(m, sizeof (master_rec_t));
138 }
139
140 void
free_master_data()141 free_master_data()
142 {
143 master_rec_t *m;
144
145 m = master_list;
146 while (m != NULL) {
147 master_rec_t *next;
148
149 next = m->next;
150 mf_free_master_rec(m);
151 m = next;
152 }
153 master_list = NULL;
154 }
155
156 /*
157 * Unfortunately, kobj_lex() is too sophisticated for our needs
158 */
159 static mftoken_t
mf_lex(struct _buf * file,char * val,size_t size)160 mf_lex(struct _buf *file, char *val, size_t size)
161 {
162 char *cp;
163 int ch, badquote;
164 size_t remain;
165 mftoken_t token = MF_UNEXPECTED;
166
167 if (size < 2)
168 return (token); /* MF_UNEXPECTED */
169
170 cp = val;
171
172 /* skip leading whitespace */
173 while ((ch = kobj_getc(file)) == ' ' || ch == '\t')
174 ;
175
176 /* strip comments */
177 if (ch == '#') {
178 while ((ch = kobj_getc(file)) != '\n' && ch != '\r' &&
179 ch != -1)
180 ;
181 }
182
183 remain = size - 1;
184 *cp++ = (char)ch;
185 switch (ch) {
186 case -1:
187 token = MF_EOF;
188 break;
189 case '\n':
190 case '\r':
191 token = MF_NEWLINE;
192 break;
193 case '=':
194 token = MF_EQUALS;
195 break;
196 case '|':
197 token = MF_BIT_OR;
198 break;
199 case '"':
200 remain++;
201 cp--;
202 badquote = 0;
203 while (!badquote && (ch = kobj_getc(file)) != '"') {
204 switch (ch) {
205 case '\n':
206 case -1:
207 remain = size - 1;
208 cp = val;
209 *cp++ = '\n';
210 badquote = 1;
211 /* since we consumed the newline/EOF */
212 (void) kobj_ungetc(file);
213 break;
214 default:
215 if (--remain == 0) {
216 token = MF_UNEXPECTED;
217 goto out;
218 }
219 *cp++ = (char)ch;
220 break;
221 }
222 }
223 token = MF_STRING;
224 break;
225 default:
226 do {
227 if (--remain == 0) {
228 token = MF_UNEXPECTED;
229 break;
230 }
231
232 token = MF_IDENT;
233 *cp++ = (char)(ch = kobj_getc(file));
234
235 /* if terminating character, break out */
236 if ((ch == -1) || (ch == ' ') || (ch == '\t') ||
237 (ch == '\n') || (ch == '\r') || (ch == '=') ||
238 (ch == '|')) {
239 (void) kobj_ungetc(file);
240 remain++;
241 cp--;
242 break;
243 }
244
245 if ((ch == '#') || (ch == '"'))
246 token = MF_UNEXPECTED;
247 } while (token != MF_UNEXPECTED);
248 break;
249 }
250 out:
251 *cp = '\0';
252
253 return (token);
254 }
255
256 static master_rec_t *
get_line(struct _buf * file)257 get_line(struct _buf *file)
258 {
259 master_rec_t *m = NULL;
260 device_id_t *d = NULL;
261 property_t *p = NULL;
262 mftoken_t token;
263 char tokval[MAXPATHLEN];
264 mfparse_t parse_state;
265
266 parse_state = MF_INIT;
267 token = mf_lex(file, tokval, sizeof (tokval));
268 while (token != MF_EOF) {
269 switch (parse_state) {
270 case MF_INIT:
271 m = mf_alloc_master_rec();
272 parse_state = MF_DEVID;
273 /*FALLTHROUGH*/
274 case MF_DEVID:
275 if (token == MF_IDENT) {
276 d = mf_alloc_device_id();
277 d->id = strdup(tokval);
278 d->next = m->device_ids;
279 m->device_ids = d;
280 parse_state = MF_NAME;
281 } else if (token != MF_NEWLINE)
282 parse_state = MF_ERROR_DONE;
283 break;
284 case MF_NAME:
285 if (token == MF_IDENT) {
286 m->name = strdup(tokval);
287 parse_state = MF_DEVTYPE;
288 } else if (token == MF_BIT_OR) {
289 parse_state = MF_DEVID;
290 } else
291 parse_state = MF_ERROR_DONE;
292 break;
293 case MF_DEVTYPE:
294 if (token == MF_IDENT) {
295 /* device_type not used */
296 parse_state = MF_BUSTYPE;
297 } else if (token == MF_NEWLINE) {
298 /* version line ignored */
299 parse_state = MF_VERSION_DONE;
300 } else
301 parse_state = MF_ERROR_DONE;
302 break;
303 case MF_BUSTYPE:
304 if (token == MF_IDENT) {
305 /* bus_type ignored */
306 parse_state = MF_BEFNAME;
307 } else
308 parse_state = MF_ERROR_DONE;
309 break;
310 case MF_BEFNAME:
311 if (token == MF_IDENT) {
312 /* realmode driver name ignored */
313 parse_state = MF_DESCRIPTION;
314 } else
315 parse_state = MF_ERROR_DONE;
316 break;
317 case MF_DESCRIPTION:
318 if (token == MF_STRING) {
319 m->description = strdup(tokval);
320 parse_state = MF_PROPNAME;
321 } else
322 parse_state = MF_ERROR_DONE;
323 break;
324 case MF_PROPNAME:
325 if (token == MF_IDENT) {
326 p = mf_alloc_property();
327 p->name = strdup(tokval);
328 parse_state = MF_PROPASSIGN;
329 } else if (token == MF_NEWLINE) {
330 parse_state = MF_VALID_DONE;
331 } else
332 parse_state = MF_ERROR_DONE;
333 break;
334 case MF_PROPASSIGN:
335 if (token == MF_EQUALS) {
336 parse_state = MF_PROPVAL;
337 } else
338 parse_state = MF_ERROR_DONE;
339 break;
340 case MF_PROPVAL:
341 if (token == MF_STRING || token == MF_IDENT) {
342 p->value = strdup(tokval);
343 p->next = m->properties;
344 /* delete properties which begin with '$' */
345 if (*p->name == '$') {
346 mf_free_property(p);
347 } else
348 m->properties = p;
349 p = NULL;
350 parse_state = MF_PROPNAME;
351 } else
352 parse_state = MF_ERROR_DONE;
353 break;
354 case MF_VERSION_DONE:
355 case MF_VALID_DONE:
356 case MF_ERROR_DONE:
357 /* terminating states handled outside switch() */
358 break;
359 }
360
361 if (parse_state == MF_VERSION_DONE) {
362 /* ignore version line */
363 mf_free_master_rec(m);
364 parse_state = MF_INIT;
365 } else if (parse_state == MF_VALID_DONE) {
366 /* valid line */
367 break;
368 } else if (parse_state == MF_ERROR_DONE) {
369 mf_free_master_rec(m);
370 if (p != NULL)
371 mf_free_property(p);
372 /*
373 * Error in master file. Should never happen
374 * since master file is not user-edited. Eat rest
375 * of line to attempt error recovery
376 */
377 cmn_err(CE_NOTE, "!error in %s", masterfile);
378 while (token != MF_NEWLINE && token != MF_EOF)
379 token = mf_lex(file, tokval, sizeof (tokval));
380 parse_state = MF_INIT;
381 continue;
382 }
383
384 token = mf_lex(file, tokval, sizeof (tokval));
385 }
386
387 return (m);
388 }
389
390 void
process_master_file()391 process_master_file()
392 {
393 struct _buf *file;
394 master_rec_t *m;
395
396 if ((file = kobj_open_file(masterfile)) == (struct _buf *)-1) {
397 cmn_err(CE_WARN, "!cannot open master file: %s", masterfile);
398 return;
399 }
400
401 while ((m = get_line(file)) != NULL) {
402 m->next = master_list;
403 master_list = m;
404 }
405
406 kobj_close_file(file);
407 }
408
409 /*
410 * Return the first master file record found matching pnpid list
411 */
412 const master_rec_t *
master_file_lookup(device_id_t * pnpid)413 master_file_lookup(device_id_t *pnpid)
414 {
415 master_rec_t *m;
416 device_id_t *d;
417
418 while (pnpid != NULL) {
419 m = master_list;
420 while (m != NULL) {
421 d = m->device_ids;
422 while (d != NULL) {
423 if (strcmp(pnpid->id, d->id) == 0)
424 return (m);
425 d = d->next;
426 }
427 m = m->next;
428 }
429 pnpid = pnpid->next;
430 }
431
432 return (NULL);
433 }
434