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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/kobj.h> 29 #include <sys/kobj_lex.h> 30 #include <sys/ddi.h> 31 #include <sys/sunddi.h> 32 #include <sys/sunndi.h> 33 34 #define masterfile "/boot/solaris/devicedb/master" 35 36 /* Maximum size of a master line */ 37 #define MASTER_LINE_MAX (1024*2) 38 static char *one_master_line; 39 static int one_master_line_cur_index = 0, one_master_line_max = 0; 40 struct master_line { 41 char *column[10]; 42 char *text; /* to be kmem alloc'd */ 43 int line_size; 44 struct master_line *next, *prev; 45 }; 46 static struct master_line *incore_master_head = NULL, 47 *incore_master_tail = NULL; 48 /* same order as columns in /boot/solaris/devicedb/master */ 49 static int mf_column = 0; 50 static int incore_master_table_line_count = 0; 51 52 #define MASTER_OPS_DEBUG_PRINT_INCORE 0x0001 53 #define MASTER_OPS_DEBUG_PROCESS 0x0002 54 #define MASTER_OPS_DEBUG_LOOKUP 0x0004 55 #define MASTER_OPS_DEBUG_CID_FOUND 0x2000 56 static long master_ops_debug = 0x0; 57 #define EOL 0xA 58 #define FILE_T struct _buf 59 60 static void 61 print_incore_master_table() { 62 struct master_line *ptr = incore_master_head; 63 int i; 64 65 if (master_ops_debug & MASTER_OPS_DEBUG_PRINT_INCORE) { 66 for (i = 0; i < incore_master_table_line_count; i++) { 67 printf("1)%s, 2)%s, 3)%s, 4)%s, 5)%s, 6)%s, ", 68 ptr->column[0], 69 ptr->column[1], 70 ptr->column[2], 71 ptr->column[3], 72 ptr->column[4], 73 ptr->column[5]); 74 if (ptr->column[6] != NULL) { 75 printf("7)%s, ", ptr->column[6]); 76 } 77 if (ptr->column[7] != NULL) { 78 printf("8)%s", ptr->column[7]); 79 } 80 printf("\n"); 81 ptr = ptr->next; 82 if (ptr == NULL) { 83 i++; 84 break; 85 } 86 } 87 printf("There are %d lines\n", i); 88 } 89 } 90 91 /* 92 * parses one_master_line[] from index pointed by one_master_line_cur_index 93 * returns the following tokens 94 * POUND -- if a comment line 95 * NEWLINE -- if an empty line 96 * NAME -- return a string from one_master_line separated by " or blank 97 * EOL -- End of line 98 */ 99 static int 100 master_lex(char *val) { 101 char *cp; 102 int ch; 103 int token; 104 105 cp = val; 106 /* skip leading blanks */ 107 while (((ch = one_master_line[one_master_line_cur_index++]) == ' ' || 108 (ch == '\t')) && (one_master_line_cur_index < one_master_line_max)) 109 ; 110 if ((ch == 0) || (one_master_line_cur_index >= one_master_line_max)) { 111 val = 0; 112 return (EOL); 113 } 114 *cp++ = (char)ch; 115 switch (ch) { 116 case '#': 117 token = POUND; 118 break; 119 case '\n': 120 case '\r': 121 token = NEWLINE; 122 break; 123 case '"': 124 cp--; 125 while (((ch = one_master_line[one_master_line_cur_index++]) 126 != '"') && (one_master_line_cur_index <= 127 one_master_line_max)) { 128 *cp++ = (char)ch; 129 } 130 token = NAME; 131 break; 132 default: 133 ch = one_master_line[one_master_line_cur_index++]; 134 while ((ch != ' ') && (ch != '\t') && (ch != '\n') && 135 (ch != '\r') && (one_master_line_cur_index <= 136 one_master_line_max)) { 137 *cp++ = (char)ch; 138 ch = one_master_line[one_master_line_cur_index++]; 139 } 140 token = NAME; 141 break; 142 } 143 *cp = '\0'; 144 return (token); 145 } 146 147 /* 148 * read a line from devicedb/master file and put it to one_master_line[] buffer 149 * one_master_line_max -- size of data in one_master_line[] 150 * one_master_line_cur_index -- reset to zero 151 */ 152 static int 153 master_get_a_line(FILE_T *file) { 154 int ch; 155 one_master_line_max = 0; 156 one_master_line_cur_index = 0; /* used by master_lex() */ 157 while (((ch = kobj_getc(file)) != '\n') && (ch != '\r')) { 158 if (ch == -1) { 159 if (one_master_line_max == 0) { 160 one_master_line[0] = 0; 161 return (EOF); 162 } else { 163 return (one_master_line_max); 164 } 165 } 166 one_master_line[one_master_line_max++] = ch; 167 if (one_master_line_max >= MASTER_LINE_MAX) { 168 cmn_err(CE_WARN, "!master file line too long:"); 169 cmn_err(CE_CONT, "%s", one_master_line); 170 } 171 } 172 one_master_line[one_master_line_max] = 0; 173 return (one_master_line_max); 174 } 175 176 /* 177 * skip a line 178 */ 179 static void 180 master_skip(FILE_T *file) { 181 char ch; 182 while (((ch = kobj_getc(file)) != '\n') && (ch != '\r')) 183 ; 184 } 185 186 /* 187 * return NULL if no bar found 188 * if bar found, return pointer after the bar 189 * plus, change character '|' (bar) to null as a delimiter 190 */ 191 static char * 192 find_bar(char *target) { 193 if (target == NULL) { 194 return (NULL); 195 } 196 while ((*target != '|') && (*target != ' ') && (*target != 0)) { 197 target++; 198 } 199 if (*target == '|') { 200 *target = 0; 201 return (++target); 202 } 203 return (NULL); 204 } 205 206 /* 207 * If column 0 has | (bars) as device separators, we need make (dup) 208 * more lines for each device. 209 */ 210 static void 211 dup_devices() { 212 struct master_line *last, *ptr = incore_master_tail; 213 char *token; 214 int i; 215 216 if (ptr == NULL || ptr->column == NULL || ptr->column[0] == NULL) { 217 return; 218 } 219 token = incore_master_tail->column[0]; 220 while ((token = find_bar(token)) != NULL) { 221 last = (struct master_line *)kmem_zalloc( 222 sizeof (struct master_line), KM_SLEEP); 223 for (i = 0; i < 10; i++) { 224 last->column[i] = ptr->column[i]; 225 } 226 last->text = ptr->text; 227 last->line_size = 0; /* 'cause we re-use the same line */ 228 last->column[0] = token; 229 ptr->next = last; 230 last->prev = ptr; 231 last->next = NULL; 232 ptr = incore_master_tail = last; 233 incore_master_table_line_count++; 234 } 235 } 236 237 /* 238 * sets master_ops_debug flag from propertyu passed by the boot 239 */ 240 static void 241 set_master_ops_debug_flags() 242 { 243 char *prop; 244 long flags; 245 246 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 247 DDI_PROP_DONTPASS, "master_ops_debug", &prop) == DDI_PROP_SUCCESS) { 248 long data; 249 if (ddi_strtol(prop, NULL, 0, &data) == 0) { 250 master_ops_debug = (unsigned long)data; 251 e_ddi_prop_remove(DDI_DEV_T_NONE, ddi_root_node(), 252 "master_ops_debug"); 253 e_ddi_prop_update_int(DDI_DEV_T_NONE, ddi_root_node(), 254 "master_ops_debug", data); 255 } 256 ddi_prop_free(prop); 257 } 258 } 259 260 /* 261 * open / read / parse / close devicedb/master file 262 */ 263 void 264 process_master_file() { 265 FILE_T *file; 266 char tokbuf[MASTER_LINE_MAX]; 267 int token; 268 int done = 0; 269 int line_begin; 270 int x = 0; 271 int total_line_processed = 0; 272 273 set_master_ops_debug_flags(); 274 275 incore_master_head = incore_master_tail = NULL; 276 if ((file = kobj_open_file(masterfile)) == (struct _buf *)-1) { 277 cmn_err(CE_WARN, "!cannot open master file: %s", masterfile); 278 return; 279 } 280 one_master_line = (char *)kmem_zalloc(MASTER_LINE_MAX, KM_SLEEP); 281 master_skip(file); /* skip the first line which is "version" */ 282 mf_column = 0; 283 while (!done) { 284 if (mf_column == 0) { 285 x = master_get_a_line(file); 286 total_line_processed++; 287 if (x == EOF) { /* end of file */ 288 done = 1; 289 continue; 290 } 291 if (x == 0) { /* blank line */ 292 continue; 293 } 294 } 295 token = master_lex(tokbuf); 296 switch (token) { 297 case POUND: /* ignore comment line */ 298 if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) { 299 printf("master_ops: # found skip this line\n"); 300 } 301 mf_column = 0; 302 break; 303 case NAME: /* found actual string, parse and keep it */ 304 if (mf_column == 0) { 305 if (incore_master_tail == NULL) { 306 /* very 1st line */ 307 incore_master_head = 308 incore_master_tail = (struct 309 master_line *) kmem_zalloc( 310 sizeof (struct master_line), 311 KM_SLEEP); 312 incore_master_head->text = (char *) 313 kmem_zalloc(one_master_line_max, 314 KM_SLEEP); 315 incore_master_head->line_size = 316 one_master_line_max; 317 } else { 318 incore_master_tail->next = (struct 319 master_line *)kmem_zalloc( 320 sizeof (struct master_line), 321 KM_SLEEP); 322 incore_master_tail = 323 incore_master_tail->next; 324 incore_master_tail->text = (char *) 325 kmem_zalloc(one_master_line_max, 326 KM_SLEEP); 327 incore_master_tail->line_size = 328 one_master_line_max; 329 } 330 line_begin = 0; 331 incore_master_table_line_count++; 332 } 333 if ((line_begin + strlen(tokbuf) + 1) > 334 MASTER_LINE_MAX) { 335 mf_column = 0; 336 cmn_err(CE_WARN, "!master file line too long"); 337 cmn_err(CE_CONT, "line data: \"%s\"", 338 one_master_line); 339 master_skip(file); /* skip this line */ 340 break; 341 } 342 (void) strcpy(incore_master_tail->text + line_begin, 343 tokbuf); 344 incore_master_tail->column[mf_column] = line_begin + 345 incore_master_tail->text; 346 if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) { 347 printf("master_ops: line=%d column[%x] found:"\ 348 " \"%s\"\n", 349 incore_master_table_line_count, mf_column, 350 incore_master_tail->column[mf_column]); 351 } 352 line_begin += strlen(tokbuf) + 1; 353 mf_column++; 354 break; 355 case EOF: /* end of file */ 356 if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) { 357 printf("master_ops: EOF found. We're done.\n"); 358 } 359 done = 1; 360 break; 361 case EOL: /* end of line */ 362 if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) { 363 printf("master_ops: EOL found.\n"); 364 } 365 mf_column = 0; 366 one_master_line_max = 0; 367 dup_devices(); 368 break; 369 default: /* something went wrong */ 370 cmn_err(CE_WARN, "!master_ops: something went wrong "\ 371 "parsing master file: %s", tokbuf); 372 } 373 } 374 kobj_close_file(file); 375 376 if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) { 377 printf("master_ops: incore line count: %d\n", 378 incore_master_table_line_count); 379 printf("master_ops: total line processed: %d\n", 380 total_line_processed); 381 } 382 print_incore_master_table(); 383 } 384 385 /* 386 * Loop and free all per line master data, including line text 387 */ 388 void 389 free_master_data() { 390 int i; 391 struct master_line *next, *cur = incore_master_head; 392 for (i = 0; i < incore_master_table_line_count; i++) { 393 next = cur->next; 394 if ((cur->text != NULL) && (cur->line_size != 0)) { 395 kmem_free(cur->text, cur->line_size); 396 } 397 kmem_free(cur, sizeof (struct master_line)); 398 if (next == NULL) { 399 break; /* we're done */ 400 } 401 cur = next; 402 } 403 incore_master_head = incore_master_tail = NULL; 404 if (one_master_line) { 405 kmem_free(one_master_line, MASTER_LINE_MAX); 406 } 407 } 408 409 /* 410 * To match pnpid with master table entries 411 * returns 0 if no matching device found in master file 412 * 1 if a matching device is in master file 413 * devname -- device node name 414 * description -- device description string 415 * properties -- device attributes (e.g. compatibility="kb8042") 416 * (could be NULL) 417 */ 418 int 419 master_file_lookup(char *pnpid, char **devname, char **description, 420 char **properties) 421 { 422 struct master_line *cur = incore_master_head; 423 424 ASSERT(pnpid != NULL); 425 426 if (master_ops_debug & MASTER_OPS_DEBUG_LOOKUP) 427 printf("master_ops: Looking for %s: ", pnpid); 428 429 while (cur != NULL) { 430 if (strcmp(pnpid, cur->column[0]) == 0) { 431 432 *devname = kmem_zalloc(strlen(cur->column[1]) + 1, 433 KM_SLEEP); 434 (void) strcpy(*devname, cur->column[1]); 435 *description = kmem_zalloc(strlen(cur->column[5]) + 1, 436 KM_SLEEP); 437 (void) strcpy(*description, cur->column[5]); 438 if (cur->column[6] != NULL) { 439 *properties = kmem_zalloc( 440 strlen(cur->column[6]) + 1, KM_SLEEP); 441 (void) strcpy(*properties, cur->column[6]); 442 } else 443 *properties = NULL; 444 445 if (master_ops_debug & MASTER_OPS_DEBUG_LOOKUP) { 446 printf("FOUND. dev node name: \"%s\"\n", 447 *devname); 448 printf("description: \"%s\"", *description); 449 if (*properties != NULL) { 450 printf(" properties: \"%s\"\n", 451 *properties); 452 } else { 453 printf("\n"); 454 } 455 } 456 return (1); 457 } 458 cur = cur->next; 459 } 460 /* XXX: for the devices not found, they should go to used resources?? */ 461 if (master_ops_debug & MASTER_OPS_DEBUG_LOOKUP) { 462 printf("NOT FOUND!!!\n"); 463 } 464 return (0); 465 } 466 467 /* 468 * master_file_lookups() -- processes multiple pnp IDs (CIDs) 469 * return 1 if a PNP id is matched 470 * else return 0 471 */ 472 int 473 master_file_lookups(char *pnpid, char **devname, char **description, 474 char **properties, int pnpid_size) 475 { 476 char *tmp = pnpid; 477 478 /* 479 * terminate the loop based on pnpid_size. If pnpid_size 480 * is 0, the loop terminates without ever calling 481 * master_file_lookup(); this happens if there's no 482 * _CID object present 483 */ 484 while (tmp < pnpid + pnpid_size) { 485 int ret = master_file_lookup(tmp, devname, description, 486 properties); 487 if (ret == 1) { 488 if (master_ops_debug & MASTER_OPS_DEBUG_CID_FOUND) { 489 cmn_err(CE_NOTE, "CID found: %s", tmp); 490 } 491 return (ret); /* a CID is found */ 492 } 493 tmp += strlen(tmp) + 1; /* move on to the next one */ 494 } 495 return (0); 496 } 497