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