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, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 #include <sys/types.h>
  29 #include <sys/stropts.h>
  30 #include <sys/debug.h>
  31 #include <sys/isa_defs.h>
  32 #include <sys/dditypes.h>
  33 #include <sys/ddi_impldefs.h>
  34 #include "devid_impl.h"
  35 
  36 static int devid_str_decode_id(char *devidstr, ddi_devid_t *devidp,
  37     char **minor_namep, impl_devid_t *id);
  38 
  39 
  40 /*
  41  * Validate device id.
  42  */
  43 int
  44 #ifdef  _KERNEL
  45 ddi_devid_valid(ddi_devid_t devid)
  46 #else   /* !_KERNEL */
  47 devid_valid(ddi_devid_t devid)
  48 #endif  /* _KERNEL */
  49 {
  50         impl_devid_t    *id = (impl_devid_t *)devid;
  51         ushort_t        type;
  52 
  53         DEVID_ASSERT(devid != NULL);
  54 
  55         if (id->did_magic_hi != DEVID_MAGIC_MSB)
  56                 return (DEVID_RET_INVALID);
  57 
  58         if (id->did_magic_lo != DEVID_MAGIC_LSB)
  59                 return (DEVID_RET_INVALID);
  60 
  61         if (id->did_rev_hi != DEVID_REV_MSB)
  62                 return (DEVID_RET_INVALID);
  63 
  64         if (id->did_rev_lo != DEVID_REV_LSB)
  65                 return (DEVID_RET_INVALID);
  66 
  67         type = DEVID_GETTYPE(id);
  68         if ((type == DEVID_NONE) || (type > DEVID_MAXTYPE))
  69                 return (DEVID_RET_INVALID);
  70 
  71         return (DEVID_RET_VALID);
  72 }
  73 
  74 /*
  75  * Return the sizeof a device id. If called with NULL devid it returns
  76  * the amount of space needed to determine the size.
  77  */
  78 size_t
  79 #ifdef  _KERNEL
  80 ddi_devid_sizeof(ddi_devid_t devid)
  81 #else   /* !_KERNEL */
  82 devid_sizeof(ddi_devid_t devid)
  83 #endif  /* _KERNEL */
  84 {
  85         impl_devid_t    *id = (impl_devid_t *)devid;
  86 
  87         if (id == NULL)
  88                 return (sizeof (*id) - sizeof (id->did_id));
  89 
  90         DEVID_ASSERT(DEVID_FUNC(devid_valid)(devid) == DEVID_RET_VALID);
  91 
  92         return (sizeof (*id) + DEVID_GETLEN(id) - sizeof (id->did_id));
  93 }
  94 
  95 /*
  96  * Compare two device id's.
  97  *      -1 - less than
  98  *      0  - equal
  99  *      1  - greater than
 100  */
 101 int
 102 #ifdef  _KERNEL
 103 ddi_devid_compare(ddi_devid_t id1, ddi_devid_t id2)
 104 #else   /* !_KERNEL */
 105 devid_compare(ddi_devid_t id1, ddi_devid_t id2)
 106 #endif  /* _KERNEL */
 107 {
 108         int             rval;
 109         impl_devid_t    *i_id1  = (impl_devid_t *)id1;
 110         impl_devid_t    *i_id2  = (impl_devid_t *)id2;
 111         ushort_t        i_id1_type;
 112         ushort_t        i_id2_type;
 113 
 114         DEVID_ASSERT((id1 != NULL) && (id2 != NULL));
 115         DEVID_ASSERT(DEVID_FUNC(devid_valid)(id1) == DEVID_RET_VALID);
 116         DEVID_ASSERT(DEVID_FUNC(devid_valid)(id2) == DEVID_RET_VALID);
 117 
 118         /* magic and revision comparison */
 119         if ((rval = bcmp(id1, id2, 4)) != 0) {
 120                 return (rval);
 121         }
 122 
 123         /* get current devid types */
 124         i_id1_type = DEVID_GETTYPE(i_id1);
 125         i_id2_type = DEVID_GETTYPE(i_id2);
 126 
 127         /*
 128          * Originaly all page83 devids used DEVID_SCSI3_WWN.
 129          * To avoid a possible uniqueness issue each type of page83
 130          * encoding supported is represented as a separate
 131          * devid type.  If comparing DEVID_SCSI3_WWN against
 132          * one of the new page83 encodings we assume that no
 133          * uniqueness issue exists (since we had apparently been
 134          * running with the old DEVID_SCSI3_WWN encoding without
 135          * a problem).
 136          */
 137         if ((i_id1_type == DEVID_SCSI3_WWN) ||
 138             (i_id2_type == DEVID_SCSI3_WWN)) {
 139                 /*
 140                  * Atleast one devid is using old scsi
 141                  * encode algorithm.  Force devid types
 142                  * to same scheme for comparison.
 143                  */
 144                 if (IS_DEVID_SCSI3_VPD_TYPE(i_id1_type)) {
 145                         i_id1_type = DEVID_SCSI3_WWN;
 146                 }
 147                 if (IS_DEVID_SCSI3_VPD_TYPE(i_id2_type)) {
 148                         i_id2_type = DEVID_SCSI3_WWN;
 149                 }
 150         }
 151 
 152         /* type comparison */
 153         if (i_id1_type != i_id2_type) {
 154                 return ((i_id1_type < i_id2_type) ? -1 : 1);
 155         }
 156 
 157         /* length comparison */
 158         if (DEVID_GETLEN(i_id1) != DEVID_GETLEN(i_id2)) {
 159                 return (DEVID_GETLEN(i_id1) < DEVID_GETLEN(i_id2) ? -1 : 1);
 160         }
 161 
 162         /* id comparison */
 163         rval = bcmp(i_id1->did_id, i_id2->did_id, DEVID_GETLEN(i_id1));
 164 
 165         return (rval);
 166 }
 167 
 168 /*
 169  * Free a Device Id
 170  */
 171 void
 172 #ifdef  _KERNEL
 173 ddi_devid_free(ddi_devid_t devid)
 174 #else   /* !_KERNEL */
 175 devid_free(ddi_devid_t devid)
 176 #endif  /* _KERNEL */
 177 {
 178         DEVID_ASSERT(devid != NULL);
 179         DEVID_FREE(devid, DEVID_FUNC(devid_sizeof)(devid));
 180 }
 181 
 182 /*
 183  * Encode a device id into a string.  See ddi_impldefs.h for details.
 184  */
 185 char *
 186 #ifdef  _KERNEL
 187 ddi_devid_str_encode(ddi_devid_t devid, char *minor_name)
 188 #else   /* !_KERNEL */
 189 devid_str_encode(ddi_devid_t devid, char *minor_name)
 190 #endif  /* _KERNEL */
 191 {
 192         impl_devid_t    *id = (impl_devid_t *)devid;
 193         size_t          driver_len, devid_len, slen;
 194         char            *sbuf, *dsp, *dp, ta;
 195         int             i, n, ascii;
 196 
 197         /* "id0" is the encoded representation of a NULL device id */
 198         if (devid == NULL) {
 199                 if ((sbuf = DEVID_MALLOC(4)) == NULL)
 200                         return (NULL);
 201                 *(sbuf+0) = DEVID_MAGIC_MSB;
 202                 *(sbuf+1) = DEVID_MAGIC_LSB;
 203                 *(sbuf+2) = '0';
 204                 *(sbuf+3) = 0;
 205                 return (sbuf);
 206         }
 207 
 208         /* verify input */
 209         if (DEVID_FUNC(devid_valid)(devid) != DEVID_RET_VALID)
 210                 return (NULL);
 211 
 212         /* scan the driver hint to see how long the hint is */
 213         for (driver_len = 0; driver_len < DEVID_HINT_SIZE; driver_len++)
 214                 if (id->did_driver[driver_len] == '\0')
 215                         break;
 216 
 217         /* scan the contained did_id to see if it meets ascii requirements */
 218         devid_len = DEVID_GETLEN(id);
 219         for (ascii = 1, i = 0; i < devid_len; i++)
 220                 if (!DEVID_IDBYTE_ISASCII(id->did_id[i])) {
 221                         ascii = 0;
 222                         break;
 223                 }
 224 
 225         /* some types should always go hex even if they look ascii */
 226         if (DEVID_TYPE_BIN_FORCEHEX(id->did_type_lo))
 227                 ascii = 0;
 228 
 229         /* set the length of the resulting string */
 230         slen = 2 + 1;                                   /* <magic><rev> "id1" */
 231         slen += 1 + driver_len + 1 + 1;                 /* ",<driver>@<type>" */
 232         slen += ascii ? devid_len : (devid_len * 2);    /* did_id field */
 233         if (minor_name) {
 234                 slen += 1;                              /* '/' */
 235                 slen += strlen(minor_name);             /* len of minor_name */
 236         }
 237         slen += 1;                                      /* NULL */
 238 
 239         /* allocate string */
 240         if ((sbuf = DEVID_MALLOC(slen)) == NULL)
 241                 return (NULL);
 242 
 243         /* perform encode of id to hex string */
 244         dsp = sbuf;
 245         *dsp++ = id->did_magic_hi;
 246         *dsp++ = id->did_magic_lo;
 247         *dsp++ = DEVID_REV_BINTOASCII(id->did_rev_lo);
 248         *dsp++ = ',';
 249         for (i = 0; i < driver_len; i++)
 250                 *dsp++ = id->did_driver[i];
 251         *dsp++ = '@';
 252         ta = DEVID_TYPE_BINTOASCII(id->did_type_lo);
 253         if (ascii)
 254                 ta = DEVID_TYPE_SETASCII(ta);
 255         *dsp++ = ta;
 256         for (i = 0, dp = &id->did_id[0]; i < devid_len; i++, dp++) {
 257                 if (ascii) {
 258                         if (*dp == ' ')
 259                                 *dsp++ = '_';
 260                         else if (*dp == 0x00)
 261                                 *dsp++ = '~';
 262                         else
 263                                 *dsp++ = *dp;
 264                 } else {
 265                         n = ((*dp) >> 4) & 0xF;
 266                         *dsp++ = (n < 10) ? (n + '0') : (n + ('a' - 10));
 267                         n = (*dp) & 0xF;
 268                         *dsp++ = (n < 10) ? (n + '0') : (n + ('a' - 10));
 269                 }
 270         }
 271 
 272         if (minor_name) {
 273                 *dsp++ = '/';
 274                 (void) strcpy(dsp, minor_name);
 275         } else
 276                 *dsp++ = 0;
 277 
 278         /* ensure that (strlen + 1) is correct length for free */
 279         DEVID_ASSERT((strlen(sbuf) + 1) == slen);
 280         return (sbuf);
 281 }
 282 
 283 /* free the string returned by devid_str_encode */
 284 void
 285 #ifdef  _KERNEL
 286 ddi_devid_str_free(char *devidstr)
 287 #else   /* !_KERNEL */
 288 devid_str_free(char *devidstr)
 289 #endif  /* _KERNEL */
 290 {
 291         DEVID_FREE(devidstr, strlen(devidstr) + 1);
 292 }
 293 
 294 /*
 295  * given the string representation of a device id returned by calling
 296  * devid_str_encode (passed in as devidstr), return pointers to the
 297  * broken out devid and minor_name as requested. Devidstr remains
 298  * allocated and unmodified. The devid returned in *devidp should be freed by
 299  * calling devid_free.  The minor_name returned in minor_namep should
 300  * be freed by calling devid_str_free(minor_namep).
 301  *
 302  * See ddi_impldefs.h for format details.
 303  */
 304 int
 305 #ifdef  _KERNEL
 306 ddi_devid_str_decode(
 307 #else   /* !_KERNEL */
 308 devid_str_decode(
 309 #endif  /* _KERNEL */
 310     char *devidstr, ddi_devid_t *devidp, char **minor_namep)
 311 {
 312         return (devid_str_decode_id(devidstr, devidp, minor_namep, NULL));
 313 }
 314 
 315 /* implementation for (ddi_)devid_str_decode */
 316 static int
 317 devid_str_decode_id(char *devidstr, ddi_devid_t *devidp,
 318     char **minor_namep, impl_devid_t *id)
 319 {
 320         char            *str, *msp, *dsp, *dp, ta;
 321         int             slen, devid_len, ascii, i, n, c, pre_alloc = FALSE;
 322         unsigned short  id_len, type;           /* for hibyte/lobyte */
 323 
 324         if (devidp != NULL)
 325                 *devidp = NULL;
 326         if (minor_namep != NULL)
 327                 *minor_namep = NULL;
 328         if (id != NULL)
 329                 pre_alloc = TRUE;
 330 
 331         if (devidstr == NULL)
 332                 return (DEVID_FAILURE);
 333 
 334         /* the string must atleast contain the ascii two byte header */
 335         slen = strlen(devidstr);
 336         if ((slen < 3) || (devidstr[0] != DEVID_MAGIC_MSB) ||
 337             (devidstr[1] != DEVID_MAGIC_LSB))
 338                 return (DEVID_FAILURE);
 339 
 340         /* "id0" is the encoded representation of a NULL device id */
 341         if ((devidstr[2] == '0') && (slen == 3))
 342                 return (DEVID_SUCCESS);
 343 
 344         /* "id1,@S0" is the shortest possible, reject if shorter */
 345         if (slen <  7)
 346                 return (DEVID_FAILURE);
 347 
 348         /* find the optional minor name, start after ',' */
 349         if ((msp = strchr(&devidstr[4], '/')) != NULL)
 350                 msp++;
 351 
 352         /* skip devid processing if we are not asked to return it */
 353         if (devidp) {
 354                 /* find the required '@' separator */
 355                 if ((str = strchr(devidstr, '@')) == NULL)
 356                         return (DEVID_FAILURE);
 357                 str++;                                  /* skip '@' */
 358 
 359                 /* pick up <type> after the '@' and verify */
 360                 ta = *str++;
 361                 ascii = DEVID_TYPE_ISASCII(ta);
 362                 type = DEVID_TYPE_ASCIITOBIN(ta);
 363                 if (type > DEVID_MAXTYPE)
 364                         return (DEVID_FAILURE);
 365 
 366                 /* determine length of id->did_id field */
 367                 if (msp == NULL)
 368                         id_len = strlen(str);
 369                 else
 370                         id_len = msp - str - 1;
 371 
 372                 /* account for encoding: with hex, binary is half the size */
 373                 if (!ascii) {
 374                         /* hex id field must be even length */
 375                         if (id_len & 1)
 376                                 return (DEVID_FAILURE);
 377                         id_len /= 2;
 378                 }
 379 
 380                 /* add in size of the binary devid header */
 381                 devid_len = id_len + sizeof (*id) - sizeof (id->did_id);
 382 
 383                 /*
 384                  * Allocate space for devid if we are asked to decode it
 385                  * decode it and space wasn't pre-allocated.
 386                  */
 387                 if (pre_alloc == FALSE) {
 388                         if ((id = (impl_devid_t *)DEVID_MALLOC(
 389                             devid_len)) == NULL)
 390                         return (DEVID_FAILURE);
 391                 }
 392 
 393                 /* decode header portion of the string into the binary devid */
 394                 dsp = devidstr;
 395                 id->did_magic_hi = *dsp++;           /* <magic> "id" */
 396                 id->did_magic_lo = *dsp++;
 397                 id->did_rev_hi = 0;
 398                 id->did_rev_lo =
 399                     DEVID_REV_ASCIITOBIN(*dsp);         /* <rev> "1" */
 400                 dsp++;                                  /* skip "1" */
 401                 dsp++;                                  /* skip "," */
 402                 for (i = 0; i < DEVID_HINT_SIZE; i++) {      /* <driver>@ */
 403                         if (*dsp == '@')
 404                                 break;
 405                         id->did_driver[i] = *dsp++;
 406                 }
 407                 for (; i < DEVID_HINT_SIZE; i++)
 408                         id->did_driver[i] = 0;
 409 
 410                 /* we must now be at the '@' */
 411                 if (*dsp != '@')
 412                         goto efree;
 413 
 414                 /* set the type and length */
 415                 DEVID_FORMTYPE(id, type);
 416                 DEVID_FORMLEN(id, id_len);
 417 
 418                 /* decode devid portion of string into the binary */
 419                 for (i = 0, dsp = str, dp = &id->did_id[0];
 420                     i < id_len; i++, dp++) {
 421                         if (ascii) {
 422                                 if (*dsp == '_')
 423                                         *dp = ' ';
 424                                 else if (*dsp == '~')
 425                                         *dp = 0x00;
 426                                 else
 427                                         *dp = *dsp;
 428                                 dsp++;
 429                         } else {
 430                                 c = *dsp++;
 431                                 if (c >= '0' && c <= '9')
 432                                         n = (c - '0') & 0xFF;
 433                                 else if (c >= 'a' && c <= 'f')
 434                                         n = (c - ('a' - 10)) & 0xFF;
 435                                 else
 436                                         goto efree;
 437                                 n <<= 4;
 438                                 c = *dsp++;
 439                                 if (c >= '0' && c <= '9')
 440                                         n |= (c - '0') & 0xFF;
 441                                 else if (c >= 'a' && c <= 'f')
 442                                         n |= (c - ('a' - 10)) & 0xFF;
 443                                 else
 444                                         goto efree;
 445                                 *dp = n;
 446                         }
 447                 }
 448 
 449                 /* verify result */
 450                 if (DEVID_FUNC(devid_valid)((ddi_devid_t)id) != DEVID_RET_VALID)
 451                         goto efree;
 452         }
 453 
 454         /* duplicate minor_name if we are asked to decode it */
 455         if (minor_namep && msp) {
 456                 if ((*minor_namep = DEVID_MALLOC(strlen(msp) + 1)) == NULL)
 457                         goto efree;
 458                 (void) strcpy(*minor_namep, msp);
 459         }
 460 
 461         /* return pointer to binary */
 462         if (devidp)
 463                 *devidp = (ddi_devid_t)id;
 464         return (DEVID_SUCCESS);
 465 
 466 efree:
 467         if ((pre_alloc == FALSE) && (id))
 468                 DEVID_FREE(id, devid_len);
 469         return (DEVID_FAILURE);
 470 }
 471 
 472 
 473 /*
 474  * Compare two device id's in string form
 475  *      -1 - id1 less than id2
 476  *      0  - equal
 477  *      1  - id1 greater than id2
 478  */
 479 int
 480 #ifdef  _KERNEL
 481 ddi_devid_str_compare(char *id1_str, char *id2_str)
 482 #else   /* !_KERNEL */
 483 devid_str_compare(char *id1_str, char *id2_str)
 484 #endif  /* _KERNEL */
 485 {
 486         int             rval    = DEVID_FAILURE;
 487         ddi_devid_t     devid1;
 488         ddi_devid_t     devid2;
 489 #ifdef  _KERNEL
 490         /* kernel use static protected by lock. */
 491         static kmutex_t id_lock;
 492         static uchar_t  id1[sizeof (impl_devid_t) + MAXPATHLEN];
 493         static uchar_t  id2[sizeof (impl_devid_t) + MAXPATHLEN];
 494 #else   /* !_KERNEL */
 495         /* userland place on stack, since malloc might fail */
 496         uchar_t         id1[sizeof (impl_devid_t) + MAXPATHLEN];
 497         uchar_t         id2[sizeof (impl_devid_t) + MAXPATHLEN];
 498 #endif  /* _KERNEL */
 499 
 500 #ifdef  _KERNEL
 501         mutex_enter(&id_lock);
 502 #endif  /* _KERNEL */
 503 
 504         /*
 505          * encode string form of devid
 506          */
 507         if ((devid_str_decode_id(id1_str, &devid1, NULL, (impl_devid_t *)id1) ==
 508             DEVID_SUCCESS) &&
 509             (devid_str_decode_id(id2_str, &devid2, NULL, (impl_devid_t *)id2) ==
 510             DEVID_SUCCESS)) {
 511                 rval = DEVID_FUNC(devid_compare)(devid1, devid2);
 512         }
 513 
 514 #ifdef  _KERNEL
 515         mutex_exit(&id_lock);
 516 #endif  /* _KERNEL */
 517 
 518         return (rval);
 519 }