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 * 69 mf_alloc_device_id() 70 { 71 return ((device_id_t *)kmem_zalloc(sizeof (device_id_t), KM_SLEEP)); 72 } 73 74 void 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 * 84 mf_alloc_property() 85 { 86 return ((property_t *)kmem_zalloc(sizeof (property_t), KM_SLEEP)); 87 } 88 89 static void 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 * 102 mf_alloc_master_rec() 103 { 104 return ((master_rec_t *)kmem_zalloc(sizeof (master_rec_t), KM_SLEEP)); 105 } 106 107 static void 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 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 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 * 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 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 * 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