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 devid_len = 0; 328 if (devidp != NULL) 329 *devidp = NULL; 330 if (minor_namep != NULL) 331 *minor_namep = NULL; 332 if (id != NULL) 333 pre_alloc = TRUE; 334 335 if (devidstr == NULL) 336 return (DEVID_FAILURE); 337 338 /* the string must atleast contain the ascii two byte header */ 339 slen = strlen(devidstr); 340 if ((slen < 3) || (devidstr[0] != DEVID_MAGIC_MSB) || 341 (devidstr[1] != DEVID_MAGIC_LSB)) 342 return (DEVID_FAILURE); 343 344 /* "id0" is the encoded representation of a NULL device id */ 345 if ((devidstr[2] == '0') && (slen == 3)) 346 return (DEVID_SUCCESS); 347 348 /* "id1,@S0" is the shortest possible, reject if shorter */ 349 if (slen < 7) 350 return (DEVID_FAILURE); 351 352 /* find the optional minor name, start after ',' */ 353 if ((msp = strchr(&devidstr[4], '/')) != NULL) 354 msp++; 355 356 /* skip devid processing if we are not asked to return it */ 357 if (devidp) { 358 /* find the required '@' separator */ 359 if ((str = strchr(devidstr, '@')) == NULL) 360 return (DEVID_FAILURE); 361 str++; /* skip '@' */ 362 363 /* pick up <type> after the '@' and verify */ 364 ta = *str++; 365 ascii = DEVID_TYPE_ISASCII(ta); 366 type = DEVID_TYPE_ASCIITOBIN(ta); 367 if (type > DEVID_MAXTYPE) 368 return (DEVID_FAILURE); 369 370 /* determine length of id->did_id field */ 371 if (msp == NULL) 372 id_len = strlen(str); 373 else 374 id_len = msp - str - 1; 375 376 /* account for encoding: with hex, binary is half the size */ 377 if (!ascii) { 378 /* hex id field must be even length */ 379 if (id_len & 1) 380 return (DEVID_FAILURE); 381 id_len /= 2; 382 } 383 384 /* add in size of the binary devid header */ 385 devid_len = id_len + sizeof (*id) - sizeof (id->did_id); 386 387 /* 388 * Allocate space for devid if we are asked to decode it 389 * decode it and space wasn't pre-allocated. 390 */ 391 if (pre_alloc == FALSE) { 392 if ((id = (impl_devid_t *)DEVID_MALLOC( 393 devid_len)) == NULL) 394 return (DEVID_FAILURE); 395 } 396 397 /* decode header portion of the string into the binary devid */ 398 dsp = devidstr; 399 id->did_magic_hi = *dsp++; /* <magic> "id" */ 400 id->did_magic_lo = *dsp++; 401 id->did_rev_hi = 0; 402 id->did_rev_lo = 403 DEVID_REV_ASCIITOBIN(*dsp); /* <rev> "1" */ 404 dsp++; /* skip "1" */ 405 dsp++; /* skip "," */ 406 for (i = 0; i < DEVID_HINT_SIZE; i++) { /* <driver>@ */ 407 if (*dsp == '@') 408 break; 409 id->did_driver[i] = *dsp++; 410 } 411 for (; i < DEVID_HINT_SIZE; i++) 412 id->did_driver[i] = 0; 413 414 /* we must now be at the '@' */ 415 if (*dsp != '@') 416 goto efree; 417 418 /* set the type and length */ 419 DEVID_FORMTYPE(id, type); 420 DEVID_FORMLEN(id, id_len); 421 422 /* decode devid portion of string into the binary */ 423 for (i = 0, dsp = str, dp = &id->did_id[0]; 424 i < id_len; i++, dp++) { 425 if (ascii) { 426 if (*dsp == '_') 427 *dp = ' '; 428 else if (*dsp == '~') 429 *dp = 0x00; 430 else 431 *dp = *dsp; 432 dsp++; 433 } else { 434 c = *dsp++; 435 if (c >= '0' && c <= '9') 436 n = (c - '0') & 0xFF; 437 else if (c >= 'a' && c <= 'f') 438 n = (c - ('a' - 10)) & 0xFF; 439 else 440 goto efree; 441 n <<= 4; 442 c = *dsp++; 443 if (c >= '0' && c <= '9') 444 n |= (c - '0') & 0xFF; 445 else if (c >= 'a' && c <= 'f') 446 n |= (c - ('a' - 10)) & 0xFF; 447 else 448 goto efree; 449 *dp = n; 450 } 451 } 452 453 /* verify result */ 454 if (DEVID_FUNC(devid_valid)((ddi_devid_t)id) != DEVID_RET_VALID) 455 goto efree; 456 } 457 458 /* duplicate minor_name if we are asked to decode it */ 459 if (minor_namep && msp) { 460 if ((*minor_namep = DEVID_MALLOC(strlen(msp) + 1)) == NULL) 461 goto efree; 462 (void) strcpy(*minor_namep, msp); 463 } 464 465 /* return pointer to binary */ 466 if (devidp) 467 *devidp = (ddi_devid_t)id; 468 return (DEVID_SUCCESS); 469 470 efree: 471 if ((pre_alloc == FALSE) && (id)) 472 DEVID_FREE(id, devid_len); 473 return (DEVID_FAILURE); 474 } 475 476 477 /* 478 * Compare two device id's in string form 479 * -1 - id1 less than id2 480 * 0 - equal 481 * 1 - id1 greater than id2 482 */ 483 int 484 #ifdef _KERNEL 485 ddi_devid_str_compare(char *id1_str, char *id2_str) 486 #else /* !_KERNEL */ 487 devid_str_compare(char *id1_str, char *id2_str) 488 #endif /* _KERNEL */ 489 { 490 int rval = DEVID_FAILURE; 491 ddi_devid_t devid1; 492 ddi_devid_t devid2; 493 #ifdef _KERNEL 494 /* kernel use static protected by lock. */ 495 static kmutex_t id_lock; 496 static uchar_t id1[sizeof (impl_devid_t) + MAXPATHLEN]; 497 static uchar_t id2[sizeof (impl_devid_t) + MAXPATHLEN]; 498 #else /* !_KERNEL */ 499 /* userland place on stack, since malloc might fail */ 500 uchar_t id1[sizeof (impl_devid_t) + MAXPATHLEN]; 501 uchar_t id2[sizeof (impl_devid_t) + MAXPATHLEN]; 502 #endif /* _KERNEL */ 503 504 #ifdef _KERNEL 505 mutex_enter(&id_lock); 506 #endif /* _KERNEL */ 507 508 /* 509 * encode string form of devid 510 */ 511 if ((devid_str_decode_id(id1_str, &devid1, NULL, (impl_devid_t *)id1) == 512 DEVID_SUCCESS) && 513 (devid_str_decode_id(id2_str, &devid2, NULL, (impl_devid_t *)id2) == 514 DEVID_SUCCESS)) { 515 rval = DEVID_FUNC(devid_compare)(devid1, devid2); 516 } 517 518 #ifdef _KERNEL 519 mutex_exit(&id_lock); 520 #endif /* _KERNEL */ 521 522 return (rval); 523 } 524