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 }