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