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 }