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 }