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