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 } 523